Subject: Re: WANTED: Floppy formatter
To: None <peter@wonderland.org>
From: Mike Long <mike.long@analog.com>
List: port-i386
Date: 06/19/1995 11:10:37
>From: Peter Galbavy <peter@wonderland.org>
>Date: Sat, 17 Jun 1995 13:59:42 +0100 (BST)
>
>I am trying to move the office systems here to NetBSD, and the only
>thing that I *really* need is to be able to format floppies. What
>a bummer. Oh, and verify of course.
>
>Has someone got an fdformat and diffs to -current they couls point
>me at, please ?

Here is something I saved a couple of months ago (I'm such a pack
rat).  I have not tried it out for myself.  I think the floppy driver
has changed a bit since April, so it may take some work to get it to
function.

----------------------------------------------------------------------
Newsgroups: comp.unix.bsd.netbsd.misc
From: jtk@atria.com (John Kohl)
Subject: Re: format_fd0
In-Reply-To: kstailey@leidecker.gsfc.nasa.gov's message of 05 Apr 1995 20:34:36 GMT
Nntp-Posting-Host: banana.atria.com
Organization: Atria Software, Inc.
Date: 07 Apr 1995 08:47:43 -0400

>>>>> "Kenneth" == Kenneth Stailey <kstailey@leidecker.gsfc.nasa.gov> writes:
In article <KSTAILEY.95Apr5163436@leidecker.gsfc.nasa.gov> kstailey@leidecker.gsfc.nasa.gov (Kenneth Stailey) writes:

Kenneth> Yes, I heard that someone had a NetBSD/i386 that could do this.  Does
Kenneth> anyone know about it?

Here are diffs from my system at home.  Code sucked in from FreeBSD
floppy driver about 4 months ago.

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by John Kohl <jtk@earth> on Fri Apr  7 08:29:14 1995
#
# This archive contains:
#	fdformat	
#
# Error checking via sum(1) will be performed.

LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH

if sum -r </dev/null >/dev/null 2>&1
then
	sumopt='-r'
else
	sumopt=''
fi

echo mkdir - fdformat
mkdir fdformat

echo x - fdformat/Makefile
sed 's/^@//' >fdformat/Makefile <<'@EOF'
#
PROG    = fdformat

# the -I's seem to be confusing, but necessery this way
# (so the right <unistd.h> will be found in /usr/include, and the
# "../i386/isa/ic/nec765.h" included from fdreg.h is accessible, too)
CFLAGS+= -Wall

@.include <bsd.prog.mk>
@EOF
set `sum $sumopt <fdformat/Makefile`; if test $1 -ne 39064
then
	echo ERROR: fdformat/Makefile checksum is $1 should be 39064
fi

chmod 644 fdformat/Makefile

echo x - fdformat/fdformat.1
sed 's/^@//' >fdformat/fdformat.1 <<'@EOF'
@.\" Copyright (C) 1993, 1994 by Joerg Wunsch, Dresden
@.\" 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.
@.\"
@.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
@.\"
@.Dd September 16, 1993
@.Os
@.Dt FDFORMAT 1
@.Sh NAME
@.Nm fdformat
@.Nd format floppy disks
@.Sh SYNOPSIS
@.Nm fdformat
@.Bq Fl q
@.Bq Fl v
@.Bq Fl n
@.Bq Fl f Ar capacity
@.Bq Fl c Ar cyls
@.Bq Fl s Ar secs
@.Bq Fl h Ar heads
@.br
@.Bq Fl r Ar rate
@.Bq Fl g Ar gap3len
@.Bq Fl i Ar intleave
@.Bq Fl S Ar secshft
@.Bq Fl F Ar fillbyte
@.Bq Fl t Ar steps_per_track
@.Ar device_name
@.Sh DESCRIPTION
@.Nm Fdformat
formats a floppy disk at device
@.Ar device_name .
@.Ar Device_name
should be a character device; it may be given either with a full path
name of a raw device node for a floppy disk drive
@.Pq e.\ g. Pa /dev/rfd0 ,
or default name in an abbreviated form
@.Pq e.\ g. Em fd0 .
In the latter case, the name is constructed by prepending
@.Pa /dev/r
and appending a
@.Em .capacity
to the
@.Ar device_name .
Note that any geometry constraints of the device node
@.Pq minor device number
are meaningless, since they're overridden by
@.Nm fdformat .
@.Pp
The options are as follows:
@.Bl -tag -width 10n -offset indent
@.It Fl q
supress any normal output from the command, and don't ask the
user for a confirmation whether to format the floppy disk at
@.Ar device_name .
@.It Fl f Ar capacity
The normal way to specify the desired formatting parameters.
@.Ar Capacity
is the number of kilobytes to format.
@.It Fl n
Don't verify floppy after formatting.
@.It Fl v
Don't format, verify only.
@.It Fl c Ar cyls
@.It Fl s Ar secs
@.It Fl h Ar heads
@.It Fl r Ar rate
@.It Fl g Ar gap3len
@.It Fl i Ar intleave
@.It Fl S Ar secshft
@.It Fl F Ar fillbyte
@.It Fl t Ar steps_per_track
An alternate method to specify the geometry data to write to the floppy disk.
@.El

