Module Name:    src
Committed By:   tsutsui
Date:           Sun Oct 14 18:38:32 UTC 2012

Modified Files:
        src/sys/arch/x68k/dev: fd.c fdreg.h

Log Message:
Add floppy format support.  Mostly taken from sys/dev/isa/fd.c.

Tested both fdNa (1232KB, 1024bytes/sector, 8sectors/track) and
fdNc (1200KB, 512bytes/sector, 15sectors/track) format on X68030
using fdformat(1).

Finally we can prepare NetBSD/x68k install floppies without Human68k
(except actual initial bootstrap).


To generate a diff of this commit:
cvs rdiff -u -r1.103 -r1.104 src/sys/arch/x68k/dev/fd.c
cvs rdiff -u -r1.5 -r1.6 src/sys/arch/x68k/dev/fdreg.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/x68k/dev/fd.c
diff -u src/sys/arch/x68k/dev/fd.c:1.103 src/sys/arch/x68k/dev/fd.c:1.104
--- src/sys/arch/x68k/dev/fd.c:1.103	Sun Oct 14 17:25:59 2012
+++ src/sys/arch/x68k/dev/fd.c	Sun Oct 14 18:38:32 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: fd.c,v 1.103 2012/10/14 17:25:59 tsutsui Exp $	*/
+/*	$NetBSD: fd.c,v 1.104 2012/10/14 18:38:32 tsutsui Exp $	*/
 
 /*-
  * Copyright (c) 1998 The NetBSD Foundation, Inc.
@@ -64,7 +64,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: fd.c,v 1.103 2012/10/14 17:25:59 tsutsui Exp $");
+__KERNEL_RCSID(0, "$NetBSD: fd.c,v 1.104 2012/10/14 18:38:32 tsutsui Exp $");
 
 #include "opt_ddb.h"
 #include "opt_m68k_arch.h"
@@ -87,6 +87,7 @@ __KERNEL_RCSID(0, "$NetBSD: fd.c,v 1.103
 #include <sys/uio.h>
 #include <sys/syslog.h>
 #include <sys/queue.h>
+#include <sys/proc.h>
 #include <sys/fdio.h>
 #include <sys/rnd.h>
 
@@ -112,6 +113,9 @@ int     fddebug = 0;
 #define FDUNIT(dev)	(minor(dev) / 8)
 #define FDTYPE(dev)	(minor(dev) % 8)
 
+/* (mis)use device use flag to identify format operation */
+#define B_FORMAT B_DEVPRIVATE
+
 enum fdc_state {
 	DEVIDLE = 0,
 	MOTORWAIT,
@@ -184,19 +188,29 @@ struct fd_type {
 	int	size;		/* size of disk in sectors */
 	int	step;		/* steps per cylinder */
 	int	rate;		/* transfer speed code */
+	uint8_t	fillbyte;	/* format fill byte */
+	uint8_t	interleave;	/* interleave factor (formatting) */
 	const char *name;
 };
 
 /* The order of entries in the following table is important -- BEWARE! */
 struct fd_type fd_types[] = {
-        {  8,2,16,3,0xff,0xdf,0x35,0x74,77,1232,1,FDC_500KBPS, "1.2MB/[1024bytes/sector]"    }, /* 1.2 MB japanese format */
-        { 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS,"1.44MB"    }, /* 1.44MB diskette */
-        { 15,2,30,2,0xff,0xdf,0x1b,0x54,80,2400,1,FDC_500KBPS, "1.2MB"    }, /* 1.2 MB AT-diskettes */
-        {  9,2,18,2,0xff,0xdf,0x23,0x50,40, 720,2,FDC_300KBPS, "360KB/AT" }, /* 360kB in 1.2MB drive */
-        {  9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,1,FDC_250KBPS, "360KB/PC" }, /* 360kB PC diskettes */
-        {  9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS, "720KB"    }, /* 3.5" 720kB diskette */
-        {  9,2,18,2,0xff,0xdf,0x23,0x50,80,1440,1,FDC_300KBPS, "720KB/x"  }, /* 720kB in 1.2MB drive */
-        {  9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS, "360KB/x"  }, /* 360kB in 720kB drive */
+	{  8,2,16,3,0xff,0xdf,0x35,0x74,77,1232,1,FDC_500KBPS, 0xf6, 1,
+	    "1.2MB/[1024bytes/sector]"    }, /* 1.2 MB japanese format */
+	{ 18,2,36,2,0xff,0xcf,0x1b,0x6c,80,2880,1,FDC_500KBPS, 0xf6, 1,
+	    "1.44MB"    }, /* 1.44MB diskette */
+	{ 15,2,30,2,0xff,0xdf,0x1b,0x54,80,2400,1,FDC_500KBPS, 0xf6, 1,
+	    "1.2MB"    }, /* 1.2 MB AT-diskettes */
+	{  9,2,18,2,0xff,0xdf,0x23,0x50,40, 720,2,FDC_300KBPS, 0xf6, 1,
+	    "360KB/AT" }, /* 360kB in 1.2MB drive */
+	{  9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,1,FDC_250KBPS, 0xf6, 1,
+	    "360KB/PC" }, /* 360kB PC diskettes */
+	{  9,2,18,2,0xff,0xdf,0x2a,0x50,80,1440,1,FDC_250KBPS, 0xf6, 1,
+	    "720KB"    }, /* 3.5" 720kB diskette */
+	{  9,2,18,2,0xff,0xdf,0x23,0x50,80,1440,1,FDC_300KBPS, 0xf6, 1,
+	    "720KB/x"  }, /* 720kB in 1.2MB drive */
+	{  9,2,18,2,0xff,0xdf,0x2a,0x50,40, 720,2,FDC_250KBPS, 0xf6, 1,
+	    "360KB/x"  }, /* 360kB in 720kB drive */
 };
 
 /* software state, per disk (with up to 4 disks per ctlr) */
@@ -283,6 +297,7 @@ void fdcpseudointr(void *);
 void fdcretry(struct fdc_softc *);
 void fdfinish(struct fd_softc *, struct buf *);
 inline struct fd_type *fd_dev_to_type(struct fd_softc *, dev_t);
+int fdformat(dev_t, struct ne7_fd_formb *, struct lwp *);
 static int fdcpoll(struct fdc_softc *);
 static int fdgetdisklabel(struct fd_softc *, dev_t);
 static void fd_do_eject(struct fdc_softc *, int);
@@ -659,7 +674,8 @@ fdstrategy(struct buf *bp)
 	}
 
 	if (bp->b_blkno < 0 ||
-	    (bp->b_bcount % FDC_BSIZE) != 0) {
+	    ((bp->b_bcount % FDC_BSIZE) != 0 &&
+	     (bp->b_flags & B_FORMAT) == 0)) {
 		DPRINTF(("fdstrategy: unit=%d, blkno=%" PRId64 ", "
 		    "bcount=%d\n", unit,
 		    bp->b_blkno, bp->b_bcount));
@@ -943,6 +959,9 @@ fdclose(dev_t dev, int flags, int mode, 
 		break;
 	}
 
+	/* clear flags */
+	fd->sc_opts &= ~(FDOPT_NORETRY | FDOPT_SILENT);
+
 	if ((fd->sc_flags & FD_OPEN) == 0) {
 		bus_space_write_1(fdc->sc_iot, fdc->sc_ioh, fdout,
 		    (1 << unit));
@@ -1059,6 +1078,7 @@ fdcintr(void *arg)
 	int read, head, sec, pos, i, sectrac, nblks;
 	int tmp;
 	struct fd_type *type;
+	struct ne7_fd_formb *finfo = NULL;
 
  loop:
 	fd = TAILQ_FIRST(&fdc->sc_drives);
@@ -1088,6 +1108,9 @@ fdcintr(void *arg)
 		goto loop;
 	}
 
+	if (bp->b_flags & B_FORMAT)
+		finfo = (struct ne7_fd_formb *)bp->b_data;
+
 	switch (fdc->sc_state) {
 	case DEVIDLE:
 		DPRINTF(("fdcintr: in DEVIDLE\n"));
@@ -1154,10 +1177,13 @@ fdcintr(void *arg)
 	doio:
 		DPRINTF(("fdcintr: DOIO: "));
 		type = fd->sc_type;
+		if (finfo != NULL)
+			fd->sc_skip = (char *)&(finfo->fd_formb_cylno(0)) -
+			    (char *)finfo;
 		sectrac = type->sectrac;
 		pos = fd->sc_blkno % (sectrac * (1 << (type->secsize - 2)));
 		sec = pos / (1 << (type->secsize - 2));
-		if (type->secsize == 2) {
+		if (finfo != NULL || type->secsize == 2) {
 			fd->sc_part = SEC_P11;
 			nblks = (sectrac - sec) << (type->secsize - 2);
 			nblks = min(nblks, fd->sc_bcount / FDC_BSIZE);
@@ -1189,7 +1215,8 @@ fdcintr(void *arg)
 		nblks = min(nblks, FDC_MAXIOSIZE / FDC_BSIZE);
 		DPRINTF((" %d\n", nblks));
 		fd->sc_nblks = nblks;
-		fd->sc_nbytes = nblks * FDC_BSIZE;
+		fd->sc_nbytes =
+		    (finfo != NULL) ? bp->b_bcount : nblks * FDC_BSIZE;
 		head = (fd->sc_blkno
 		    % (type->seccyl * (1 << (type->secsize - 2))))
 		    / (type->sectrac * (1 << (type->secsize - 2)));
@@ -1220,23 +1247,37 @@ fdcintr(void *arg)
 		DPRINTF(("C H R N: %d %d %d %d\n", fd->sc_cylin, head, sec,
 		    type->secsize));
 
-		if (fd->sc_part != SEC_P11)
+		if (finfo == NULL && fd->sc_part != SEC_P11)
 			goto docopy;
 
 		fdc_dmastart(fdc, read, (char *)bp->b_data + fd->sc_skip,
 		    fd->sc_nbytes);
-		if (read)
-			out_fdc(iot, ioh, NE7CMD_READ);	/* READ */
-		else
-			out_fdc(iot, ioh, NE7CMD_WRITE); /* WRITE */
-		out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
-		out_fdc(iot, ioh, bp->b_cylinder);	/* cylinder */
-		out_fdc(iot, ioh, head);
-		out_fdc(iot, ioh, sec + 1);		/* sector +1 */
-		out_fdc(iot, ioh, type->secsize);	/* sector size */
-		out_fdc(iot, ioh, type->sectrac);	/* sectors/track */
-		out_fdc(iot, ioh, type->gap1);		/* gap1 size */
-		out_fdc(iot, ioh, type->datalen);	/* data length */
+		if (finfo != NULL) {
+			/* formatting */
+			if (out_fdc(iot, ioh, NE7CMD_FORMAT) < 0) {
+				fdc->sc_errors = 4;
+				fdcretry(fdc);
+				goto loop;
+			}
+			out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
+			out_fdc(iot, ioh, finfo->fd_formb_secshift);
+			out_fdc(iot, ioh, finfo->fd_formb_nsecs);
+			out_fdc(iot, ioh, finfo->fd_formb_gaplen);
+			out_fdc(iot, ioh, finfo->fd_formb_fillbyte);
+		} else {
+			if (read)
+				out_fdc(iot, ioh, NE7CMD_READ);	/* READ */
+			else
+				out_fdc(iot, ioh, NE7CMD_WRITE); /* WRITE */
+			out_fdc(iot, ioh, (head << 2) | fd->sc_drive);
+			out_fdc(iot, ioh, bp->b_cylinder);	/* cylinder */
+			out_fdc(iot, ioh, head);
+			out_fdc(iot, ioh, sec + 1);		/* sector +1 */
+			out_fdc(iot, ioh, type->secsize); /* sector size */
+			out_fdc(iot, ioh, type->sectrac); /* sectors/track */
+			out_fdc(iot, ioh, type->gap1);		/* gap1 size */
+			out_fdc(iot, ioh, type->datalen); /* data length */
+		}
 		fdc->sc_state = IOCOMPLETE;
 
 		disk_busy(&fd->sc_dk);
@@ -1385,7 +1426,7 @@ fdcintr(void *arg)
 		fd->sc_skip += fd->sc_nbytes;
 		fd->sc_bcount -= fd->sc_nbytes;
 		DPRINTF(("fd->sc_bcount = %d\n", fd->sc_bcount));
-		if (fd->sc_bcount > 0) {
+		if (finfo == NULL && fd->sc_bcount > 0) {
 			bp->b_cylinder = fd->sc_blkno
 			    / (fd->sc_type->seccyl
 			    * (1 << (fd->sc_type->secsize - 2)));
@@ -1505,6 +1546,9 @@ fdcretry(struct fdc_softc *fdc)
 	fd = TAILQ_FIRST(&fdc->sc_drives);
 	bp = bufq_peek(fd->sc_q);
 
+	if (fd->sc_opts & FDOPT_NORETRY)
+		goto fail;
+
 	switch (fdc->sc_errors) {
 	case 0:
 		/* try again */
@@ -1524,9 +1568,12 @@ fdcretry(struct fdc_softc *fdc)
 		break;
 
 	default:
-		diskerr(bp, "fd", "hard error", LOG_PRINTF,
-		    fd->sc_skip, (struct disklabel *)NULL);
-		fdcpstatus(7, fdc);
+	fail:
+		if ((fd->sc_opts & FDOPT_SILENT) == 0) {
+			diskerr(bp, "fd", "hard error", LOG_PRINTF,
+			    fd->sc_skip, (struct disklabel *)NULL);
+			fdcpstatus(7, fdc);
+		}
 
 		bp->b_error = EIO;
 		fdfinish(fd, bp);
@@ -1539,9 +1586,15 @@ fdioctl(dev_t dev, u_long cmd, void *add
 {
 	struct fd_softc *fd = device_lookup_private(&fd_cd, FDUNIT(dev));
 	struct fdc_softc *fdc = device_private(device_parent(fd->sc_dev));
+	struct fdformat_parms *form_parms;
+	struct fdformat_cmd *form_cmd;
+	struct ne7_fd_formb *fd_formb;
 	int part = DISKPART(dev);
 	struct disklabel buffer;
 	int error;
+	unsigned int scratch;
+	int il[FD_MAX_NSEC + 1];
+	int i, j;
 
 	DPRINTF(("fdioctl:"));
 	switch (cmd) {
@@ -1591,6 +1644,134 @@ fdioctl(dev_t dev, u_long cmd, void *add
 		error = writedisklabel(dev, fdstrategy, &buffer, NULL);
 		return error;
 
+	case FDIOCGETFORMAT:
+		DPRINTF(("FDIOCGETFORMAT\n"));
+		form_parms = (struct fdformat_parms *)addr;
+		form_parms->fdformat_version = FDFORMAT_VERSION;
+		form_parms->nbps = 128 * (1 << fd->sc_type->secsize);
+		form_parms->ncyl = fd->sc_type->cyls;
+		form_parms->nspt = fd->sc_type->sectrac;
+		form_parms->ntrk = fd->sc_type->heads;
+		form_parms->stepspercyl = fd->sc_type->step;
+		form_parms->gaplen = fd->sc_type->gap2;
+		form_parms->fillbyte = fd->sc_type->fillbyte;
+		form_parms->interleave = fd->sc_type->interleave;
+		switch (fd->sc_type->rate) {
+		case FDC_500KBPS:
+			form_parms->xfer_rate = 500 * 1024;
+			break;
+		case FDC_300KBPS:
+			form_parms->xfer_rate = 300 * 1024;
+			break;
+		case FDC_250KBPS:
+			form_parms->xfer_rate = 250 * 1024;
+			break;
+		default:
+			return EINVAL;
+		}
+		return 0;
+
+	case FDIOCSETFORMAT:
+		DPRINTF(("FDIOCSETFORMAT\n"));
+		if((flag & FWRITE) == 0)
+			return EBADF;	/* must be opened for writing */
+		form_parms = (struct fdformat_parms *)addr;
+		if (form_parms->fdformat_version != FDFORMAT_VERSION)
+			return EINVAL;	/* wrong version of formatting prog */
+
+		scratch = form_parms->nbps >> 7;
+		if ((form_parms->nbps & 0x7f) || ffs(scratch) == 0 ||
+		    scratch & ~(1 << (ffs(scratch) - 1)))
+			/* not a power-of-two multiple of 128 */
+			return EINVAL;
+
+		switch (form_parms->xfer_rate) {
+		case 500 * 1024:
+			fd->sc_type->rate = FDC_500KBPS;
+			break;
+		case 300 * 1024:
+			fd->sc_type->rate = FDC_300KBPS;
+			break;
+		case 250 * 1024:
+			fd->sc_type->rate = FDC_250KBPS;
+			break;
+		default:
+			return EINVAL;
+		}
+
+		if (form_parms->nspt > FD_MAX_NSEC ||
+		    form_parms->fillbyte > 0xff ||
+		    form_parms->interleave > 0xff)
+			return EINVAL;
+		fd->sc_type->sectrac = form_parms->nspt;
+		if (form_parms->ntrk != 2 && form_parms->ntrk != 1)
+			return EINVAL;
+		fd->sc_type->heads = form_parms->ntrk;
+		fd->sc_type->seccyl = form_parms->nspt * form_parms->ntrk;
+		fd->sc_type->secsize = ffs(scratch)-1;
+		fd->sc_type->gap2 = form_parms->gaplen;
+		fd->sc_type->cyls = form_parms->ncyl;
+		fd->sc_type->size = fd->sc_type->seccyl * form_parms->ncyl *
+		    form_parms->nbps / DEV_BSIZE;
+		fd->sc_type->step = form_parms->stepspercyl;
+		fd->sc_type->fillbyte = form_parms->fillbyte;
+		fd->sc_type->interleave = form_parms->interleave;
+		return 0;
+
+	case FDIOCFORMAT_TRACK:
+		DPRINTF(("FDIOCFORMAT_TRACK\n"));
+		if ((flag & FWRITE) == 0)
+			return EBADF;	/* must be opened for writing */
+		form_cmd = (struct fdformat_cmd *)addr;
+		if (form_cmd->formatcmd_version != FDFORMAT_VERSION)
+			return EINVAL;	/* wrong version of formatting prog */
+
+		if (form_cmd->head >= fd->sc_type->heads ||
+		    form_cmd->cylinder >= fd->sc_type->cyls) {
+			return EINVAL;
+		}
+
+		fd_formb = malloc(sizeof(struct ne7_fd_formb),
+		    M_TEMP, M_NOWAIT);
+		if (fd_formb == NULL)
+			return ENOMEM;
+
+		fd_formb->head = form_cmd->head;
+		fd_formb->cyl = form_cmd->cylinder;
+		fd_formb->transfer_rate = fd->sc_type->rate;
+		fd_formb->fd_formb_secshift = fd->sc_type->secsize;
+		fd_formb->fd_formb_nsecs = fd->sc_type->sectrac;
+		fd_formb->fd_formb_gaplen = fd->sc_type->gap2;
+		fd_formb->fd_formb_fillbyte = fd->sc_type->fillbyte;
+
+		memset(il, 0, sizeof il);
+		for (j = 0, i = 1; i <= fd_formb->fd_formb_nsecs; i++) {
+			while (il[(j % fd_formb->fd_formb_nsecs) + 1])
+				j++;
+			il[(j % fd_formb->fd_formb_nsecs)+  1] = i;
+			j += fd->sc_type->interleave;
+		}
+		for (i = 0; i < fd_formb->fd_formb_nsecs; i++) {
+			fd_formb->fd_formb_cylno(i) = form_cmd->cylinder;
+			fd_formb->fd_formb_headno(i) = form_cmd->head;
+			fd_formb->fd_formb_secno(i) = il[i + 1];
+			fd_formb->fd_formb_secsize(i) = fd->sc_type->secsize;
+		}
+
+		error = fdformat(dev, fd_formb, l);
+		free(fd_formb, M_TEMP);
+		return error;
+
+	case FDIOCGETOPTS:		/* get drive options */
+		DPRINTF(("FDIOCGETOPTS\n"));
+		*(int *)addr = fd->sc_opts;
+		return 0;
+
+	case FDIOCSETOPTS:        	/* set drive options */
+		DPRINTF(("FDIOCSETOPTS\n"));
+		fd->sc_opts = *(int *)addr;
+		return 0;
+
 	case DIOCLOCK:
 		/*
 		 * Nothing to do here, really.
@@ -1625,6 +1806,48 @@ fdioctl(dev_t dev, u_long cmd, void *add
 #endif
 }
 
+int
+fdformat(dev_t dev, struct ne7_fd_formb *finfo, struct lwp *l)
+{
+	int rv = 0;
+	struct fd_softc *fd = device_lookup_private(&fd_cd, FDUNIT(dev));
+	struct fd_type *type = fd->sc_type;
+	struct buf *bp;
+
+	/* set up a buffer header for fdstrategy() */
+	bp = getiobuf(NULL, false);
+	if (bp == NULL)
+		return ENOBUFS;
+
+	bp->b_cflags = BC_BUSY;
+	bp->b_flags = B_PHYS | B_FORMAT;
+	bp->b_proc = l->l_proc;
+	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) * (128 << type->secsize) / DEV_BSIZE;
+
+	bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs;
+	bp->b_data = (void *)finfo;
+
+#ifdef FD_DEBUG
+	printf("fdformat: blkno %" PRIx64 " count %x\n",
+	    bp->b_blkno, bp->b_bcount);
+#endif
+
+	/* now do the format */
+	fdstrategy(bp);
+
+	/* ...and wait for it to complete */
+	rv = biowait(bp);
+	putiobuf(bp);
+	return rv;
+}
+
 void
 fd_do_eject(struct fdc_softc *fdc, int unit)
 {

Index: src/sys/arch/x68k/dev/fdreg.h
diff -u src/sys/arch/x68k/dev/fdreg.h:1.5 src/sys/arch/x68k/dev/fdreg.h:1.6
--- src/sys/arch/x68k/dev/fdreg.h:1.5	Tue May 15 12:17:33 2012
+++ src/sys/arch/x68k/dev/fdreg.h	Sun Oct 14 18:38:32 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: fdreg.h,v 1.5 2012/05/15 12:17:33 tsutsui Exp $	*/
+/*	$NetBSD: fdreg.h,v 1.6 2012/10/14 18:38:32 tsutsui Exp $	*/
 
 /*-
  * Copyright (c) 1991 The Regents of the University of California.
@@ -60,3 +60,52 @@
 #define FDC_INTR 96		/* interrupt vector */
 #define FDC_DMA 0		/* DMA ch# */
 #define FDC_DMAINTR 100		/* DMA interrupt vector */
+
+/*
+ * fdformat parameters
+ */
+
+#define FD_MAX_NSEC	18	/* highest known number of spt */
+
+struct ne7_fd_formb {
+	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-dependent since it exactly
+			 * matches the byte sequence to write to FDC
+			 * during its `format track' operation
+			 */
+			uint8_t secshift; /* 0 -> 128, ...; usually 2 -> 512 */
+			uint8_t nsecs;    /* must be <= FD_MAX_NSEC */
+			uint8_t gaplen;   /* GAP 3 length; usually 84 */
+			uint8_t 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)
+				 */
+				uint8_t cylno;		/* 0 thru 79 (or 39) */
+				uint8_t headno;		/* 0, or 1 */
+				uint8_t secno;		/* starting at 1! */
+				uint8_t secsize;	/* usually 2 */
+			} idfields[FD_MAX_NSEC]; /* 0 <= idx < nsecs used */
+		} structured;
+		uint8_t 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

Reply via email to