Subject: Re: port-i386/646: fdformat missing on i386
To: None <gnats-bugs@NetBSD.ORG>
From: John Kohl <jtk@kolvir.blrc.ma.us>
List: netbsd-bugs
Date: 05/09/1995 23:43:37
Here are the patches for fdformat that I didn't send in earlier.

diff -u -x RCS -x CVS -x *~ -x *,v -x Repository -x Entries -P -r /u2/src/sys/arch/i386/include/ioctl_fd.h sys/arch/i386/include/ioctl_fd.h
--- /u2/src/sys/arch/i386/include/ioctl_fd.h	Wed Dec 31 19:00:00 1969
+++ sys/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__ */
diff -u -x RCS -x CVS -x *~ -x *,v -x Repository -x Entries -P -r /u2/src/sys/arch/i386/isa/fd.c sys/arch/i386/isa/fd.c
--- /u2/src/sys/arch/i386/isa/fd.c	Fri May  5 06:03:18 1995
+++ sys/arch/i386/isa/fd.c	Sat May  6 10:48:18 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 <dev/isa/isavar.h>
 #include <dev/isa/isadmavar.h>
@@ -67,6 +75,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 {
@@ -115,25 +126,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[] = {
@@ -156,6 +149,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 */
@@ -200,6 +194,7 @@
 int fdcintr __P((void *));
 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)
@@ -461,7 +456,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;
 	}
@@ -695,6 +691,7 @@
 	struct fd_softc *fd = fdcd.cd_devs[FDUNIT(dev)];
 
 	fd->sc_flags &= ~FD_OPEN;
+	fd->sc_opts &= ~FDOPT_NORETRY;
 	return 0;
 }
 
@@ -764,6 +761,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)
@@ -799,6 +799,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? */
@@ -817,6 +818,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;
@@ -867,12 +871,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
@@ -899,6 +906,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
@@ -911,6 +931,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);
@@ -982,7 +1003,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;
 		}
@@ -1059,6 +1080,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 */
@@ -1076,6 +1099,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",
@@ -1109,11 +1133,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;
@@ -1150,6 +1175,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;
 	}
@@ -1157,4 +1204,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 -u -x RCS -x CVS -x *~ -x *,v -x Repository -x Entries -P -r /u2/src/usr.bin/fdformat/Makefile usr.bin/fdformat/Makefile
--- /u2/src/usr.bin/fdformat/Makefile	Wed Dec 31 19:00:00 1969
+++ usr.bin/fdformat/Makefile	Sat Dec 17 18:56:41 1994
@@ -0,0 +1,9 @@
+#
+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>
diff -u -x RCS -x CVS -x *~ -x *,v -x Repository -x Entries -P -r /u2/src/usr.bin/fdformat/fdformat.1 usr.bin/fdformat/fdformat.1
--- /u2/src/usr.bin/fdformat/fdformat.1	Wed Dec 31 19:00:00 1969
+++ usr.bin/fdformat/fdformat.1	Sat Dec 17 19:02:04 1994
@@ -0,0 +1,133 @@
+.\" 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.
diff -u -x RCS -x CVS -x *~ -x *,v -x Repository -x Entries -P -r /u2/src/usr.bin/fdformat/fdformat.c usr.bin/fdformat/fdformat.c
--- /u2/src/usr.bin/fdformat/fdformat.c	Wed Dec 31 19:00:00 1969
+++ usr.bin/fdformat/fdformat.c	Sat Dec 17 18:54:05 1994
@@ -0,0 +1,384 @@
+/*
+ * 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:
+ */