If the
@.Fl q
flag has not been specified, the user is asked for a confirmation
of the intended formatting process. In order to continue, an answer
of
@.Dq y
must be given.
@.Sh DIAGNOSTICS
Unless
@.Fl q
has been specified, a single letter is printed to standard output
to inform the user about the progress of work.
First, an
@.Sq Em F
is printed when the track(s) is being formatted, then a
@.Sq Em V
while it's being verified, and if an error has been detected, it
will finally change to
@.Sq Em E .
@.Pp
An exit status of 0 is returned upon successful operation. Exit status
1 is returned on any errors during floppy formatting, and an exit status
of 2 reflects invalid arguments given to the program (along with an
appropriate information written to diagnostic output).
@.Sh SEE ALSO
@.Xr fdc 4 .
@.Sh HISTORY
@.Nm Fdformat
has been developed for 386BSD 0.1
and upgraded to the new
@.Xr fd 4
floppy disk driver. It later became part of the
@.Em FreeBSD
system, release 1.1, and was then ported to NetBSD 1.0.
@.Sh AUTHOR
The program has been contributed by
@.if n Joerg Wunsch,
@.if t J\(:org Wunsch,
Dresden, with changes by Serge Vakulenko and Andrew A. Chernov, Moscow.
@EOF
set `sum $sumopt <fdformat/fdformat.1`; if test $1 -ne 6454
then
	echo ERROR: fdformat/fdformat.1 checksum is $1 should be 6454
fi

chmod 444 fdformat/fdformat.1

echo x - fdformat/fdformat.c
cat >fdformat/fdformat.c <<'@EOF'
/*
 * Copyright (C) 1992-1994 by Joerg Wunsch, Dresden
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
 */

/*
 * FreeBSD:
 * format a floppy disk
 * 
 * Added FD_GTYPE ioctl, verifying, proportional indicators.
 * Serge Vakulenko, vak@zebub.msk.su
 * Sat Dec 18 17:45:47 MSK 1993
 *
 * Final adaptation, change format/verify logic, add separate
 * format gap/interleave values
 * Andrew A. Chernov, ache@astral.msk.su
 * Thu Jan 27 00:47:24 MSK 1994
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <strings.h>
#include <ctype.h>

#include <errno.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <machine/ioctl_fd.h>

static void
format_track(int fd, int cyl, int secs, int head, int rate,
             int gaplen, int secsize, int fill,int interleave)
{
        struct fd_formb f;
        register int i,j;
        int il[FD_MAX_NSEC + 1];

        memset(il,0,sizeof il);
        for(j = 0, i = 1; i <= secs; i++) {
            while(il[(j%secs)+1]) j++;
            il[(j%secs)+1] = i;
            j += interleave;
        }

        f.format_version = FD_FORMAT_VERSION;
        f.head = head;
        f.cyl = cyl;
        f.transfer_rate = rate;

        f.fd_formb_secshift = secsize;
        f.fd_formb_nsecs = secs;
        f.fd_formb_gaplen = gaplen;
        f.fd_formb_fillbyte = fill;
        for(i = 0; i < secs; i++) {
                f.fd_formb_cylno(i) = cyl;
                f.fd_formb_headno(i) = head;
                f.fd_formb_secno(i) = il[i+1];
                f.fd_formb_secsize(i) = secsize;
        }
        if(ioctl(fd, FD_FORM, (caddr_t)&f) < 0) {
                perror("\nfdformat: ioctl(FD_FORM)");
                exit(1);
        }
}

static int
verify_track(int fd, int track, int tracksize)
{
        static char *buf = 0;
        static int bufsz = 0;
        int fdopts = -1, ofdopts, rv = 0;

        if (ioctl(fd, FD_GOPTS, &fdopts) < 0)
                perror("warning: ioctl(FD_GOPTS)");
        else {
                ofdopts = fdopts;
                fdopts |= FDOPT_NORETRY;
                (void)ioctl(fd, FD_SOPTS, &fdopts);
        }
        
        if (bufsz < tracksize) {
                if (buf)
                        free (buf);
                bufsz = tracksize;
                buf = 0;
        }
        if (! buf)
                buf = malloc (bufsz);
        if (! buf) {
                fprintf (stderr, "\nfdformat: out of memory\n");
                exit (2);
        }
        if (lseek (fd, (long) track*tracksize, 0) < 0)
                rv = -1;
        /* try twice reading it, without using the normal retrier */
        else if (read (fd, buf, tracksize) != tracksize
                 && read (fd, buf, tracksize) != tracksize)
                rv = -1;
        if(fdopts != -1)
                (void)ioctl(fd, FD_SOPTS, &ofdopts);
        return (rv);
}

static const char *
makename(const char *arg, const char *suffix)
{
        static char namebuff[20];       /* big enough for "/dev/rfd0a"... */

        memset(namebuff, 0, 20);
        if(*arg == '\0') /* ??? */
                return arg;
        if(*arg == '/')  /* do not convert absolute pathnames */
                return arg;
        strcpy(namebuff, "/dev/r");
        strncat(namebuff, arg, 3);
        strcat(namebuff, suffix);
        return namebuff;
}

static void
usage (void)
{
        printf("Usage:\n\tfdformat [-q] [-n | -v] [-f #] [-c #] [-s #] [-h #]\n");
        printf("\t\t [-r #] [-g #] [-i #] [-S #] [-F #] [-t #] devname\n");
        printf("Options:\n");
        printf("\t-q\tsupress any normal output, don't ask for confirmation\n");
        printf("\t-n\tdon't verify floppy after formatting\n");
        printf("\t-v\tdon't format, verify only\n");
        printf("\t-f #\tspecify desired floppy capacity, in kilobytes;\n");
        printf("\t\tvalid choices are 360, 720, 800, 820, 1200, 1440, 1480, 1720\n");
        printf("\tdevname\tthe full name of floppy device or in short form fd0, fd1\n");
        printf("Obscure options:\n");
        printf("\t-c #\tspecify number of cylinders, 40 or 80\n");
        printf("\t-s #\tspecify number of sectors per track, 9, 10, 15 or 18\n");
        printf("\t-h #\tspecify number of floppy heads, 1 or 2\n");
        printf("\t-r #\tspecify data rate, 250, 300 or 500 kbps\n");
        printf("\t-g #\tspecify gap length\n");
        printf("\t-i #\tspecify interleave factor\n");
        printf("\t-S #\tspecify sector size, 0=128, 1=256, 2=512 bytes\n");
        printf("\t-F #\tspecify fill byte\n");
        printf("\t-t #\tnumber of steps per track\n");
        exit(2);
}

static int
yes (void)
{
        char reply [256], *p;

        reply[sizeof(reply)-1] = 0;
        for (;;) {
                fflush(stdout);
                if (! fgets (reply, sizeof(reply)-1, stdin))
                        return (0);
                for (p=reply; *p==' ' || *p=='\t'; ++p)
                        continue;
                if (*p=='y' || *p=='Y')
                        return (1);
                if (*p=='n' || *p=='N' || *p=='\n' || *p=='\r')
                        return (0);
                printf("Answer `yes' or `no': ");
        }
}

int
main(int argc, char **argv)
{
        int format = -1, cyls = -1, secs = -1, heads = -1, intleave = -1;
        int rate = -1, gaplen = -1, secsize = -1, steps = -1;
        int fill = 0xf6, quiet = 0, verify = 1, verify_only = 0;
        int fd, c, track, error, tracks_per_dot, bytes_per_track, errs;
        const char *devname, *suffix;
        struct fd_type fdt;

        while((c = getopt(argc, argv, "f:c:s:h:r:g:S:F:t:i:qvn")) != -1)
                switch(c) {
                case 'f':       /* format in kilobytes */
                        format = atoi(optarg);
                        break;

                case 'c':       /* # of cyls */
                        cyls = atoi(optarg);
                        break;

                case 's':       /* # of secs per track */
                        secs = atoi(optarg);
                        break;

                case 'h':       /* # of heads */
                        heads = atoi(optarg);
                        break;

                case 'r':       /* transfer rate, kilobyte/sec */
                        rate = atoi(optarg);
                        break;

                case 'g':       /* length of GAP3 to format with */
                        gaplen = atoi(optarg);
                        break;

                case 'S':       /* sector size shift factor (1 << S)*128 */
                        secsize = atoi(optarg);
                        break;

                case 'F':       /* fill byte, C-like notation allowed */
                        fill = (int)strtol(optarg, (char **)0, 0);
                        break;

                case 't':       /* steps per track */
                        steps = atoi(optarg);
                        break;

                case 'i':       /* interleave factor */
                        intleave = atoi(optarg);
                        break;

                case 'q':
                        quiet = 1;
                        break;

                case 'n':
                        verify = 0;
                        break;

                case 'v':
                        verify = 1;
                        verify_only = 1;
                        break;

                case '?': default:
                        usage();
                }

        if(optind != argc - 1)
                usage();

        switch(format) {
        default:
                fprintf(stderr, "fdformat: bad floppy size: %dK\n", format);
                exit(2);
        case -1:   suffix = "";      break;
        case 360:  suffix = ".360";  break;
        case 720:  suffix = ".720";  break;
        case 800:  suffix = ".800";  break;
        case 820:  suffix = ".820";  break;
        case 1200: suffix = ".1200"; break;
        case 1440: suffix = ".1440"; break;
        case 1480: suffix = ".1480"; break;
        case 1720: suffix = ".1720"; break;
        }

        devname = makename(argv[optind], suffix);

        if((fd = open(devname, O_RDWR)) < 0) {
                perror(devname);
                exit(1);
        }

        if(ioctl(fd, FD_GTYPE, &fdt) < 0) {
                fprintf(stderr, "fdformat: not a floppy disk: %s\n", devname);
                exit(1);
        }

        switch(rate) {
        case -1:  break;
        case 250: fdt.rate = FDC_250KBPS; break;
        case 300: fdt.rate = FDC_300KBPS; break;
        case 500: fdt.rate = FDC_500KBPS; break;
        default:
                fprintf(stderr, "fdformat: invalid transfer rate: %d\n", rate);
                exit(2);
        }

        if (cyls >= 0)    fdt.tracks = cyls;
        if (secs >= 0)    fdt.sectrac = secs;
        if (fdt.sectrac > FD_MAX_NSEC) {
                fprintf(stderr, "fdformat: too many sectors per track, max value is %d\n", FD_MAX_NSEC);
                exit(2);
        }
        if (heads >= 0)   fdt.heads = heads;
        if (gaplen >= 0)  fdt.gap2 = gaplen;
        if (secsize >= 0) fdt.secsize = secsize;
        if (steps >= 0)   fdt.step = steps;

        bytes_per_track = fdt.sectrac * (1<<fdt.secsize) * 128;
        tracks_per_dot = fdt.tracks * fdt.heads / 40;

        if (verify_only) {
                if(!quiet)
                        printf("Verify %dK floppy `%s'.\n",
                                fdt.tracks * fdt.heads * bytes_per_track / 1024,
                                devname);
        }
        else if(!quiet) {
                printf("Format %dK floppy `%s'? (y/n): ",
                        fdt.tracks * fdt.heads * bytes_per_track / 1024,
                        devname);
                if(! yes ()) {
                        printf("Not confirmed.\n");
                        return 0;
                }
        }

        /*
         * Formatting.
         */
        if(!quiet) {
                printf("Processing ----------------------------------------\r");
                printf("Processing ");
                fflush(stdout);
        }

        error = errs = 0;

        for (track = 0; track < fdt.tracks * fdt.heads; track++) {
                if (!verify_only) {
                        format_track(fd, track / fdt.heads, fdt.sectrac,
                                track % fdt.heads, fdt.rate, fdt.gap2,
				     fdt.secsize, fill,
				     intleave >= 0 ? intleave : 1);
                        if(!quiet && !((track + 1) % tracks_per_dot)) {
                                putchar('F');
                                fflush(stdout);
                        }
                }
                if (verify) {
                        if (verify_track(fd, track, bytes_per_track) < 0)
                                error = errs = 1;
                        if(!quiet && !((track + 1) % tracks_per_dot)) {
                                if (!verify_only)
                                        putchar('\b');
                                if (error) {
                                        putchar('E');
                                        error = 0;
                                }
                                else
                                        putchar('V');
                                fflush(stdout);
                        }
                }
        }
        if(!quiet)
                printf(" done.\n");

        return errs;
}
/*
 * Local Variables:
 *  c-indent-level:               8
 *  c-continued-statement-offset: 8
 *  c-continued-brace-offset:     0
 *  c-brace-offset:              -8
 *  c-brace-imaginary-offset:     0
 *  c-argdecl-indent:             8
 *  c-label-offset:              -8
 *  c++-hanging-braces:           1
 *  c++-access-specifier-offset: -8
 *  c++-empty-arglist-indent:     8
 *  c++-friend-offset:            0
 * End:
 */
@EOF
set `sum $sumopt <fdformat/fdformat.c`; if test $1 -ne 47452
then
	echo ERROR: fdformat/fdformat.c checksum is $1 should be 47452
fi

chmod 444 fdformat/fdformat.c

echo x - fdformat/fdformat-sys-diffs
sed 's/^@//' >fdformat/fdformat-sys-diffs <<'@EOF'
diff -rubw /u2/src/sys/arch/i386/isa/fd.c ./arch/i386/isa/fd.c
--- /u2/src/sys/arch/i386/isa/fd.c	Mon Mar 20 02:37:49 1995
+++ ./arch/i386/isa/fd.c	Sat Jan 28 16:29:01 1995
@@@ -8,6 +8,12 @@
  * This code is derived from software contributed to Berkeley by
  * Don Ahn.
  *
+ * Portions Copyright (c) 1993, 1994 by
+ *  jc@irbs.UUCP (John Capo)
+ *  vak@zebub.msk.su (Serge Vakulenko)
+ *  ache@astral.msk.su (Andrew A. Chernov)
+ *  joerg_wunsch@uriah.sax.de (Joerg Wunsch)
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@@ -50,12 +56,14 @@
 #include <sys/dkstat.h>
 #include <sys/disk.h>
 #include <sys/buf.h>
+#include <sys/malloc.h>
 #include <sys/uio.h>
 #include <sys/syslog.h>
 #include <sys/queue.h>
 
 #include <machine/cpu.h>
 #include <machine/pio.h>
+#include <machine/ioctl_fd.h>
 
 #include <i386/isa/isavar.h>
 #include <i386/isa/dmavar.h>
@@@ -69,6 +77,9 @@
 #define FDUNIT(dev)	(minor(dev) / 8)
 #define FDTYPE(dev)	(minor(dev) % 8)
 
+/* XXX misuse a flag to identify format operation */
+#define B_FORMAT B_XXX
+
 #define b_cylin b_resid
 
 enum fdc_state {
@@@ -117,25 +128,7 @@
 	NULL, "fdc", fdcprobe, fdcattach, DV_DULL, sizeof(struct fdc_softc)
 };
 
-/*
- * Floppies come in various flavors, e.g., 1.2MB vs 1.44MB; here is how
- * we tell them apart.
- */
-struct fd_type {
-	int	sectrac;	/* sectors per track */
-	int	heads;		/* number of heads */
-	int	seccyl;		/* sectors per cylinder */
-	int	secsize;	/* size code for sectors */
-	int	datalen;	/* data len when secsize = 0 */
-	int	steprate;	/* step rate and head unload time */
-	int	gap1;		/* gap len between sectors */
-	int	gap2;		/* formatting gap */
-	int	tracks;		/* total num of tracks */
-	int	size;		/* size of disk in sectors */
-	int	step;		/* steps per cylinder */
-	int	rate;		/* transfer speed code */
-	char	*name;
-};
+/* fd_type struct now in ioctl_fd.h */
 
 /* The order of entries in the following table is important -- BEWARE! */
 struct fd_type fd_types[] = {
@@@ -158,6 +151,7 @@
 
 	daddr_t	sc_blkno;	/* starting block number */
 	int sc_bcount;		/* byte count left */
+ 	int sc_opts;			/* user-set options */
 	int sc_skip;		/* bytes already transferred */
 	int sc_nblks;		/* number of blocks currently tranferring */
 	int sc_nbytes;		/* number of bytes currently tranferring */
@@@ -202,6 +196,7 @@
 int fdcintr __P((struct fdc_softc *));
 void fdcretry __P((struct fdc_softc *fdc));
 void fdfinish __P((struct fd_softc *fd, struct buf *bp));
+int fdformat __P((dev_t, struct fd_formb *, struct proc *));
 
 int
 fdcprobe(parent, match, aux)
@@@ -482,7 +477,8 @@
 	if (unit >= fdcd.cd_ndevs ||
 	    (fd = fdcd.cd_devs[unit]) == 0 ||
 	    bp->b_blkno < 0 ||
-	    (bp->b_bcount % FDC_BSIZE) != 0) {
+	    ((bp->b_bcount % FDC_BSIZE) != 0 &&
+	     (bp->b_flags & B_FORMAT) == 0)) {
 		bp->b_error = EINVAL;
 		goto bad;
 	}
@@@ -716,6 +712,7 @@
 	struct fd_softc *fd = fdcd.cd_devs[FDUNIT(dev)];
 
 	fd->sc_flags &= ~FD_OPEN;
+	fd->sc_opts &= ~FDOPT_NORETRY;
 	return 0;
 }
 
@@@ -785,6 +782,9 @@
 	int s;
 
 	s = splbio();
+#ifdef DEBUG
+	log(LOG_ERR,"fdctimeout: state %d\n", fdc->sc_state);
+#endif
 	fdcstatus(&fd->sc_dev, 0, "timeout");
 
 	if (fd->sc_q.b_actf)
@@@ -820,6 +820,7 @@
 	int iobase = fdc->sc_iobase;
 	int read, head, trac, sec, i, s, nblks;
 	struct fd_type *type;
+	struct fd_formb *finfo = NULL;
 
 loop:
 	/* Is there a drive for the controller to do a transfer with? */
@@@ -838,6 +839,9 @@
 		goto loop;
 	}
 
+	if (bp->b_flags & B_FORMAT)
+	    finfo = (struct fd_formb *)bp->b_data;
+
 	switch (fdc->sc_state) {
 	case DEVIDLE:
 		fdc->sc_errors = 0;
@@@ -888,12 +892,15 @@
 	case DOIO:
 	doio:
 		type = fd->sc_type;
+		if (finfo)
+		    fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) -
+			(char *)finfo;
 		sec = fd->sc_blkno % type->seccyl;
 		nblks = type->seccyl - sec;
 		nblks = min(nblks, fd->sc_bcount / FDC_BSIZE);
 		nblks = min(nblks, FDC_MAXIOSIZE / FDC_BSIZE);
 		fd->sc_nblks = nblks;
-		fd->sc_nbytes = nblks * FDC_BSIZE;
+		fd->sc_nbytes = finfo ? bp->b_bcount : nblks * FDC_BSIZE;
 		head = sec / type->sectrac;
 		sec -= head * type->sectrac;
 #ifdef DIAGNOSTIC
@@@ -920,6 +927,19 @@
 		    read ? "read" : "write", fd->sc_drive, fd->sc_cylin, head,
 		    sec, nblks);
 #endif
+		if (finfo) {
+                        /* formatting */
+			if (out_fdc(iobase, NE7CMD_FORMAT) < 0) {
+			    fdc->sc_errors = 4;
+			    fdcretry(fdc);
+			    goto loop;
+			}
+                        out_fdc(iobase, (head << 2) | fd->sc_drive);
+                        out_fdc(iobase, finfo->fd_formb_secshift);
+                        out_fdc(iobase, finfo->fd_formb_nsecs);
+                        out_fdc(iobase, finfo->fd_formb_gaplen);
+                        out_fdc(iobase, finfo->fd_formb_fillbyte);
+		} else {
 		if (read)
 			out_fdc(iobase, NE7CMD_READ);	/* READ */
 		else
@@@ -932,6 +952,7 @@
 		out_fdc(iobase, type->sectrac);		/* sectors/track */
 		out_fdc(iobase, type->gap1);		/* gap1 size */
 		out_fdc(iobase, type->datalen);		/* data length */
+		}
 		fdc->sc_state = IOCOMPLETE;
 		/* allow 2 seconds for operation */
 		timeout(fdctimeout, fdc, 2 * hz);
@@@ -1003,7 +1024,7 @@
 		fd->sc_blkno += fd->sc_nblks;
 		fd->sc_skip += fd->sc_nbytes;
 		fd->sc_bcount -= fd->sc_nbytes;
-		if (fd->sc_bcount > 0) {
+		if (!finfo && fd->sc_bcount > 0) {
 			bp->b_cylin = fd->sc_blkno / fd->sc_type->seccyl;
 			goto doseek;
 		}
@@@ -1080,6 +1101,8 @@
 	fd = fdc->sc_drives.tqh_first;
 	bp = fd->sc_q.b_actf;
 
+	if (fd->sc_opts & FDOPT_NORETRY)
+	    goto fail;
 	switch (fdc->sc_errors) {
 	case 0:
 		/* try again */
@@@ -1097,6 +1120,7 @@
 		break;
 
 	default:
+	fail:
 		diskerr(bp, "fd", "hard error", LOG_PRINTF,
 		    fd->sc_skip / FDC_BSIZE, (struct disklabel *)NULL);
 		printf(" (st0 %b st1 %b st2 %b cyl %d head %d sec %d)\n",
@@@ -1130,11 +1154,12 @@
 }
 
 int
-fdioctl(dev, cmd, addr, flag)
+fdioctl(dev, cmd, addr, flag, p)
 	dev_t dev;
 	u_long cmd;
 	caddr_t addr;
 	int flag;
+	struct proc *p;
 {
 	struct fd_softc *fd = fdcd.cd_devs[FDUNIT(dev)];
 	struct disklabel buffer;
@@@ -1171,6 +1196,28 @@
 		error = writedisklabel(dev, fdstrategy, &buffer, NULL);
 		return error;
 
+        case FD_FORM:
+                if((flag & FWRITE) == 0)
+                        return EBADF;  /* must be opened for writing */
+                else if(((struct fd_formb *)addr)->format_version !=
+                        FD_FORMAT_VERSION)
+                        return EINVAL; /* wrong version of formatting prog */
+                else
+                        return fdformat(dev, (struct fd_formb *)addr, p);
+                break;
+
+        case FD_GTYPE:                  /* get drive type */
+                *(struct fd_type *)addr = *fd->sc_type;
+		return 0;
+
+        case FD_GOPTS:                  /* get drive options */
+                *(int *)addr = fd->sc_opts;
+                return 0;
+                
+        case FD_SOPTS:                  /* set drive options */
+                fd->sc_opts = *(int *)addr;
+		return 0;
+
 	default:
 		return ENOTTY;
 	}
@@@ -1178,4 +1225,64 @@
 #ifdef DIAGNOSTIC
 	panic("fdioctl: impossible");
 #endif
+}
+
+int
+fdformat(dev, finfo, p)
+        dev_t dev;
+        struct fd_formb *finfo;
+        struct proc *p;
+{
+        int rv = 0, s;
+	struct fd_softc *fd = fdcd.cd_devs[FDUNIT(dev)];
+	struct fd_type *type = fd->sc_type;
+        struct buf *bp;
+
+        /* set up a buffer header for fdstrategy() */
+        bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT);
+        if(bp == 0)
+                return ENOBUFS;
+        bzero((void *)bp, sizeof(struct buf));
+        bp->b_flags = B_BUSY | B_PHYS | B_FORMAT;
+        bp->b_proc = p;
+        bp->b_dev = dev;
+
+        /*
+         * calculate a fake blkno, so fdstrategy() would initiate a
+         * seek to the requested cylinder
+         */
+        bp->b_blkno = (finfo->cyl * (type->sectrac * type->heads)
+                + finfo->head * type->sectrac) * FDC_BSIZE / DEV_BSIZE;
+
+        bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
+        bp->b_data = (caddr_t)finfo;
+        
+#ifdef DEBUG
+	printf("fdformat: blkno %x count %x\n", bp->b_blkno, bp->b_bcount);
+#endif
+
+        /* now do the format */
+        fdstrategy(bp);
+
+        /* ...and wait for it to complete */
+        s = splbio();
+        while(!(bp->b_flags & B_DONE))
+        {
+                rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 0);
+                if(rv == EWOULDBLOCK)
+		    /*break*/;
+        }
+        splx(s);
+        
+        if(rv == EWOULDBLOCK) {
+                /* timed out */
+                rv = EIO;
+		/* XXX what to do to the buf? it will eventually fall
+		   out as finished, but ... ?*/
+		/*biodone(bp);*/
+	}
+        if(bp->b_flags & B_ERROR)
+                rv = bp->b_error;
+        free(bp, M_TEMP);
+        return rv;
 }
diff -rubw /dev/null ./arch/i386/include/ioctl_fd.h
--- /dev/null	Thu Apr  6 18:28:33 1995
+++ ./arch/i386/include/ioctl_fd.h	Sat Jan 28 16:58:23 1995
@@@ -0,0 +1,130 @@
+/*	$Id: ioctl_fd.h,v 1.3 1995/01/28 21:58:20 jtk Exp $	*/
+
+/*
+ * Copyright (C) 1992-1994 by Joerg Wunsch, Dresden
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * From: Id: ioctl_fd.h,v 1.7 1994/10/30 19:17:39 joerg Exp
+ */
+
+#ifndef _I386_IOCTL_FD_H_
+#define _I386_IOCTL_FD_H_
+
+#define FD_FORMAT_VERSION 110	/* used to validate before formatting */
+#define FD_MAX_NSEC 36		/* highest known number of spt - allow for */
+				/* 2.88 MB drives */
+
+struct fd_formb {
+	int format_version;	/* == FD_FORMAT_VERSION */
+	int cyl, head;
+	int transfer_rate;	/* fdreg.h: FDC_???KBPS */
+
+	union {
+		struct fd_form_data {
+			/*
+			 * DO NOT CHANGE THE LAYOUT OF THIS STRUCTS
+			 * it is hardware-dependant since it exactly
+			 * matches the byte sequence to write to FDC
+			 * during its `format track' operation
+			 */
+			u_char secshift; /* 0 -> 128, ...; usually 2 -> 512 */
+			u_char nsecs;	/* must be <= FD_MAX_NSEC */
+			u_char gaplen;	/* GAP 3 length; usually 84 */
+			u_char fillbyte; /* usually 0xf6 */
+			struct fd_idfield_data {
+				/*
+				 * data to write into id fields;
+				 * for obscure formats, they mustn't match
+				 * the real values (but mostly do)
+				 */
+				u_char cylno;	/* 0 thru 79 (or 39) */
+				u_char headno;	/* 0, or 1 */
+				u_char secno;	/* starting at 1! */
+				u_char secsize;	/* usually 2 */
+			} idfields[FD_MAX_NSEC]; /* 0 <= idx < nsecs used */
+		} structured;
+		u_char raw[1];	/* to have continuous indexed access */
+	} format_info;
+};
+
+/* make life easier */
+# define fd_formb_secshift   format_info.structured.secshift
+# define fd_formb_nsecs      format_info.structured.nsecs
+# define fd_formb_gaplen     format_info.structured.gaplen
+# define fd_formb_fillbyte   format_info.structured.fillbyte
+/* these data must be filled in for(i = 0; i < fd_formb_nsecs; i++) */
+# define fd_formb_cylno(i)   format_info.structured.idfields[i].cylno
+# define fd_formb_headno(i)  format_info.structured.idfields[i].headno
+# define fd_formb_secno(i)   format_info.structured.idfields[i].secno
+# define fd_formb_secsize(i) format_info.structured.idfields[i].secsize
+
+/*
+ * Floppies come in various flavors, e.g., 1.2MB vs 1.44MB; here is how
+ * we tell them apart.
+ */
+struct fd_type {
+	int	sectrac;	/* sectors per track */
+	int	heads;		/* number of heads */
+	int	seccyl;		/* sectors per cylinder */
+	int	secsize;	/* size code for sectors */
+	int	datalen;	/* data len when secsize = 0 */
+	int	steprate;	/* step rate and head unload time */
+	int	gap1;		/* gap len between sectors */
+	int	gap2;		/* formatting gap */
+	int	tracks;		/* total num of tracks */
+	int	size;		/* size of disk in sectors */
+	int	step;		/* steps per cylinder */
+	int	rate;		/* transfer speed code */
+	char	*name;
+};
+
+
+#define FD_FORM   _IOW('F', 61, struct fd_formb) /* format a track */
+#define FD_GTYPE  _IOR('F', 62, struct fd_type)  /* get drive type */
+#define FD_STYPE  _IOW('F', 63, struct fd_type)  /* set drive type */
+
+#define FD_GOPTS  _IOR('F', 64, int) /* drive options, see below */
+#define FD_SOPTS  _IOW('F', 65, int)
+
+#define FD_DEBUG  _IOW('F', 66, int)
+
+#define FDOPT_NORETRY 0x0001	/* no retries on failure (cleared on close) */
+
+/*
+ * The following definitions duplicate those in sys/i386/isa/fdreg.h
+ * They are here since their values are to be used in the above
+ * structure when formatting a floppy. For very obvious reasons, both
+ * definitions must match ;-)
+ */
+#ifndef FDC_500KBPS
+#define	FDC_500KBPS	0x00	/* 500KBPS MFM drive transfer rate */
+#define	FDC_300KBPS	0x01	/* 300KBPS MFM drive transfer rate */
+#define	FDC_250KBPS	0x02	/* 250KBPS MFM drive transfer rate */
+#define	FDC_125KBPS	0x03	/* 125KBPS FM drive transfer rate */
+				/* for some controllers 1MPBS instead */
+#endif /* FDC_500KBPS */
+
+
+#endif /* !_I386_IOCTL_FD_H__ */
@EOF
set `sum $sumopt <fdformat/fdformat-sys-diffs`; if test $1 -ne 2398
then
	echo ERROR: fdformat/fdformat-sys-diffs checksum is $1 should be 2398
fi

chmod 664 fdformat/fdformat-sys-diffs

chmod 775 fdformat

exit 0
--
John Kohl <jtk@atria.com> or <jtkohl@mit.edu>
working for but not representing:	Atria Software
sometimes hacking on:			NetBSD/i386