summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/fdc/fdc.c765
-rw-r--r--sys/dev/fdc/fdcreg.h13
-rw-r--r--sys/isa/fd.c765
-rw-r--r--sys/isa/fdreg.h13
-rw-r--r--sys/sys/fdcio.h40
-rw-r--r--usr.sbin/fdcontrol/Makefile4
-rw-r--r--usr.sbin/fdcontrol/fdcontrol.c206
-rw-r--r--usr.sbin/fdformat/fdformat.c297
-rw-r--r--usr.sbin/fdread/fdutil.c392
-rw-r--r--usr.sbin/fdread/fdutil.h8
10 files changed, 1683 insertions, 820 deletions
diff --git a/sys/dev/fdc/fdc.c b/sys/dev/fdc/fdc.c
index 845f520..04be1bd 100644
--- a/sys/dev/fdc/fdc.c
+++ b/sys/dev/fdc/fdc.c
@@ -65,6 +65,7 @@
#include <sys/disklabel.h>
#include <sys/fcntl.h>
#include <sys/fdcio.h>
+#include <sys/filio.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
@@ -87,7 +88,7 @@
enum fdc_type
{
- FDC_NE765, FDC_I82077, FDC_NE72065, FDC_UNKNOWN = -1
+ FDC_NE765, FDC_ENHANCED, FDC_UNKNOWN = -1
};
enum fdc_states {
@@ -189,6 +190,7 @@ typedef struct fdc_data *fdc_p;
typedef enum fdc_type fdc_t;
#define FDUNIT(s) (((s) >> 6) & 3)
+#define FDNUMTOUNIT(n) (((n) & 3) << 6)
#define FDTYPE(s) ((s) & 0x3f)
/*
@@ -210,13 +212,9 @@ static __inline T fdc_get_ ## A(device_t dev) \
}
FDC_ACCESSOR(fdunit, FDUNIT, int)
-/* configuration flags */
-#define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */
+/* configuration flags for fdc */
#define FDC_NO_FIFO (1 << 2) /* do not enable FIFO */
-/* internally used only, not really from CMOS: */
-#define RTCFDT_144M_PRETENDED 0x1000
-
/* error returns for fd_cmd() */
#define FD_FAILED -1
#define FD_NOT_VALID -2
@@ -229,54 +227,65 @@ FDC_ACCESSOR(fdunit, FDUNIT, int)
*/
#define FDC_DMAOV_MAX 25
-#define NUMTYPES 17
-#define NUMDENS (NUMTYPES - 7)
-
-#define NO_TYPE 0
-#define FD_1720 1
-#define FD_1480 2
-#define FD_1440 3
-#define FD_1200 4
-#define FD_820 5
-#define FD_800 6
-#define FD_720 7
-#define FD_360 8
-#define FD_640 9
-#define FD_1232 10
-
-#define FD_1480in5_25 11
-#define FD_1440in5_25 12
-#define FD_820in5_25 13
-#define FD_800in5_25 14
-#define FD_720in5_25 15
-#define FD_360in5_25 16
-#define FD_640in5_25 17
+/*
+ * Number of subdevices that can be used for different density types.
+ * By now, the lower 6 bit of the minor number are reserved for this,
+ * allowing for up to 64 subdevices, but we only use 16 out of this.
+ * Density #0 is used for automatic format detection, the other
+ * densities are available as programmable densities (for assignment
+ * by fdcontrol(8)).
+ * The upper 2 bits of the minor number are reserved for the subunit
+ * (drive #) per controller.
+ */
+#define NUMDENS 16
#define BIO_RDSECTID BIO_CMD1
-static struct fd_type fd_types[NUMTYPES] =
+/*
+ * List of native drive densities. Order must match enum fd_drivetype
+ * in <sys/fdcio.h>. Upon attaching the drive, each of the
+ * programmable subdevices is initialized with the native density
+ * definition.
+ */
+static struct fd_type fd_native_types[] =
{
-{ 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
-{ 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
-{ 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
-{ 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */
-{ 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */
-{ 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */
-{ 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */
-{ 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */
-{ 8,2,0xFF,0x2A,80,1280,1,FDC_250KBPS,2,0x50,1 }, /* 640K in DD 5.25in */
-{ 8,3,0xFF,0x35,77,1232,1,FDC_500KBPS,2,0x74,1 }, /* 1.23M in HD 5.25in */
-
-{ 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
-{ 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
-{ 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */
-{ 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */
-{ 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */
-{ 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */
-{ 8,2,0xFF,0x2A,80,1280,1,FDC_300KBPS,2,0x50,1 }, /* 640K in HD 5.25in */
+{ 0 }, /* FDT_NONE */
+{ 9,2,0xFF,0x2A,40, 720,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* FDT_360K */
+{ 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* FDT_12M */
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* FDT_720K */
+{ 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* FDT_144M */
+#if 0 /* we currently don't handle 2.88 MB */
+{ 36,2,0xFF,0x1B,80,5760,FDC_1MBPS, 2,0x4C,1,1,FL_MFM|FL_PERPND } /*FDT_288M*/
+#else
+{ 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* FDT_144M */
+#endif
+};
+
+/*
+ * 360 KB 5.25" and 720 KB 3.5" drives don't have automatic density
+ * selection, they just start out with their native density (or lose).
+ * So 1.2 MB 5.25", 1.44 MB 3.5", and 2.88 MB 3.5" drives have their
+ * respective lists of densities to search for.
+ */
+static struct fd_type fd_searchlist_12m[] = {
+{ 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* 1.2M */
+{ 9,2,0xFF,0x23,40, 720,FDC_300KBPS,2,0x50,1,0,FL_MFM|FL_2STEP }, /* 360K */
+{ 9,2,0xFF,0x20,80,1440,FDC_300KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+};
+
+static struct fd_type fd_searchlist_144m[] = {
+{ 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.44M */
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
};
-#define DRVS_PER_CTLR 2 /* 2 floppies */
+/* We search for 1.44M first since this is the most common case. */
+static struct fd_type fd_searchlist_288m[] = {
+{ 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.44M */
+#if 0
+{ 36,2,0xFF,0x1B,80,5760,FDC_1MBPS, 2,0x4C,1,1,FL_MFM|FL_PERPND } /* 2.88M */
+#endif
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+};
#define MAX_SEC_SIZE (128 << 3)
#define MAX_CYLINDER 85 /* some people really stress their drives
@@ -291,25 +300,27 @@ static devclass_t fdc_devclass;
struct fd_data {
struct fdc_data *fdc; /* pointer to controller structure */
int fdsu; /* this units number on this controller */
- int type; /* Drive type (FD_1440...) */
- struct fd_type *ft; /* pointer to the type descriptor */
+ enum fd_drivetype type; /* drive type */
+ struct fd_type *ft; /* pointer to current type descriptor */
+ struct fd_type fts[NUMDENS]; /* type descriptors */
int flags;
#define FD_OPEN 0x01 /* it's open */
-#define FD_ACTIVE 0x02 /* it's active */
-#define FD_MOTOR 0x04 /* motor should be on */
-#define FD_MOTOR_WAIT 0x08 /* motor coming up */
+#define FD_NONBLOCK 0x02 /* O_NONBLOCK set */
+#define FD_ACTIVE 0x04 /* it's active */
+#define FD_MOTOR 0x08 /* motor should be on */
+#define FD_MOTOR_WAIT 0x10 /* motor coming up */
+#define FD_UA 0x20 /* force unit attention */
int skip;
int hddrv;
#define FD_NO_TRACK -2
int track; /* where we think the head is */
- int options; /* user configurable options, see ioctl_fd.h */
+ int options; /* user configurable options, see fdcio.h */
struct callout_handle toffhandle;
struct callout_handle tohandle;
struct devstat device_stats;
eventhandler_tag clonetag;
dev_t masterdev;
-#define NCLONEDEVS 10 /* must match the table below */
- dev_t clonedevs[NCLONEDEVS];
+ dev_t clonedevs[NUMDENS - 1];
device_t dev;
fdu_t fdu;
};
@@ -319,6 +330,17 @@ struct fdc_ivars {
};
static devclass_t fd_devclass;
+/* configuration flags for fd */
+#define FD_TYPEMASK 0x0f /* drive type, matches enum
+ * fd_drivetype; on i386 machines, if
+ * given as 0, use RTC type for fd0
+ * and fd1 */
+#define FD_DTYPE(flags) ((flags) & FD_TYPEMASK)
+#define FD_NO_CHLINE 0x10 /* drive does not support changeline
+ * aka. unit attention */
+#define FD_NO_PROBE 0x20 /* don't probe drive (seek test), just
+ * assume it is there */
+
/*
* Throughout this file the following conventions will be used:
*
@@ -391,6 +413,7 @@ static timeout_t fd_iotimeout;
static timeout_t fd_pseudointr;
static driver_intr_t fdc_intr;
static int fdcpio(fdc_p, long, caddr_t, u_int);
+static int fdautoselect(dev_t);
static int fdstate(struct fdc_data *);
static int retrier(struct fdc_data *);
static void fdbiodone(struct bio *);
@@ -450,16 +473,12 @@ fdctl_wr_pcmcia(fdc_p fdc, u_int8_t v)
}
#endif
-#if 0
-
static u_int8_t
fdin_rd(fdc_p fdc)
{
return bus_space_read_1(fdc->portt, fdc->porth, FDIN);
}
-#endif
-
#define CDEV_MAJOR 9
static struct cdevsw fd_cdevsw = {
/* open */ Fdopen,
@@ -861,13 +880,11 @@ fdc_probe(device_t dev)
device_set_desc(dev, "NEC 765 or clone");
fdc->fdct = FDC_NE765;
break;
- case 0x81:
- device_set_desc(dev, "Intel 82077 or clone");
- fdc->fdct = FDC_I82077;
- break;
+ case 0x81: /* not mentioned in any hardware doc */
case 0x90:
- device_set_desc(dev, "NEC 72065B or clone");
- fdc->fdct = FDC_NE72065;
+ device_set_desc(dev,
+ "enhanced floppy controller (i82077, NE72065 or clone)");
+ fdc->fdct = FDC_ENHANCED;
break;
default:
device_set_desc(dev, "generic floppy controller");
@@ -960,7 +977,7 @@ fdc_detach(device_t dev)
static void
fdc_add_child(device_t dev, const char *name, int unit)
{
- int disabled;
+ int disabled, flags;
struct fdc_ivars *ivar;
device_t child;
@@ -973,6 +990,8 @@ fdc_add_child(device_t dev, const char *name, int unit)
if (child == NULL)
return;
device_set_ivars(child, ivar);
+ if (resource_int_value(name, unit, "flags", &flags) == 0)
+ device_set_flags(child, flags);
if (resource_int_value(name, unit, "disabled", &disabled) == 0
&& disabled != 0)
device_disable(child);
@@ -1034,11 +1053,14 @@ fdc_attach(device_t dev)
static int
fdc_print_child(device_t me, device_t child)
{
- int retval = 0;
+ int retval = 0, flags;
retval += bus_print_child_header(me, child);
- retval += printf(" on %s drive %d\n", device_get_nameunit(me),
+ retval += printf(" on %s drive %d", device_get_nameunit(me),
fdc_get_fdunit(child));
+ if ((flags = device_get_flags(me)) != 0)
+ retval += printf(" flags %#x", flags);
+ retval += printf("\n");
return (retval);
}
@@ -1098,62 +1120,54 @@ DRIVER_MODULE(fdc, pccard, fdc_pccard_driver, fdc_devclass, 0, 0);
#endif /* NCARD > 0 */
-static struct {
- char *match;
- int minor;
- int link;
-} fd_suffix[] = {
- /*
- * Genuine clone devices must come first, and their number must
- * match NCLONEDEVS above.
- */
- { ".1720", 1, 0 },
- { ".1480", 2, 0 },
- { ".1440", 3, 0 },
- { ".1200", 4, 0 },
- { ".820", 5, 0 },
- { ".800", 6, 0 },
- { ".720", 7, 0 },
- { ".360", 8, 0 },
- { ".640", 9, 0 },
- { ".1232", 10, 0 },
- { "a", 0, 1 },
- { "b", 0, 1 },
- { "c", 0, 1 },
- { "d", 0, 1 },
- { "e", 0, 1 },
- { "f", 0, 1 },
- { "g", 0, 1 },
- { "h", 0, 1 },
- { 0, 0 }
-};
-
+/*
+ * Create a clone device upon request by devfs.
+ */
static void
fd_clone(void *arg, char *name, int namelen, dev_t *dev)
{
struct fd_data *fd;
- int u, d, i;
+ int i, u;
char *n;
+ size_t l;
fd = (struct fd_data *)arg;
if (*dev != NODEV)
return;
if (dev_stdclone(name, &n, "fd", &u) != 2)
return;
- for (i = 0; ; i++) {
- if (fd_suffix[i].match == NULL)
- return;
- if (strcmp(n, fd_suffix[i].match))
- continue;
- d = fd_suffix[i].minor;
- break;
- }
- if (fd_suffix[i].link == 0) {
- *dev = make_dev(&fd_cdevsw, (u << 6) + d,
- UID_ROOT, GID_OPERATOR, 0640, name);
- fd->clonedevs[i] = *dev;
- } else {
+ l = strlen(n);
+ if (l == 1 && *n >= 'a' && *n <= 'h') {
+ /*
+ * Trailing letters a through h denote
+ * pseudo-partitions. We don't support true
+ * (UFS-style) partitions, so we just implement them
+ * as symlinks if someone asks us nicely.
+ */
*dev = make_dev_alias(fd->masterdev, name);
+ return;
+ }
+ if (l >= 2 && l <= 5 && *n == '.') {
+ /*
+ * Trailing numbers, preceded by a dot, denote
+ * subdevices for different densities. Historically,
+ * they have been named by density (like fd0.1440),
+ * but we allow arbitrary numbers between 1 and 4
+ * digits, so fd0.1 through fd0.15 are possible as
+ * well.
+ */
+ for (i = 1; i < l; i++)
+ if (n[i] < '0' || n[i] > '9')
+ return;
+ for (i = 0; i < NUMDENS - 1; i++)
+ if (fd->clonedevs[i] == NODEV) {
+ *dev = make_dev(&fd_cdevsw,
+ FDNUMTOUNIT(u) + i + 1,
+ UID_ROOT, GID_OPERATOR, 0640,
+ name);
+ fd->clonedevs[i] = *dev;
+ return;
+ }
}
}
@@ -1164,46 +1178,48 @@ static int
fd_probe(device_t dev)
{
int i;
- u_int fdt, st0, st3;
+ u_int st0, st3;
struct fd_data *fd;
struct fdc_data *fdc;
fdsu_t fdsu;
- static int fd_fifo = 0;
+ int flags;
fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */
fd = device_get_softc(dev);
fdc = device_get_softc(device_get_parent(dev));
+ flags = device_get_flags(dev);
bzero(fd, sizeof *fd);
fd->dev = dev;
fd->fdc = fdc;
fd->fdsu = fdsu;
fd->fdu = device_get_unit(dev);
-
-#ifdef __i386__
- /* look up what bios thinks we have */
- switch (fd->fdu) {
- case 0:
- if ((fdc->flags & FDC_ISPCMCIA))
- fdt = RTCFDT_144M;
- else if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0)
- fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED;
- else
- fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
- break;
- case 1:
- fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
- break;
- default:
- fdt = RTCFDT_NONE;
- break;
+ fd->flags = FD_UA; /* make sure fdautoselect() will be called */
+
+ fd->type = FD_DTYPE(flags);
+#if _MACHINE_ARCH == i386
+ if (fd->type == FDT_NONE && (fd->fdu == 0 || fd->fdu == 1)) {
+ /* Look up what the BIOS thinks we have. */
+ if (fd->fdu == 0) {
+ if ((fdc->flags & FDC_ISPCMCIA))
+ /*
+ * Somewhat special. No need to force the
+ * user to set device flags, since the Y-E
+ * Data PCMCIA floppy is always a 1.44 MB
+ * device.
+ */
+ fd->type = FDT_144M;
+ else
+ fd->type = (rtcin(RTC_FDISKETTE) & 0xf0) >> 4;
+ } else {
+ fd->type = rtcin(RTC_FDISKETTE) & 0x0f;
+ }
+ if (fd->type == FDT_288M_1)
+ fd->type = FDT_288M;
}
-#else
- fdt = RTCFDT_144M; /* XXX probably */
-#endif
-
+#endif /* _MACHINE_ARCH == i386 */
/* is there a unit? */
- if (fdt == RTCFDT_NONE)
+ if (fd->type == FDT_NONE)
return (ENXIO);
/* select it */
@@ -1212,94 +1228,88 @@ fd_probe(device_t dev)
DELAY(1000000); /* 1 sec */
/* XXX This doesn't work before the first set_motor() */
- if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN
- && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0
- && enable_fifo(fdc) == 0) {
+ if ((fdc->flags & FDC_HAS_FIFO) == 0 &&
+ fdc->fdct == FDC_ENHANCED &&
+ (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0 &&
+ enable_fifo(fdc) == 0) {
device_printf(device_get_parent(dev),
"FIFO enabled, %d bytes threshold\n", fifo_threshold);
}
- fd_fifo = 1;
-
- if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0)
- && (st3 & NE7_ST3_T0)) {
- /* if at track 0, first seek inwards */
- /* seek some steps: */
- fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0);
- DELAY(300000); /* ...wait a moment... */
- fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
- }
- /* If we're at track 0 first seek inwards. */
- if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) {
- /* Seek some steps... */
- if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
- /* ...wait a moment... */
- DELAY(300000);
- /* make ctrlr happy: */
- fd_sense_int(fdc, 0, 0);
+ if ((flags & FD_NO_PROBE) == 0) {
+ /* If we're at track 0 first seek inwards. */
+ if ((fd_sense_drive_status(fdc, &st3) == 0) &&
+ (st3 & NE7_ST3_T0)) {
+ /* Seek some steps... */
+ if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
+ /* ...wait a moment... */
+ DELAY(300000);
+ /* make ctrlr happy: */
+ fd_sense_int(fdc, 0, 0);
+ }
}
- }
- for (i = 0; i < 2; i++) {
- /*
- * we must recalibrate twice, just in case the
- * heads have been beyond cylinder 76, since most
- * FDCs still barf when attempting to recalibrate
- * more than 77 steps
- */
- /* go back to 0: */
- if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
- /* a second being enough for full stroke seek*/
- DELAY(i == 0 ? 1000000 : 300000);
-
- /* anything responding? */
- if (fd_sense_int(fdc, &st0, 0) == 0 &&
- (st0 & NE7_ST0_EC) == 0)
- break; /* already probed succesfully */
+ for (i = 0; i < 2; i++) {
+ /*
+ * we must recalibrate twice, just in case the
+ * heads have been beyond cylinder 76, since
+ * most FDCs still barf when attempting to
+ * recalibrate more than 77 steps
+ */
+ /* go back to 0: */
+ if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
+ /* a second being enough for full stroke seek*/
+ DELAY(i == 0 ? 1000000 : 300000);
+
+ /* anything responding? */
+ if (fd_sense_int(fdc, &st0, 0) == 0 &&
+ (st0 & NE7_ST0_EC) == 0)
+ break; /* already probed succesfully */
+ }
}
}
set_motor(fdc, fdsu, TURNOFF);
- if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
+ if ((flags & FD_NO_PROBE) == 0 &&
+ (st0 & NE7_ST0_EC) != 0) /* no track 0 -> no drive present */
return (ENXIO);
- fd->track = FD_NO_TRACK;
- fd->fdc = fdc;
- fd->fdsu = fdsu;
- fd->options = 0;
- callout_handle_init(&fd->toffhandle);
- callout_handle_init(&fd->tohandle);
-
- switch (fdt) {
- case RTCFDT_12M:
+ switch (fd->type) {
+ case FDT_12M:
device_set_desc(dev, "1200-KB 5.25\" drive");
- fd->type = FD_1200;
+ fd->type = FDT_12M;
break;
- case RTCFDT_144M | RTCFDT_144M_PRETENDED:
- device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive");
- fdt = RTCFDT_144M;
- fd->type = FD_1440;
- case RTCFDT_144M:
+ case FDT_144M:
device_set_desc(dev, "1440-KB 3.5\" drive");
- fd->type = FD_1440;
+ fd->type = FDT_144M;
break;
- case RTCFDT_288M:
- case RTCFDT_288M_1:
+ case FDT_288M:
device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)");
- fd->type = FD_1440;
+ fd->type = FDT_288M;
break;
- case RTCFDT_360K:
+ case FDT_360K:
device_set_desc(dev, "360-KB 5.25\" drive");
- fd->type = FD_360;
+ fd->type = FDT_360K;
break;
- case RTCFDT_720K:
- printf("720-KB 3.5\" drive");
- fd->type = FD_720;
+ case FDT_720K:
+ device_set_desc(dev, "720-KB 3.5\" drive");
+ fd->type = FDT_720K;
break;
default:
return (ENXIO);
}
+ fd->track = FD_NO_TRACK;
+ fd->fdc = fdc;
+ fd->fdsu = fdsu;
+ fd->options = 0;
+ callout_handle_init(&fd->toffhandle);
+ callout_handle_init(&fd->tohandle);
+
+ /* initialize densities for subdevices */
+ for (i = 0; i < NUMDENS; i++)
+ memcpy(fd->fts + i, fd_native_types + fd->type,
+ sizeof(struct fd_type));
return (0);
}
@@ -1318,7 +1328,7 @@ fd_attach(device_t dev)
fd->clonetag = EVENTHANDLER_REGISTER(dev_clone, fd_clone, fd, 1000);
fd->masterdev = make_dev(&fd_cdevsw, fd->fdu << 6,
UID_ROOT, GID_OPERATOR, 0640, "fd%d", fd->fdu);
- for (i = 0; i < NCLONEDEVS; i++)
+ for (i = 0; i < NUMDENS - 1; i++)
fd->clonedevs[i] = NODEV;
devstat_add_entry(&fd->device_stats, device_get_name(dev),
device_get_unit(dev), 0, DEVSTAT_NO_ORDERED_TAGS,
@@ -1337,7 +1347,7 @@ fd_detach(device_t dev)
untimeout(fd_turnoff, fd, fd->toffhandle);
devstat_remove_entry(&fd->device_stats);
destroy_dev(fd->masterdev);
- for (i = 0; i < NCLONEDEVS; i++)
+ for (i = 0; i < NUMDENS - 1; i++)
if (fd->clonedevs[i] != NODEV)
destroy_dev(fd->clonedevs[i]);
cdevsw_remove(&fd_cdevsw);
@@ -1525,77 +1535,78 @@ Fdopen(dev_t dev, int flags, int mode, struct thread *td)
int type = FDTYPE(minor(dev));
fd_p fd;
fdc_p fdc;
+ int rv, unitattn, dflags;
- /* check bounds */
if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0)
return (ENXIO);
fdc = fd->fdc;
- if ((fdc == NULL) || (fd->type == NO_TYPE))
+ if ((fdc == NULL) || (fd->type == FDT_NONE))
return (ENXIO);
if (type > NUMDENS)
return (ENXIO);
- if (type == 0)
- type = fd->type;
- else {
- /*
- * For each type of basic drive, make sure we are trying
- * to open a type it can do,
- */
- if (type != fd->type) {
- switch (fd->type) {
- case FD_360:
- return (ENXIO);
- case FD_720:
- if ( type != FD_820
- && type != FD_800
- && type != FD_640
- )
- return (ENXIO);
- break;
- case FD_1200:
- switch (type) {
- case FD_1480:
- type = FD_1480in5_25;
- break;
- case FD_1440:
- type = FD_1440in5_25;
- break;
- case FD_1232:
- break;
- case FD_820:
- type = FD_820in5_25;
- break;
- case FD_800:
- type = FD_800in5_25;
- break;
- case FD_720:
- type = FD_720in5_25;
- break;
- case FD_640:
- type = FD_640in5_25;
- break;
- case FD_360:
- type = FD_360in5_25;
- break;
- default:
- return(ENXIO);
- }
- break;
- case FD_1440:
- if ( type != FD_1720
- && type != FD_1480
- && type != FD_1200
- && type != FD_820
- && type != FD_800
- && type != FD_720
- && type != FD_640
- )
- return(ENXIO);
- break;
+ dflags = device_get_flags(fd->dev);
+ /*
+ * This is a bit bogus. It's still possible that e. g. a
+ * descriptor gets inherited to a child, but then it's at
+ * least for the same subdevice. By checking FD_OPEN here, we
+ * can ensure that a device isn't attempted to be opened with
+ * different densities at the same time where the second open
+ * could clobber the settings from the first one.
+ */
+ if (fd->flags & FD_OPEN)
+ return (EBUSY);
+
+ if (type == 0) {
+ if (flags & FNONBLOCK) {
+ /*
+ * Unfortunately, physio(9) discards its ioflag
+ * argument, thus preventing us from seeing the
+ * IO_NDELAY bit. So we need to keep track
+ * ourselves.
+ */
+ fd->flags |= FD_NONBLOCK;
+ fd->ft = 0;
+ } else {
+ /*
+ * Figure out a unit attention condition.
+ *
+ * If UA has been forced, proceed.
+ *
+ * If motor is off, turn it on for a moment
+ * and select our drive, in order to read the
+ * UA hardware signal.
+ *
+ * If motor is on, and our drive is currently
+ * selected, just read the hardware bit.
+ *
+ * If motor is on, but active for another
+ * drive on that controller, we are lost. We
+ * cannot risk to deselect the other drive, so
+ * we just assume a forced UA condition to be
+ * on the safe side.
+ */
+ unitattn = 0;
+ if ((dflags & FD_NO_CHLINE) != 0 ||
+ (fd->flags & FD_UA) != 0) {
+ unitattn = 1;
+ fd->flags &= ~FD_UA;
+ } else if (fdc->fdout & (FDO_MOEN0 | FDO_MOEN1 |
+ FDO_MOEN2 | FDO_MOEN3)) {
+ if ((fdc->fdout & FDO_FDSEL) == fd->fdsu)
+ unitattn = fdin_rd(fdc) & FDI_DCHG;
+ else
+ unitattn = 1;
+ } else {
+ set_motor(fdc, fd->fdsu, TURNON);
+ unitattn = fdin_rd(fdc) & FDI_DCHG;
+ set_motor(fdc, fd->fdsu, TURNOFF);
}
+ if (unitattn && (rv = fdautoselect(dev)) != 0)
+ return (rv);
}
+ } else {
+ fd->ft = fd->fts + type;
}
- fd->ft = fd_types + type - 1;
fd->flags |= FD_OPEN;
/*
* Clearing the DMA overrun counter at open time is a bit messy.
@@ -1620,7 +1631,7 @@ fdclose(dev_t dev, int flags, int mode, struct thread *td)
struct fd_data *fd;
fd = devclass_get_softc(fd_devclass, fdu);
- fd->flags &= ~FD_OPEN;
+ fd->flags &= ~(FD_OPEN | FD_NONBLOCK);
fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR);
return (0);
@@ -1642,13 +1653,18 @@ fdstrategy(struct bio *bp)
panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)",
(u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev));
fdc = fd->fdc;
- if (fd->type == NO_TYPE) {
+ if (fd->type == FDT_NONE || fd->ft == 0) {
bp->bio_error = ENXIO;
bp->bio_flags |= BIO_ERROR;
goto bad;
}
fdblk = 128 << (fd->ft->secsize);
if (bp->bio_cmd != BIO_FORMAT && bp->bio_cmd != BIO_RDSECTID) {
+ if (fd->flags & FD_NONBLOCK) {
+ bp->bio_error = EAGAIN;
+ bp->bio_flags |= BIO_ERROR;
+ goto bad;
+ }
if (bp->bio_blkno < 0) {
printf(
"fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n",
@@ -1816,6 +1832,102 @@ fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count)
}
/*
+ * Try figuring out the density of the media present in our device.
+ */
+static int
+fdautoselect(dev_t dev)
+{
+ fdu_t fdu;
+ fd_p fd;
+ struct fd_type *fdtp;
+ struct fdc_readid id;
+ int i, n, oopts, rv;
+
+ fdu = FDUNIT(minor(dev));
+ fd = devclass_get_softc(fd_devclass, fdu);
+
+ switch (fd->type) {
+ default:
+ return (ENXIO);
+
+ case FDT_360K:
+ case FDT_720K:
+ /* no autoselection on those drives */
+ fd->ft = fd_native_types + fd->type;
+ return (0);
+
+ case FDT_12M:
+ fdtp = fd_searchlist_12m;
+ n = sizeof fd_searchlist_12m / sizeof(struct fd_type);
+ break;
+
+ case FDT_144M:
+ fdtp = fd_searchlist_144m;
+ n = sizeof fd_searchlist_144m / sizeof(struct fd_type);
+ break;
+
+ case FDT_288M:
+ fdtp = fd_searchlist_288m;
+ n = sizeof fd_searchlist_288m / sizeof(struct fd_type);
+ break;
+ }
+
+ /*
+ * Try reading sector ID fields, first at cylinder 0, head 0,
+ * then at cylinder 2, head N. We don't probe cylinder 1,
+ * since for 5.25in DD media in a HD drive, there are no data
+ * to read (2 step pulses per media cylinder required). For
+ * two-sided media, the second probe always goes to head 1, so
+ * we can tell them apart from single-sided media. As a
+ * side-effect this means that single-sided media should be
+ * mentioned in the search list after two-sided media of an
+ * otherwise identical density. Media with a different number
+ * of sectors per track but otherwise identical parameters
+ * cannot be distinguished at all.
+ *
+ * If we successfully read an ID field on both cylinders where
+ * the recorded values match our expectation, we are done.
+ * Otherwise, we try the next density entry from the table.
+ *
+ * Stepping to cylinder 2 has the side-effect of clearing the
+ * unit attention bit.
+ */
+ oopts = fd->options;
+ fd->options |= FDOPT_NOERRLOG | FDOPT_NORETRY;
+ for (i = 0; i < n; i++, fdtp++) {
+ fd->ft = fdtp;
+
+ id.cyl = id.head = 0;
+ rv = fdmisccmd(dev, BIO_RDSECTID, &id);
+ if (rv != 0)
+ continue;
+ if (id.cyl != 0 || id.head != 0 ||
+ id.secshift != fdtp->secsize)
+ continue;
+ id.cyl = 2;
+ id.head = fd->ft->heads - 1;
+ rv = fdmisccmd(dev, BIO_RDSECTID, &id);
+ if (id.cyl != 2 || id.head != fdtp->heads - 1 ||
+ id.secshift != fdtp->secsize)
+ continue;
+ if (rv == 0)
+ break;
+ }
+
+ fd->options = oopts;
+ if (i == n) {
+ device_printf(fd->dev, "autoselection failed\n");
+ fd->ft = 0;
+ return (EIO);
+ } else {
+ device_printf(fd->dev, "autoselected %d KB medium\n",
+ fd->ft->size / 2);
+ return (0);
+ }
+}
+
+
+/*
* The controller state machine.
*
* If it returns a non zero value, it should be called again immediately.
@@ -1825,7 +1937,7 @@ fdstate(fdc_p fdc)
{
struct fdc_readid *idp;
int read, format, rdsectid, cylinder, head, i, sec = 0, sectrac;
- int st0, cyl, st3, idf;
+ int st0, cyl, st3, idf, ne7cmd, mfm, steptrac;
unsigned long blknum;
fdu_t fdu = fdc->fdu;
fd_p fd;
@@ -1862,6 +1974,8 @@ fdstate(fdc_p fdc)
if (fdc->fd && (fd != fdc->fd))
device_printf(fd->dev, "confused fd pointers\n");
read = bp->bio_cmd == BIO_READ;
+ mfm = (fd->ft->flags & FL_MFM)? NE7CMD_MFM: 0;
+ steptrac = (fd->ft->flags & FL_2STEP)? 2: 1;
if (read)
idf = ISADMA_READ;
else
@@ -1922,8 +2036,7 @@ fdstate(fdc_p fdc)
return (1); /* will return immediately */
}
if (fd_cmd(fdc, 3, NE7CMD_SEEK,
- fd->fdsu, cylinder * fd->ft->steptrac,
- 0))
+ fd->fdsu, cylinder * steptrac, 0))
{
/*
* Seek command not accepted, looks like
@@ -1948,7 +2061,7 @@ fdstate(fdc_p fdc)
/* Make sure seek really happened. */
if(fd->track == FD_NO_TRACK) {
- int descyl = cylinder * fd->ft->steptrac;
+ int descyl = cylinder * steptrac;
do {
/*
* This might be a "ready changed" interrupt,
@@ -2023,6 +2136,8 @@ fdstate(fdc_p fdc)
sec = blknum % (sectrac * fd->ft->heads);
head = sec / sectrac;
sec = sec % sectrac + 1;
+ if (head != 0 && fd->ft->offset_side2 != 0)
+ sec += fd->ft->offset_side2;
fd->hddrv = ((head&1)<<2)+fdu;
if(format || !(read || rdsectid))
@@ -2061,6 +2176,7 @@ fdstate(fdc_p fdc)
}
if (format) {
+ ne7cmd = NE7CMD_FORMAT | mfm;
if (fdc->flags & FDC_NODMA) {
/*
* This seems to be necessary for
@@ -2082,7 +2198,7 @@ fdstate(fdc_p fdc)
}
/* formatting */
- if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu,
+ if(fd_cmd(fdc, 6, ne7cmd, head << 2 | fdu,
finfo->fd_formb_secshift,
finfo->fd_formb_nsecs,
finfo->fd_formb_gaplen,
@@ -2097,13 +2213,15 @@ fdstate(fdc_p fdc)
return (retrier(fdc));
}
} else if (rdsectid) {
- if (fd_cmd(fdc, 2, NE7CMD_READID, head << 2 | fdu, 0)) {
+ ne7cmd = NE7CMD_READID | mfm;
+ if (fd_cmd(fdc, 2, ne7cmd, head << 2 | fdu, 0)) {
/* controller jamming */
fdc->retry = 6;
return (retrier(fdc));
}
} else {
/* read or write operation */
+ ne7cmd = (read ? NE7CMD_READ | NE7CMD_SK : NE7CMD_WRITE) | mfm;
if (fdc->flags & FDC_NODMA) {
/*
* This seems to be necessary even when
@@ -2121,7 +2239,7 @@ fdstate(fdc_p fdc)
fdblk);
}
if (fd_cmd(fdc, 9,
- (read ? NE7CMD_READ : NE7CMD_WRITE),
+ ne7cmd,
head << 2 | fdu, /* head & unit */
fd->track, /* track */
head,
@@ -2482,12 +2600,111 @@ fdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
struct fdc_status *fsp;
struct fdc_readid *rid;
size_t fdblk;
- int error;
+ int error, type;
fdu = FDUNIT(minor(dev));
+ type = FDTYPE(minor(dev));
fd = devclass_get_softc(fd_devclass, fdu);
+ /*
+ * First, handle everything that could be done with
+ * FD_NONBLOCK still being set.
+ */
+ switch (cmd) {
+ case FIONBIO:
+ if (*(int *)addr != 0)
+ fd->flags |= FD_NONBLOCK;
+ else {
+ if (fd->ft == 0) {
+ /*
+ * No drive type has been selected yet,
+ * cannot turn FNONBLOCK off.
+ */
+ return (EINVAL);
+ }
+ fd->flags &= ~FD_NONBLOCK;
+ }
+ return (0);
+
+ case FIOASYNC:
+ /* keep the generic fcntl() code happy */
+ return (0);
+ case FD_GTYPE: /* get drive type */
+ if (fd->ft == 0)
+ /* no type known yet, return the native type */
+ *(struct fd_type *)addr = fd_native_types[fd->type];
+ else
+ *(struct fd_type *)addr = *fd->ft;
+ return (0);
+
+ case FD_STYPE: /* set drive type */
+ if (type == 0) {
+ /*
+ * Allow setting drive type temporarily iff
+ * currently unset. Used for fdformat so any
+ * user can set it, and then start formatting.
+ */
+ if (fd->ft)
+ return (EINVAL); /* already set */
+ fd->ft = fd->fts;
+ *fd->ft = *(struct fd_type *)addr;
+ fd->flags |= FD_UA;
+ } else {
+ /*
+ * Set density definition permanently. Only
+ * allow for superuser.
+ */
+ if (suser_td(td) != 0)
+ return (EPERM);
+ fd->fts[type] = *(struct fd_type *)addr;
+ }
+ return (0);
+
+ case FD_GOPTS: /* get drive options */
+ *(int *)addr = fd->options + (type == 0? FDOPT_AUTOSEL: 0);
+ return (0);
+
+ case FD_SOPTS: /* set drive options */
+ fd->options = *(int *)addr & ~FDOPT_AUTOSEL;
+ return (0);
+
+#ifdef FDC_DEBUG
+ case FD_DEBUG:
+ if ((fd_debug != 0) != (*(int *)addr != 0)) {
+ fd_debug = (*(int *)addr != 0);
+ printf("fd%d: debugging turned %s\n",
+ fd->fdu, fd_debug ? "on" : "off");
+ }
+ return (0);
+#endif
+
+ case FD_CLRERR:
+ if (suser_td(td) != 0)
+ return (EPERM);
+ fd->fdc->fdc_errs = 0;
+ return (0);
+
+ case FD_GSTAT:
+ fsp = (struct fdc_status *)addr;
+ if ((fd->fdc->flags & FDC_STAT_VALID) == 0)
+ return (EINVAL);
+ memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int));
+ return (0);
+
+ case FD_GDTYPE:
+ *(enum fd_drivetype *)addr = fd->type;
+ return (0);
+ }
+
+ /*
+ * Now handle everything else. Make sure we have a valid
+ * drive type.
+ */
+ if (fd->flags & FD_NONBLOCK)
+ return (EAGAIN);
+ if (fd->ft == 0)
+ return (ENXIO);
fdblk = 128 << fd->ft->secsize;
error = 0;
diff --git a/sys/dev/fdc/fdcreg.h b/sys/dev/fdc/fdcreg.h
index 5dee70d..c082668 100644
--- a/sys/dev/fdc/fdcreg.h
+++ b/sys/dev/fdc/fdcreg.h
@@ -55,14 +55,11 @@
#define FDDATA 5 /* NEC 765 Data Register (R/W) */
#define FDCTL 7 /* Control Register (W) */
-#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 */
-
+/*
+ * The definitions for FDC_500KBPS etc. have been moved out to <sys/fdcio.h>
+ * since they need to be visible in userland. They cover the lower two bits
+ * of FDCTL when used for output.
+ */
/*
* this is the secret PIO data port (offset from base)
*/
diff --git a/sys/isa/fd.c b/sys/isa/fd.c
index 845f520..04be1bd 100644
--- a/sys/isa/fd.c
+++ b/sys/isa/fd.c
@@ -65,6 +65,7 @@
#include <sys/disklabel.h>
#include <sys/fcntl.h>
#include <sys/fdcio.h>
+#include <sys/filio.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
@@ -87,7 +88,7 @@
enum fdc_type
{
- FDC_NE765, FDC_I82077, FDC_NE72065, FDC_UNKNOWN = -1
+ FDC_NE765, FDC_ENHANCED, FDC_UNKNOWN = -1
};
enum fdc_states {
@@ -189,6 +190,7 @@ typedef struct fdc_data *fdc_p;
typedef enum fdc_type fdc_t;
#define FDUNIT(s) (((s) >> 6) & 3)
+#define FDNUMTOUNIT(n) (((n) & 3) << 6)
#define FDTYPE(s) ((s) & 0x3f)
/*
@@ -210,13 +212,9 @@ static __inline T fdc_get_ ## A(device_t dev) \
}
FDC_ACCESSOR(fdunit, FDUNIT, int)
-/* configuration flags */
-#define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */
+/* configuration flags for fdc */
#define FDC_NO_FIFO (1 << 2) /* do not enable FIFO */
-/* internally used only, not really from CMOS: */
-#define RTCFDT_144M_PRETENDED 0x1000
-
/* error returns for fd_cmd() */
#define FD_FAILED -1
#define FD_NOT_VALID -2
@@ -229,54 +227,65 @@ FDC_ACCESSOR(fdunit, FDUNIT, int)
*/
#define FDC_DMAOV_MAX 25
-#define NUMTYPES 17
-#define NUMDENS (NUMTYPES - 7)
-
-#define NO_TYPE 0
-#define FD_1720 1
-#define FD_1480 2
-#define FD_1440 3
-#define FD_1200 4
-#define FD_820 5
-#define FD_800 6
-#define FD_720 7
-#define FD_360 8
-#define FD_640 9
-#define FD_1232 10
-
-#define FD_1480in5_25 11
-#define FD_1440in5_25 12
-#define FD_820in5_25 13
-#define FD_800in5_25 14
-#define FD_720in5_25 15
-#define FD_360in5_25 16
-#define FD_640in5_25 17
+/*
+ * Number of subdevices that can be used for different density types.
+ * By now, the lower 6 bit of the minor number are reserved for this,
+ * allowing for up to 64 subdevices, but we only use 16 out of this.
+ * Density #0 is used for automatic format detection, the other
+ * densities are available as programmable densities (for assignment
+ * by fdcontrol(8)).
+ * The upper 2 bits of the minor number are reserved for the subunit
+ * (drive #) per controller.
+ */
+#define NUMDENS 16
#define BIO_RDSECTID BIO_CMD1
-static struct fd_type fd_types[NUMTYPES] =
+/*
+ * List of native drive densities. Order must match enum fd_drivetype
+ * in <sys/fdcio.h>. Upon attaching the drive, each of the
+ * programmable subdevices is initialized with the native density
+ * definition.
+ */
+static struct fd_type fd_native_types[] =
{
-{ 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */
-{ 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */
-{ 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */
-{ 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */
-{ 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */
-{ 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */
-{ 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */
-{ 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */
-{ 8,2,0xFF,0x2A,80,1280,1,FDC_250KBPS,2,0x50,1 }, /* 640K in DD 5.25in */
-{ 8,3,0xFF,0x35,77,1232,1,FDC_500KBPS,2,0x74,1 }, /* 1.23M in HD 5.25in */
-
-{ 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */
-{ 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */
-{ 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */
-{ 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */
-{ 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */
-{ 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */
-{ 8,2,0xFF,0x2A,80,1280,1,FDC_300KBPS,2,0x50,1 }, /* 640K in HD 5.25in */
+{ 0 }, /* FDT_NONE */
+{ 9,2,0xFF,0x2A,40, 720,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* FDT_360K */
+{ 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* FDT_12M */
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* FDT_720K */
+{ 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* FDT_144M */
+#if 0 /* we currently don't handle 2.88 MB */
+{ 36,2,0xFF,0x1B,80,5760,FDC_1MBPS, 2,0x4C,1,1,FL_MFM|FL_PERPND } /*FDT_288M*/
+#else
+{ 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* FDT_144M */
+#endif
+};
+
+/*
+ * 360 KB 5.25" and 720 KB 3.5" drives don't have automatic density
+ * selection, they just start out with their native density (or lose).
+ * So 1.2 MB 5.25", 1.44 MB 3.5", and 2.88 MB 3.5" drives have their
+ * respective lists of densities to search for.
+ */
+static struct fd_type fd_searchlist_12m[] = {
+{ 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* 1.2M */
+{ 9,2,0xFF,0x23,40, 720,FDC_300KBPS,2,0x50,1,0,FL_MFM|FL_2STEP }, /* 360K */
+{ 9,2,0xFF,0x20,80,1440,FDC_300KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+};
+
+static struct fd_type fd_searchlist_144m[] = {
+{ 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.44M */
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
};
-#define DRVS_PER_CTLR 2 /* 2 floppies */
+/* We search for 1.44M first since this is the most common case. */
+static struct fd_type fd_searchlist_288m[] = {
+{ 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.44M */
+#if 0
+{ 36,2,0xFF,0x1B,80,5760,FDC_1MBPS, 2,0x4C,1,1,FL_MFM|FL_PERPND } /* 2.88M */
+#endif
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+};
#define MAX_SEC_SIZE (128 << 3)
#define MAX_CYLINDER 85 /* some people really stress their drives
@@ -291,25 +300,27 @@ static devclass_t fdc_devclass;
struct fd_data {
struct fdc_data *fdc; /* pointer to controller structure */
int fdsu; /* this units number on this controller */
- int type; /* Drive type (FD_1440...) */
- struct fd_type *ft; /* pointer to the type descriptor */
+ enum fd_drivetype type; /* drive type */
+ struct fd_type *ft; /* pointer to current type descriptor */
+ struct fd_type fts[NUMDENS]; /* type descriptors */
int flags;
#define FD_OPEN 0x01 /* it's open */
-#define FD_ACTIVE 0x02 /* it's active */
-#define FD_MOTOR 0x04 /* motor should be on */
-#define FD_MOTOR_WAIT 0x08 /* motor coming up */
+#define FD_NONBLOCK 0x02 /* O_NONBLOCK set */
+#define FD_ACTIVE 0x04 /* it's active */
+#define FD_MOTOR 0x08 /* motor should be on */
+#define FD_MOTOR_WAIT 0x10 /* motor coming up */
+#define FD_UA 0x20 /* force unit attention */
int skip;
int hddrv;
#define FD_NO_TRACK -2
int track; /* where we think the head is */
- int options; /* user configurable options, see ioctl_fd.h */
+ int options; /* user configurable options, see fdcio.h */
struct callout_handle toffhandle;
struct callout_handle tohandle;
struct devstat device_stats;
eventhandler_tag clonetag;
dev_t masterdev;
-#define NCLONEDEVS 10 /* must match the table below */
- dev_t clonedevs[NCLONEDEVS];
+ dev_t clonedevs[NUMDENS - 1];
device_t dev;
fdu_t fdu;
};
@@ -319,6 +330,17 @@ struct fdc_ivars {
};
static devclass_t fd_devclass;
+/* configuration flags for fd */
+#define FD_TYPEMASK 0x0f /* drive type, matches enum
+ * fd_drivetype; on i386 machines, if
+ * given as 0, use RTC type for fd0
+ * and fd1 */
+#define FD_DTYPE(flags) ((flags) & FD_TYPEMASK)
+#define FD_NO_CHLINE 0x10 /* drive does not support changeline
+ * aka. unit attention */
+#define FD_NO_PROBE 0x20 /* don't probe drive (seek test), just
+ * assume it is there */
+
/*
* Throughout this file the following conventions will be used:
*
@@ -391,6 +413,7 @@ static timeout_t fd_iotimeout;
static timeout_t fd_pseudointr;
static driver_intr_t fdc_intr;
static int fdcpio(fdc_p, long, caddr_t, u_int);
+static int fdautoselect(dev_t);
static int fdstate(struct fdc_data *);
static int retrier(struct fdc_data *);
static void fdbiodone(struct bio *);
@@ -450,16 +473,12 @@ fdctl_wr_pcmcia(fdc_p fdc, u_int8_t v)
}
#endif
-#if 0
-
static u_int8_t
fdin_rd(fdc_p fdc)
{
return bus_space_read_1(fdc->portt, fdc->porth, FDIN);
}
-#endif
-
#define CDEV_MAJOR 9
static struct cdevsw fd_cdevsw = {
/* open */ Fdopen,
@@ -861,13 +880,11 @@ fdc_probe(device_t dev)
device_set_desc(dev, "NEC 765 or clone");
fdc->fdct = FDC_NE765;
break;
- case 0x81:
- device_set_desc(dev, "Intel 82077 or clone");
- fdc->fdct = FDC_I82077;
- break;
+ case 0x81: /* not mentioned in any hardware doc */
case 0x90:
- device_set_desc(dev, "NEC 72065B or clone");
- fdc->fdct = FDC_NE72065;
+ device_set_desc(dev,
+ "enhanced floppy controller (i82077, NE72065 or clone)");
+ fdc->fdct = FDC_ENHANCED;
break;
default:
device_set_desc(dev, "generic floppy controller");
@@ -960,7 +977,7 @@ fdc_detach(device_t dev)
static void
fdc_add_child(device_t dev, const char *name, int unit)
{
- int disabled;
+ int disabled, flags;
struct fdc_ivars *ivar;
device_t child;
@@ -973,6 +990,8 @@ fdc_add_child(device_t dev, const char *name, int unit)
if (child == NULL)
return;
device_set_ivars(child, ivar);
+ if (resource_int_value(name, unit, "flags", &flags) == 0)
+ device_set_flags(child, flags);
if (resource_int_value(name, unit, "disabled", &disabled) == 0
&& disabled != 0)
device_disable(child);
@@ -1034,11 +1053,14 @@ fdc_attach(device_t dev)
static int
fdc_print_child(device_t me, device_t child)
{
- int retval = 0;
+ int retval = 0, flags;
retval += bus_print_child_header(me, child);
- retval += printf(" on %s drive %d\n", device_get_nameunit(me),
+ retval += printf(" on %s drive %d", device_get_nameunit(me),
fdc_get_fdunit(child));
+ if ((flags = device_get_flags(me)) != 0)
+ retval += printf(" flags %#x", flags);
+ retval += printf("\n");
return (retval);
}
@@ -1098,62 +1120,54 @@ DRIVER_MODULE(fdc, pccard, fdc_pccard_driver, fdc_devclass, 0, 0);
#endif /* NCARD > 0 */
-static struct {
- char *match;
- int minor;
- int link;
-} fd_suffix[] = {
- /*
- * Genuine clone devices must come first, and their number must
- * match NCLONEDEVS above.
- */
- { ".1720", 1, 0 },
- { ".1480", 2, 0 },
- { ".1440", 3, 0 },
- { ".1200", 4, 0 },
- { ".820", 5, 0 },
- { ".800", 6, 0 },
- { ".720", 7, 0 },
- { ".360", 8, 0 },
- { ".640", 9, 0 },
- { ".1232", 10, 0 },
- { "a", 0, 1 },
- { "b", 0, 1 },
- { "c", 0, 1 },
- { "d", 0, 1 },
- { "e", 0, 1 },
- { "f", 0, 1 },
- { "g", 0, 1 },
- { "h", 0, 1 },
- { 0, 0 }
-};
-
+/*
+ * Create a clone device upon request by devfs.
+ */
static void
fd_clone(void *arg, char *name, int namelen, dev_t *dev)
{
struct fd_data *fd;
- int u, d, i;
+ int i, u;
char *n;
+ size_t l;
fd = (struct fd_data *)arg;
if (*dev != NODEV)
return;
if (dev_stdclone(name, &n, "fd", &u) != 2)
return;
- for (i = 0; ; i++) {
- if (fd_suffix[i].match == NULL)
- return;
- if (strcmp(n, fd_suffix[i].match))
- continue;
- d = fd_suffix[i].minor;
- break;
- }
- if (fd_suffix[i].link == 0) {
- *dev = make_dev(&fd_cdevsw, (u << 6) + d,
- UID_ROOT, GID_OPERATOR, 0640, name);
- fd->clonedevs[i] = *dev;
- } else {
+ l = strlen(n);
+ if (l == 1 && *n >= 'a' && *n <= 'h') {
+ /*
+ * Trailing letters a through h denote
+ * pseudo-partitions. We don't support true
+ * (UFS-style) partitions, so we just implement them
+ * as symlinks if someone asks us nicely.
+ */
*dev = make_dev_alias(fd->masterdev, name);
+ return;
+ }
+ if (l >= 2 && l <= 5 && *n == '.') {
+ /*
+ * Trailing numbers, preceded by a dot, denote
+ * subdevices for different densities. Historically,
+ * they have been named by density (like fd0.1440),
+ * but we allow arbitrary numbers between 1 and 4
+ * digits, so fd0.1 through fd0.15 are possible as
+ * well.
+ */
+ for (i = 1; i < l; i++)
+ if (n[i] < '0' || n[i] > '9')
+ return;
+ for (i = 0; i < NUMDENS - 1; i++)
+ if (fd->clonedevs[i] == NODEV) {
+ *dev = make_dev(&fd_cdevsw,
+ FDNUMTOUNIT(u) + i + 1,
+ UID_ROOT, GID_OPERATOR, 0640,
+ name);
+ fd->clonedevs[i] = *dev;
+ return;
+ }
}
}
@@ -1164,46 +1178,48 @@ static int
fd_probe(device_t dev)
{
int i;
- u_int fdt, st0, st3;
+ u_int st0, st3;
struct fd_data *fd;
struct fdc_data *fdc;
fdsu_t fdsu;
- static int fd_fifo = 0;
+ int flags;
fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */
fd = device_get_softc(dev);
fdc = device_get_softc(device_get_parent(dev));
+ flags = device_get_flags(dev);
bzero(fd, sizeof *fd);
fd->dev = dev;
fd->fdc = fdc;
fd->fdsu = fdsu;
fd->fdu = device_get_unit(dev);
-
-#ifdef __i386__
- /* look up what bios thinks we have */
- switch (fd->fdu) {
- case 0:
- if ((fdc->flags & FDC_ISPCMCIA))
- fdt = RTCFDT_144M;
- else if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0)
- fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED;
- else
- fdt = (rtcin(RTC_FDISKETTE) & 0xf0);
- break;
- case 1:
- fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0);
- break;
- default:
- fdt = RTCFDT_NONE;
- break;
+ fd->flags = FD_UA; /* make sure fdautoselect() will be called */
+
+ fd->type = FD_DTYPE(flags);
+#if _MACHINE_ARCH == i386
+ if (fd->type == FDT_NONE && (fd->fdu == 0 || fd->fdu == 1)) {
+ /* Look up what the BIOS thinks we have. */
+ if (fd->fdu == 0) {
+ if ((fdc->flags & FDC_ISPCMCIA))
+ /*
+ * Somewhat special. No need to force the
+ * user to set device flags, since the Y-E
+ * Data PCMCIA floppy is always a 1.44 MB
+ * device.
+ */
+ fd->type = FDT_144M;
+ else
+ fd->type = (rtcin(RTC_FDISKETTE) & 0xf0) >> 4;
+ } else {
+ fd->type = rtcin(RTC_FDISKETTE) & 0x0f;
+ }
+ if (fd->type == FDT_288M_1)
+ fd->type = FDT_288M;
}
-#else
- fdt = RTCFDT_144M; /* XXX probably */
-#endif
-
+#endif /* _MACHINE_ARCH == i386 */
/* is there a unit? */
- if (fdt == RTCFDT_NONE)
+ if (fd->type == FDT_NONE)
return (ENXIO);
/* select it */
@@ -1212,94 +1228,88 @@ fd_probe(device_t dev)
DELAY(1000000); /* 1 sec */
/* XXX This doesn't work before the first set_motor() */
- if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN
- && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0
- && enable_fifo(fdc) == 0) {
+ if ((fdc->flags & FDC_HAS_FIFO) == 0 &&
+ fdc->fdct == FDC_ENHANCED &&
+ (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0 &&
+ enable_fifo(fdc) == 0) {
device_printf(device_get_parent(dev),
"FIFO enabled, %d bytes threshold\n", fifo_threshold);
}
- fd_fifo = 1;
-
- if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0)
- && (st3 & NE7_ST3_T0)) {
- /* if at track 0, first seek inwards */
- /* seek some steps: */
- fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0);
- DELAY(300000); /* ...wait a moment... */
- fd_sense_int(fdc, 0, 0); /* make ctrlr happy */
- }
- /* If we're at track 0 first seek inwards. */
- if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) {
- /* Seek some steps... */
- if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
- /* ...wait a moment... */
- DELAY(300000);
- /* make ctrlr happy: */
- fd_sense_int(fdc, 0, 0);
+ if ((flags & FD_NO_PROBE) == 0) {
+ /* If we're at track 0 first seek inwards. */
+ if ((fd_sense_drive_status(fdc, &st3) == 0) &&
+ (st3 & NE7_ST3_T0)) {
+ /* Seek some steps... */
+ if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) {
+ /* ...wait a moment... */
+ DELAY(300000);
+ /* make ctrlr happy: */
+ fd_sense_int(fdc, 0, 0);
+ }
}
- }
- for (i = 0; i < 2; i++) {
- /*
- * we must recalibrate twice, just in case the
- * heads have been beyond cylinder 76, since most
- * FDCs still barf when attempting to recalibrate
- * more than 77 steps
- */
- /* go back to 0: */
- if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
- /* a second being enough for full stroke seek*/
- DELAY(i == 0 ? 1000000 : 300000);
-
- /* anything responding? */
- if (fd_sense_int(fdc, &st0, 0) == 0 &&
- (st0 & NE7_ST0_EC) == 0)
- break; /* already probed succesfully */
+ for (i = 0; i < 2; i++) {
+ /*
+ * we must recalibrate twice, just in case the
+ * heads have been beyond cylinder 76, since
+ * most FDCs still barf when attempting to
+ * recalibrate more than 77 steps
+ */
+ /* go back to 0: */
+ if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) {
+ /* a second being enough for full stroke seek*/
+ DELAY(i == 0 ? 1000000 : 300000);
+
+ /* anything responding? */
+ if (fd_sense_int(fdc, &st0, 0) == 0 &&
+ (st0 & NE7_ST0_EC) == 0)
+ break; /* already probed succesfully */
+ }
}
}
set_motor(fdc, fdsu, TURNOFF);
- if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */
+ if ((flags & FD_NO_PROBE) == 0 &&
+ (st0 & NE7_ST0_EC) != 0) /* no track 0 -> no drive present */
return (ENXIO);
- fd->track = FD_NO_TRACK;
- fd->fdc = fdc;
- fd->fdsu = fdsu;
- fd->options = 0;
- callout_handle_init(&fd->toffhandle);
- callout_handle_init(&fd->tohandle);
-
- switch (fdt) {
- case RTCFDT_12M:
+ switch (fd->type) {
+ case FDT_12M:
device_set_desc(dev, "1200-KB 5.25\" drive");
- fd->type = FD_1200;
+ fd->type = FDT_12M;
break;
- case RTCFDT_144M | RTCFDT_144M_PRETENDED:
- device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive");
- fdt = RTCFDT_144M;
- fd->type = FD_1440;
- case RTCFDT_144M:
+ case FDT_144M:
device_set_desc(dev, "1440-KB 3.5\" drive");
- fd->type = FD_1440;
+ fd->type = FDT_144M;
break;
- case RTCFDT_288M:
- case RTCFDT_288M_1:
+ case FDT_288M:
device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)");
- fd->type = FD_1440;
+ fd->type = FDT_288M;
break;
- case RTCFDT_360K:
+ case FDT_360K:
device_set_desc(dev, "360-KB 5.25\" drive");
- fd->type = FD_360;
+ fd->type = FDT_360K;
break;
- case RTCFDT_720K:
- printf("720-KB 3.5\" drive");
- fd->type = FD_720;
+ case FDT_720K:
+ device_set_desc(dev, "720-KB 3.5\" drive");
+ fd->type = FDT_720K;
break;
default:
return (ENXIO);
}
+ fd->track = FD_NO_TRACK;
+ fd->fdc = fdc;
+ fd->fdsu = fdsu;
+ fd->options = 0;
+ callout_handle_init(&fd->toffhandle);
+ callout_handle_init(&fd->tohandle);
+
+ /* initialize densities for subdevices */
+ for (i = 0; i < NUMDENS; i++)
+ memcpy(fd->fts + i, fd_native_types + fd->type,
+ sizeof(struct fd_type));
return (0);
}
@@ -1318,7 +1328,7 @@ fd_attach(device_t dev)
fd->clonetag = EVENTHANDLER_REGISTER(dev_clone, fd_clone, fd, 1000);
fd->masterdev = make_dev(&fd_cdevsw, fd->fdu << 6,
UID_ROOT, GID_OPERATOR, 0640, "fd%d", fd->fdu);
- for (i = 0; i < NCLONEDEVS; i++)
+ for (i = 0; i < NUMDENS - 1; i++)
fd->clonedevs[i] = NODEV;
devstat_add_entry(&fd->device_stats, device_get_name(dev),
device_get_unit(dev), 0, DEVSTAT_NO_ORDERED_TAGS,
@@ -1337,7 +1347,7 @@ fd_detach(device_t dev)
untimeout(fd_turnoff, fd, fd->toffhandle);
devstat_remove_entry(&fd->device_stats);
destroy_dev(fd->masterdev);
- for (i = 0; i < NCLONEDEVS; i++)
+ for (i = 0; i < NUMDENS - 1; i++)
if (fd->clonedevs[i] != NODEV)
destroy_dev(fd->clonedevs[i]);
cdevsw_remove(&fd_cdevsw);
@@ -1525,77 +1535,78 @@ Fdopen(dev_t dev, int flags, int mode, struct thread *td)
int type = FDTYPE(minor(dev));
fd_p fd;
fdc_p fdc;
+ int rv, unitattn, dflags;
- /* check bounds */
if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0)
return (ENXIO);
fdc = fd->fdc;
- if ((fdc == NULL) || (fd->type == NO_TYPE))
+ if ((fdc == NULL) || (fd->type == FDT_NONE))
return (ENXIO);
if (type > NUMDENS)
return (ENXIO);
- if (type == 0)
- type = fd->type;
- else {
- /*
- * For each type of basic drive, make sure we are trying
- * to open a type it can do,
- */
- if (type != fd->type) {
- switch (fd->type) {
- case FD_360:
- return (ENXIO);
- case FD_720:
- if ( type != FD_820
- && type != FD_800
- && type != FD_640
- )
- return (ENXIO);
- break;
- case FD_1200:
- switch (type) {
- case FD_1480:
- type = FD_1480in5_25;
- break;
- case FD_1440:
- type = FD_1440in5_25;
- break;
- case FD_1232:
- break;
- case FD_820:
- type = FD_820in5_25;
- break;
- case FD_800:
- type = FD_800in5_25;
- break;
- case FD_720:
- type = FD_720in5_25;
- break;
- case FD_640:
- type = FD_640in5_25;
- break;
- case FD_360:
- type = FD_360in5_25;
- break;
- default:
- return(ENXIO);
- }
- break;
- case FD_1440:
- if ( type != FD_1720
- && type != FD_1480
- && type != FD_1200
- && type != FD_820
- && type != FD_800
- && type != FD_720
- && type != FD_640
- )
- return(ENXIO);
- break;
+ dflags = device_get_flags(fd->dev);
+ /*
+ * This is a bit bogus. It's still possible that e. g. a
+ * descriptor gets inherited to a child, but then it's at
+ * least for the same subdevice. By checking FD_OPEN here, we
+ * can ensure that a device isn't attempted to be opened with
+ * different densities at the same time where the second open
+ * could clobber the settings from the first one.
+ */
+ if (fd->flags & FD_OPEN)
+ return (EBUSY);
+
+ if (type == 0) {
+ if (flags & FNONBLOCK) {
+ /*
+ * Unfortunately, physio(9) discards its ioflag
+ * argument, thus preventing us from seeing the
+ * IO_NDELAY bit. So we need to keep track
+ * ourselves.
+ */
+ fd->flags |= FD_NONBLOCK;
+ fd->ft = 0;
+ } else {
+ /*
+ * Figure out a unit attention condition.
+ *
+ * If UA has been forced, proceed.
+ *
+ * If motor is off, turn it on for a moment
+ * and select our drive, in order to read the
+ * UA hardware signal.
+ *
+ * If motor is on, and our drive is currently
+ * selected, just read the hardware bit.
+ *
+ * If motor is on, but active for another
+ * drive on that controller, we are lost. We
+ * cannot risk to deselect the other drive, so
+ * we just assume a forced UA condition to be
+ * on the safe side.
+ */
+ unitattn = 0;
+ if ((dflags & FD_NO_CHLINE) != 0 ||
+ (fd->flags & FD_UA) != 0) {
+ unitattn = 1;
+ fd->flags &= ~FD_UA;
+ } else if (fdc->fdout & (FDO_MOEN0 | FDO_MOEN1 |
+ FDO_MOEN2 | FDO_MOEN3)) {
+ if ((fdc->fdout & FDO_FDSEL) == fd->fdsu)
+ unitattn = fdin_rd(fdc) & FDI_DCHG;
+ else
+ unitattn = 1;
+ } else {
+ set_motor(fdc, fd->fdsu, TURNON);
+ unitattn = fdin_rd(fdc) & FDI_DCHG;
+ set_motor(fdc, fd->fdsu, TURNOFF);
}
+ if (unitattn && (rv = fdautoselect(dev)) != 0)
+ return (rv);
}
+ } else {
+ fd->ft = fd->fts + type;
}
- fd->ft = fd_types + type - 1;
fd->flags |= FD_OPEN;
/*
* Clearing the DMA overrun counter at open time is a bit messy.
@@ -1620,7 +1631,7 @@ fdclose(dev_t dev, int flags, int mode, struct thread *td)
struct fd_data *fd;
fd = devclass_get_softc(fd_devclass, fdu);
- fd->flags &= ~FD_OPEN;
+ fd->flags &= ~(FD_OPEN | FD_NONBLOCK);
fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR);
return (0);
@@ -1642,13 +1653,18 @@ fdstrategy(struct bio *bp)
panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)",
(u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev));
fdc = fd->fdc;
- if (fd->type == NO_TYPE) {
+ if (fd->type == FDT_NONE || fd->ft == 0) {
bp->bio_error = ENXIO;
bp->bio_flags |= BIO_ERROR;
goto bad;
}
fdblk = 128 << (fd->ft->secsize);
if (bp->bio_cmd != BIO_FORMAT && bp->bio_cmd != BIO_RDSECTID) {
+ if (fd->flags & FD_NONBLOCK) {
+ bp->bio_error = EAGAIN;
+ bp->bio_flags |= BIO_ERROR;
+ goto bad;
+ }
if (bp->bio_blkno < 0) {
printf(
"fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n",
@@ -1816,6 +1832,102 @@ fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count)
}
/*
+ * Try figuring out the density of the media present in our device.
+ */
+static int
+fdautoselect(dev_t dev)
+{
+ fdu_t fdu;
+ fd_p fd;
+ struct fd_type *fdtp;
+ struct fdc_readid id;
+ int i, n, oopts, rv;
+
+ fdu = FDUNIT(minor(dev));
+ fd = devclass_get_softc(fd_devclass, fdu);
+
+ switch (fd->type) {
+ default:
+ return (ENXIO);
+
+ case FDT_360K:
+ case FDT_720K:
+ /* no autoselection on those drives */
+ fd->ft = fd_native_types + fd->type;
+ return (0);
+
+ case FDT_12M:
+ fdtp = fd_searchlist_12m;
+ n = sizeof fd_searchlist_12m / sizeof(struct fd_type);
+ break;
+
+ case FDT_144M:
+ fdtp = fd_searchlist_144m;
+ n = sizeof fd_searchlist_144m / sizeof(struct fd_type);
+ break;
+
+ case FDT_288M:
+ fdtp = fd_searchlist_288m;
+ n = sizeof fd_searchlist_288m / sizeof(struct fd_type);
+ break;
+ }
+
+ /*
+ * Try reading sector ID fields, first at cylinder 0, head 0,
+ * then at cylinder 2, head N. We don't probe cylinder 1,
+ * since for 5.25in DD media in a HD drive, there are no data
+ * to read (2 step pulses per media cylinder required). For
+ * two-sided media, the second probe always goes to head 1, so
+ * we can tell them apart from single-sided media. As a
+ * side-effect this means that single-sided media should be
+ * mentioned in the search list after two-sided media of an
+ * otherwise identical density. Media with a different number
+ * of sectors per track but otherwise identical parameters
+ * cannot be distinguished at all.
+ *
+ * If we successfully read an ID field on both cylinders where
+ * the recorded values match our expectation, we are done.
+ * Otherwise, we try the next density entry from the table.
+ *
+ * Stepping to cylinder 2 has the side-effect of clearing the
+ * unit attention bit.
+ */
+ oopts = fd->options;
+ fd->options |= FDOPT_NOERRLOG | FDOPT_NORETRY;
+ for (i = 0; i < n; i++, fdtp++) {
+ fd->ft = fdtp;
+
+ id.cyl = id.head = 0;
+ rv = fdmisccmd(dev, BIO_RDSECTID, &id);
+ if (rv != 0)
+ continue;
+ if (id.cyl != 0 || id.head != 0 ||
+ id.secshift != fdtp->secsize)
+ continue;
+ id.cyl = 2;
+ id.head = fd->ft->heads - 1;
+ rv = fdmisccmd(dev, BIO_RDSECTID, &id);
+ if (id.cyl != 2 || id.head != fdtp->heads - 1 ||
+ id.secshift != fdtp->secsize)
+ continue;
+ if (rv == 0)
+ break;
+ }
+
+ fd->options = oopts;
+ if (i == n) {
+ device_printf(fd->dev, "autoselection failed\n");
+ fd->ft = 0;
+ return (EIO);
+ } else {
+ device_printf(fd->dev, "autoselected %d KB medium\n",
+ fd->ft->size / 2);
+ return (0);
+ }
+}
+
+
+/*
* The controller state machine.
*
* If it returns a non zero value, it should be called again immediately.
@@ -1825,7 +1937,7 @@ fdstate(fdc_p fdc)
{
struct fdc_readid *idp;
int read, format, rdsectid, cylinder, head, i, sec = 0, sectrac;
- int st0, cyl, st3, idf;
+ int st0, cyl, st3, idf, ne7cmd, mfm, steptrac;
unsigned long blknum;
fdu_t fdu = fdc->fdu;
fd_p fd;
@@ -1862,6 +1974,8 @@ fdstate(fdc_p fdc)
if (fdc->fd && (fd != fdc->fd))
device_printf(fd->dev, "confused fd pointers\n");
read = bp->bio_cmd == BIO_READ;
+ mfm = (fd->ft->flags & FL_MFM)? NE7CMD_MFM: 0;
+ steptrac = (fd->ft->flags & FL_2STEP)? 2: 1;
if (read)
idf = ISADMA_READ;
else
@@ -1922,8 +2036,7 @@ fdstate(fdc_p fdc)
return (1); /* will return immediately */
}
if (fd_cmd(fdc, 3, NE7CMD_SEEK,
- fd->fdsu, cylinder * fd->ft->steptrac,
- 0))
+ fd->fdsu, cylinder * steptrac, 0))
{
/*
* Seek command not accepted, looks like
@@ -1948,7 +2061,7 @@ fdstate(fdc_p fdc)
/* Make sure seek really happened. */
if(fd->track == FD_NO_TRACK) {
- int descyl = cylinder * fd->ft->steptrac;
+ int descyl = cylinder * steptrac;
do {
/*
* This might be a "ready changed" interrupt,
@@ -2023,6 +2136,8 @@ fdstate(fdc_p fdc)
sec = blknum % (sectrac * fd->ft->heads);
head = sec / sectrac;
sec = sec % sectrac + 1;
+ if (head != 0 && fd->ft->offset_side2 != 0)
+ sec += fd->ft->offset_side2;
fd->hddrv = ((head&1)<<2)+fdu;
if(format || !(read || rdsectid))
@@ -2061,6 +2176,7 @@ fdstate(fdc_p fdc)
}
if (format) {
+ ne7cmd = NE7CMD_FORMAT | mfm;
if (fdc->flags & FDC_NODMA) {
/*
* This seems to be necessary for
@@ -2082,7 +2198,7 @@ fdstate(fdc_p fdc)
}
/* formatting */
- if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu,
+ if(fd_cmd(fdc, 6, ne7cmd, head << 2 | fdu,
finfo->fd_formb_secshift,
finfo->fd_formb_nsecs,
finfo->fd_formb_gaplen,
@@ -2097,13 +2213,15 @@ fdstate(fdc_p fdc)
return (retrier(fdc));
}
} else if (rdsectid) {
- if (fd_cmd(fdc, 2, NE7CMD_READID, head << 2 | fdu, 0)) {
+ ne7cmd = NE7CMD_READID | mfm;
+ if (fd_cmd(fdc, 2, ne7cmd, head << 2 | fdu, 0)) {
/* controller jamming */
fdc->retry = 6;
return (retrier(fdc));
}
} else {
/* read or write operation */
+ ne7cmd = (read ? NE7CMD_READ | NE7CMD_SK : NE7CMD_WRITE) | mfm;
if (fdc->flags & FDC_NODMA) {
/*
* This seems to be necessary even when
@@ -2121,7 +2239,7 @@ fdstate(fdc_p fdc)
fdblk);
}
if (fd_cmd(fdc, 9,
- (read ? NE7CMD_READ : NE7CMD_WRITE),
+ ne7cmd,
head << 2 | fdu, /* head & unit */
fd->track, /* track */
head,
@@ -2482,12 +2600,111 @@ fdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
struct fdc_status *fsp;
struct fdc_readid *rid;
size_t fdblk;
- int error;
+ int error, type;
fdu = FDUNIT(minor(dev));
+ type = FDTYPE(minor(dev));
fd = devclass_get_softc(fd_devclass, fdu);
+ /*
+ * First, handle everything that could be done with
+ * FD_NONBLOCK still being set.
+ */
+ switch (cmd) {
+ case FIONBIO:
+ if (*(int *)addr != 0)
+ fd->flags |= FD_NONBLOCK;
+ else {
+ if (fd->ft == 0) {
+ /*
+ * No drive type has been selected yet,
+ * cannot turn FNONBLOCK off.
+ */
+ return (EINVAL);
+ }
+ fd->flags &= ~FD_NONBLOCK;
+ }
+ return (0);
+
+ case FIOASYNC:
+ /* keep the generic fcntl() code happy */
+ return (0);
+ case FD_GTYPE: /* get drive type */
+ if (fd->ft == 0)
+ /* no type known yet, return the native type */
+ *(struct fd_type *)addr = fd_native_types[fd->type];
+ else
+ *(struct fd_type *)addr = *fd->ft;
+ return (0);
+
+ case FD_STYPE: /* set drive type */
+ if (type == 0) {
+ /*
+ * Allow setting drive type temporarily iff
+ * currently unset. Used for fdformat so any
+ * user can set it, and then start formatting.
+ */
+ if (fd->ft)
+ return (EINVAL); /* already set */
+ fd->ft = fd->fts;
+ *fd->ft = *(struct fd_type *)addr;
+ fd->flags |= FD_UA;
+ } else {
+ /*
+ * Set density definition permanently. Only
+ * allow for superuser.
+ */
+ if (suser_td(td) != 0)
+ return (EPERM);
+ fd->fts[type] = *(struct fd_type *)addr;
+ }
+ return (0);
+
+ case FD_GOPTS: /* get drive options */
+ *(int *)addr = fd->options + (type == 0? FDOPT_AUTOSEL: 0);
+ return (0);
+
+ case FD_SOPTS: /* set drive options */
+ fd->options = *(int *)addr & ~FDOPT_AUTOSEL;
+ return (0);
+
+#ifdef FDC_DEBUG
+ case FD_DEBUG:
+ if ((fd_debug != 0) != (*(int *)addr != 0)) {
+ fd_debug = (*(int *)addr != 0);
+ printf("fd%d: debugging turned %s\n",
+ fd->fdu, fd_debug ? "on" : "off");
+ }
+ return (0);
+#endif
+
+ case FD_CLRERR:
+ if (suser_td(td) != 0)
+ return (EPERM);
+ fd->fdc->fdc_errs = 0;
+ return (0);
+
+ case FD_GSTAT:
+ fsp = (struct fdc_status *)addr;
+ if ((fd->fdc->flags & FDC_STAT_VALID) == 0)
+ return (EINVAL);
+ memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int));
+ return (0);
+
+ case FD_GDTYPE:
+ *(enum fd_drivetype *)addr = fd->type;
+ return (0);
+ }
+
+ /*
+ * Now handle everything else. Make sure we have a valid
+ * drive type.
+ */
+ if (fd->flags & FD_NONBLOCK)
+ return (EAGAIN);
+ if (fd->ft == 0)
+ return (ENXIO);
fdblk = 128 << fd->ft->secsize;
error = 0;
diff --git a/sys/isa/fdreg.h b/sys/isa/fdreg.h
index 5dee70d..c082668 100644
--- a/sys/isa/fdreg.h
+++ b/sys/isa/fdreg.h
@@ -55,14 +55,11 @@
#define FDDATA 5 /* NEC 765 Data Register (R/W) */
#define FDCTL 7 /* Control Register (W) */
-#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 */
-
+/*
+ * The definitions for FDC_500KBPS etc. have been moved out to <sys/fdcio.h>
+ * since they need to be visible in userland. They cover the lower two bits
+ * of FDCTL when used for output.
+ */
/*
* this is the secret PIO data port (offset from base)
*/
diff --git a/sys/sys/fdcio.h b/sys/sys/fdcio.h
index b9a86cd..48568ff 100644
--- a/sys/sys/fdcio.h
+++ b/sys/sys/fdcio.h
@@ -42,7 +42,7 @@
struct fd_formb {
int format_version; /* == FD_FORMAT_VERSION */
int cyl, head;
- int transfer_rate; /* fdreg.h: FDC_???KBPS */
+ int transfer_rate; /* FDC_???KBPS */
union {
struct fd_form_data {
@@ -88,13 +88,17 @@ struct fd_type {
int secsize; /* size code for sectors */
int datalen; /* data len when secsize = 0 */
int gap; /* gap len between sectors */
- int tracks; /* total num of tracks */
+ int tracks; /* total number of cylinders */
int size; /* size of disk in sectors */
- int steptrac; /* steps per cylinder */
int trans; /* transfer speed code */
int heads; /* number of heads */
int f_gap; /* format gap len */
int f_inter; /* format interleave factor */
+ int offset_side2; /* offset of sectors on side2 */
+ int flags; /* misc. features */
+#define FL_MFM 0x0001 /* MFM recording */
+#define FL_2STEP 0x0002 /* 2 steps between cylinders */
+#define FL_PERPND 0x0004 /* perpendicular recording */
};
struct fdc_status {
@@ -112,6 +116,16 @@ struct fdc_readid {
u_char secshift; /* N - log2(secsize / 128) */
};
+/*
+ * Diskette drive type, basically the same as stored in RTC on ISA
+ * machines (see /sys/isa/rtc.h), but right-shifted by four bits.
+ */
+enum fd_drivetype {
+ FDT_NONE, FDT_360K, FDT_12M, FDT_720K, FDT_144M, FDT_288M_1,
+ FDT_288M
+};
+
+
#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 */
@@ -129,26 +143,28 @@ struct fdc_readid {
* Obtain NE765 status registers. Only successful if there is
* a valid status stored in fdc->status[].
*/
-#define FD_GSTAT _IOR('F', 68, struct fdc_status)
+#define FD_GSTAT _IOR('F', 69, struct fdc_status)
+
+#define FD_GDTYPE _IOR('F', 70, enum fd_drivetype) /* obtain drive type */
/* Options for FD_GOPTS/FD_SOPTS, cleared on device close */
#define FDOPT_NORETRY 0x0001 /* no retries on failure */
#define FDOPT_NOERRLOG 0x002 /* no "hard error" kernel log messages */
#define FDOPT_NOERROR 0x0004 /* do not indicate errors, caller will use
FD_GSTAT in order to obtain status */
+#define FDOPT_AUTOSEL 0x8000 /* read/only option: device performs media
+ * autoselection */
/*
- * 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 ;-)
+ * Transfer rate definitions. Used in the structures above. They
+ * represent the hardware encoding of bits 0 and 1 of the FDC control
+ * register when writing to the register.
+ * Transfer rates for FM encoding are half the values listed here
+ * (but we currently don't support FM encoding).
*/
-#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 */
+#define FDC_1MBPS 0x03 /* 1MPBS MFM drive transfer rate */
#endif /* !_MACHINE_IOCTL_FD_H_ */
diff --git a/usr.sbin/fdcontrol/Makefile b/usr.sbin/fdcontrol/Makefile
index ec411d7a..38f9a1e 100644
--- a/usr.sbin/fdcontrol/Makefile
+++ b/usr.sbin/fdcontrol/Makefile
@@ -1,8 +1,12 @@
# $FreeBSD$
+.PATH: ${.CURDIR}/../fdread
+
PROG= fdcontrol
+SRCS= fdcontrol.c fdutil.c
MAN= fdcontrol.8
WARNS?= 2
+CFLAGS+= -I${.CURDIR}/../fdread
.include <bsd.prog.mk>
diff --git a/usr.sbin/fdcontrol/fdcontrol.c b/usr.sbin/fdcontrol/fdcontrol.c
index e120059..9d447a5 100644
--- a/usr.sbin/fdcontrol/fdcontrol.c
+++ b/usr.sbin/fdcontrol/fdcontrol.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 1994 by Joerg Wunsch, Dresden
+ * Copyright (C) 1994, 2001 by Joerg Wunsch, Dresden
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -36,110 +36,144 @@ static const char rcsid[] =
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
#include <unistd.h>
-static int getnumber(void);
-static void usage(void);
+#include "fdutil.h"
-static int
-getnumber(void)
-{
- int i;
- char b[80];
- fgets(b, 80, stdin);
- if(b[0] == '\n') return -1;
+static int debug = -1, format, verbose, show = 1, showfmt;
+static char *fmtstring;
- sscanf(b, " %i", &i);
- return i;
-}
+static void showdev(enum fd_drivetype, const char *);
+static void usage(void);
static void
usage(void)
{
- fprintf(stderr, "usage: fdcontrol [-d 0|1] | [-s] device-node\n");
- exit(2);
+ errx(EX_USAGE,
+ "usage: fdcontrol [-F] [-d dbg] [-f fmt] [-s fmtstr] [-v] device");
}
+void
+showdev(enum fd_drivetype type, const char *fname)
+{
+ const char *name, *descr;
-#define ask(name, fmt) \
-printf(#name "? [" fmt "]: ", ft.name); fflush(stdout); \
-if((i = getnumber()) != -1) ft.name = i
+ getname(type, &name, &descr);
+ if (verbose)
+ printf("%s: %s drive (%s)\n", fname, name, descr);
+ else
+ printf("%s\n", name);
+}
int
main(int argc, char **argv)
{
- struct fd_type ft;
- int fd, i;
- int debug = -1, settype = 1;
-
- while((i = getopt(argc, argv, "d:s")) != -1)
- switch(i)
- {
- case 'd':
- debug = atoi(optarg);
- settype = 0;
- break;
-
- case 's':
- debug = -1;
- settype = 1;
- break;
-
- case '?':
- default:
- usage();
- }
-
- argc -= optind;
- argv += optind;
-
- if(argc != 1)
- usage();
-
- if((fd = open(argv[0], 0)) < 0)
- {
- warn("open(floppy)");
- return 1;
- }
-
- if(debug != -1)
- {
- if(ioctl(fd, FD_DEBUG, &debug) < 0)
- {
- warn("ioctl(FD_DEBUG)");
- return 1;
+ enum fd_drivetype type;
+ struct fd_type ft, newft, *fdtp;
+ const char *name, *descr;
+ int fd, i, mode;
+
+ while((i = getopt(argc, argv, "d:Ff:s:v")) != -1)
+ switch(i) {
+ case 'd':
+ if (strcmp(optarg, "0") == 0)
+ debug = 0;
+ else if (strcmp(optarg, "1") == 0)
+ debug = 1;
+ else
+ usage();
+ show = 0;
+ break;
+
+ case 'F':
+ showfmt = 1;
+ show = 0;
+ break;
+
+ case 'f':
+ if (getnum(optarg, &format)) {
+ fprintf(stderr,
+ "Bad argument %s to -f option; must be numeric\n",
+ optarg);
+ usage();
+ }
+ show = 0;
+ break;
+
+ case 's':
+ fmtstring = optarg;
+ show = 0;
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(argc != 1)
+ usage();
+
+ if (show || showfmt)
+ mode = O_RDONLY | O_NONBLOCK;
+ else
+ mode = O_RDWR;
+
+ if((fd = open(argv[0], mode)) < 0)
+ err(EX_UNAVAILABLE, "open(%s)", argv[0]);
+
+ if (ioctl(fd, FD_GDTYPE, &type) == -1)
+ err(EX_OSERR, "ioctl(FD_GDTYPE)");
+ if (ioctl(fd, FD_GTYPE, &ft) == -1)
+ err(EX_OSERR, "ioctl(FD_GTYPE)");
+
+ if (show) {
+ showdev(type, argv[0]);
+ return (0);
}
- return 0;
- }
-
- if(settype)
- {
- if(ioctl(fd, FD_GTYPE, &ft) < 0)
- {
- warn("ioctl(FD_GTYPE)");
- return 1;
+
+ if (format) {
+ getname(type, &name, &descr);
+ fdtp = get_fmt(format, type);
+ if (fdtp == 0)
+ errx(EX_USAGE,
+ "unknown format %d KB for drive type %s",
+ format, name);
+ ft = *fdtp;
+ }
+
+ if (fmtstring) {
+ parse_fmt(fmtstring, type, ft, &newft);
+ ft = newft;
+ }
+
+ if (showfmt) {
+ if (verbose)
+ printf("%s: %d KB media type, fmt = ",
+ argv[0], ft.size / 2);
+ print_fmt(ft);
+ return (0);
+ }
+
+ if (format || fmtstring) {
+ if (ioctl(fd, FD_STYPE, &ft) == -1)
+ err(EX_OSERR, "ioctl(FD_STYPE)");
+ return (0);
}
- ask(sectrac, "%d");
- ask(secsize, "%d");
- ask(datalen, "0x%x");
- ask(gap, "0x%x");
- ask(tracks, "%d");
- ask(size, "%d");
- ask(steptrac, "%d");
- ask(trans, "%d");
- ask(heads, "%d");
- ask(f_gap, "0x%x");
- ask(f_inter, "%d");
-
- if(ioctl(fd, FD_STYPE, &ft) < 0)
- {
- warn("ioctl(FD_STYPE)");
- return 1;
+ if (debug != -1) {
+ if (ioctl(fd, FD_DEBUG, &debug) == -1)
+ err(EX_OSERR, "ioctl(FD_DEBUG)");
+ return (0);
}
- return 0;
- }
- return 0;
+ return 0;
}
diff --git a/usr.sbin/fdformat/fdformat.c b/usr.sbin/fdformat/fdformat.c
index 0c194b3..0e037b4 100644
--- a/usr.sbin/fdformat/fdformat.c
+++ b/usr.sbin/fdformat/fdformat.c
@@ -26,19 +26,9 @@
* $FreeBSD$
*/
-/*
- * 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 <sys/types.h>
+#include <sys/fdcio.h>
+#include <sys/stat.h>
#include <ctype.h>
#include <err.h>
@@ -48,24 +38,24 @@
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
+#include <sysexits.h>
#include <unistd.h>
-#include <sys/fdcio.h>
-
#include "fdutil.h"
static void
format_track(int fd, int cyl, int secs, int head, int rate,
- int gaplen, int secsize, int fill,int interleave)
+ int gaplen, int secsize, int fill, int interleave,
+ int offset)
{
struct fd_formb f;
- register int i,j;
- int il[FD_MAX_NSEC + 1];
+ int i, j, 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;
+ memset(il, 0, sizeof il);
+ for(j = 0, i = 1 + offset; i <= secs + offset; i++) {
+ while(il[(j % secs) + 1])
+ j++;
+ il[(j % secs) + 1] = i;
j += interleave;
}
@@ -85,14 +75,14 @@ format_track(int fd, int cyl, int secs, int head, int rate,
f.fd_formb_secsize(i) = secsize;
}
if(ioctl(fd, FD_FORM, (caddr_t)&f) < 0)
- err(1, "ioctl(FD_FORM)");
+ err(EX_OSERR, "ioctl(FD_FORM)");
}
static int
verify_track(int fd, int track, int tracksize)
{
- static char *buf = 0;
- static int bufsz = 0;
+ static char *buf;
+ static int bufsz;
int fdopts = -1, ofdopts, rv = 0;
if (ioctl(fd, FD_GOPTS, &fdopts) < 0)
@@ -103,61 +93,37 @@ verify_track(int fd, int track, int tracksize)
(void)ioctl(fd, FD_SOPTS, &fdopts);
}
- if (bufsz < tracksize) {
- if (buf)
- free (buf);
- bufsz = tracksize;
- buf = 0;
- }
- if (! buf)
- buf = malloc (bufsz);
- if (! buf)
- errx(2, "out of memory");
- if (lseek (fd, (long) track*tracksize, 0) < 0)
+ if (bufsz < tracksize)
+ buf = realloc(buf, bufsz = tracksize);
+ if (buf == 0)
+ errx(EX_UNAVAILABLE, "out of memory");
+ 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)
+ 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/fd0a"... */
-
- memset(namebuff, 0, 20);
- if(*arg == '\0') /* ??? */
- return arg;
- if(*arg == '/') /* do not convert absolute pathnames */
- return arg;
- strcpy(namebuff, _PATH_DEV);
- strncat(namebuff, arg, 3);
- strcat(namebuff, suffix);
- return namebuff;
-}
-
static void
usage (void)
{
- fprintf(stderr, "%s\n%s\n",
- "usage: fdformat [-y] [-q] [-n | -v] [-f #] [-c #] [-s #] [-h #]",
- " [-r #] [-g #] [-i #] [-S #] [-F #] [-t #] device_name");
- exit(2);
+ errx(EX_USAGE,
+ "usage: fdformat [-F fill] [-f fmt] [-s fmtstr] [-nqvy] device");
}
static int
yes (void)
{
- char reply [256], *p;
+ char reply[256], *p;
- reply[sizeof(reply)-1] = 0;
+ reply[sizeof(reply) - 1] = 0;
for (;;) {
fflush(stdout);
- if (! fgets (reply, sizeof(reply)-1, stdin))
+ if (!fgets (reply, sizeof(reply) - 1, stdin))
return (0);
for (p=reply; *p==' ' || *p=='\t'; ++p)
continue;
@@ -172,129 +138,149 @@ yes (void)
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, confirm = 0;
- int fd, c, i, track, error, tracks_per_dot, bytes_per_track, errs;
- int fdopts;
- const char *device, *suffix;
- struct fd_type fdt;
+ enum fd_drivetype type;
+ struct fd_type fdt, newft, *fdtp;
+ struct stat sb;
#define MAXPRINTERRS 10
struct fdc_status fdcs[MAXPRINTERRS];
+ int format, fill, quiet, verify, verify_only, confirm;
+ int fd, c, i, track, error, tracks_per_dot, bytes_per_track, errs;
+ int fdopts, flags;
+ char *fmtstring, *device;
+ const char *name, *descr;
- while((c = getopt(argc, argv, "f:c:s:h:r:g:S:F:t:i:qyvn")) != -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;
+ format = quiet = verify_only = confirm = 0;
+ verify = 1;
+ fill = 0xf6;
+ fmtstring = 0;
- case 'F': /* fill byte, C-like notation allowed */
- fill = (int)strtol(optarg, (char **)0, 0);
+ while((c = getopt(argc, argv, "F:f:nqs:vy")) != -1)
+ switch(c) {
+ case 'F': /* fill byte */
+ if (getnum(optarg, &fill)) {
+ fprintf(stderr,
+ "Bad argument %s to -F option; must be numeric\n",
+ optarg);
+ usage();
+ }
break;
- case 't': /* steps per track */
- steps = atoi(optarg);
+ case 'f': /* format in kilobytes */
+ if (getnum(optarg, &format)) {
+ fprintf(stderr,
+ "Bad argument %s to -f option; must be numeric\n",
+ optarg);
+ usage();
+ }
break;
- case 'i': /* interleave factor */
- intleave = atoi(optarg);
+ case 'n': /* don't verify */
+ verify = 0;
break;
- case 'q':
+ case 'q': /* quiet */
quiet = 1;
break;
- case 'y':
- confirm = 1;
+ case 's': /* format string with detailed options */
+ fmtstring = optarg;
break;
- case 'n':
- verify = 0;
- break;
-
- case 'v':
+ case 'v': /* verify only */
verify = 1;
verify_only = 1;
break;
- case '?': default:
+ case 'y': /* confirm */
+ confirm = 1;
+ break;
+
+ default:
usage();
}
if(optind != argc - 1)
usage();
- switch(format) {
- default:
- errx(2, "bad floppy size: %dK", format);
- case -1: suffix = ""; break;
- case 360: suffix = ".360"; break;
- case 640: suffix = ".640"; break;
- case 720: suffix = ".720"; break;
- case 800: suffix = ".800"; break;
- case 820: suffix = ".820"; break;
- case 1200: suffix = ".1200"; break;
- case 1232: suffix = ".1232"; break;
- case 1440: suffix = ".1440"; break;
- case 1480: suffix = ".1480"; break;
- case 1720: suffix = ".1720"; break;
+ if (stat(argv[optind], &sb) == -1 && errno == ENOENT) {
+ /* try prepending _PATH_DEV */
+ device = malloc(strlen(argv[optind] + sizeof _PATH_DEV + 1));
+ if (device == 0)
+ errx(EX_UNAVAILABLE, "out of memory");
+ strcpy(device, _PATH_DEV);
+ strcat(device, argv[optind]);
+ if (stat(device, &sb) == -1) {
+ free(device);
+ device = argv[optind]; /* let it fail below */
+ }
+ } else {
+ device = argv[optind];
}
- device = makename(argv[optind], suffix);
-
- if((fd = open(device, O_RDWR)) < 0)
- err(1, "%s", device);
+ if ((fd = open(device, O_RDWR | O_NONBLOCK)) < 0)
+ err(EX_OSERR, "open(%s)", device);
+ /*
+ * Device initialization.
+ *
+ * First, get the device type descriptor. This tells us about
+ * the media geometry data we need to format a medium. It also
+ * lets us know quickly whether the device name actually points
+ * to a floppy disk drive.
+ *
+ * Then, obtain any drive options. We're mainly interested to
+ * see whether we're currently working on a device with media
+ * density autoselection (FDOPT_AUTOSEL). Then, we add the
+ * device option to tell the kernel not to log media errors,
+ * since we can handle them ourselves. If the device does
+ * media density autoselection, we then need to set the device
+ * type appropriately, since by opening with O_NONBLOCK we
+ * told the driver to bypass media autoselection (otherwise we
+ * wouldn't stand a chance to format an unformatted or damaged
+ * medium). We do not attempt to set the media type on any
+ * other devices since this is a privileged operation. For the
+ * same reason, specifying -f and -s options is only possible
+ * for autoselecting devices.
+ *
+ * Finally, we are ready to turn off O_NONBLOCK, and start to
+ * actually format something.
+ */
if(ioctl(fd, FD_GTYPE, &fdt) < 0)
- errx(1, "not a floppy disk: %s", device);
- fdopts = FDOPT_NOERRLOG;
+ errx(EX_OSERR, "not a floppy disk: %s", device);
+ if (ioctl(fd, FD_GDTYPE, &type) == -1)
+ err(EX_OSERR, "ioctl(FD_GDTYPE)");
+ if (ioctl(fd, FD_GOPTS, &fdopts) == -1)
+ err(EX_OSERR, "ioctl(FD_GOPTS)");
+ fdopts |= FDOPT_NOERRLOG;
if (ioctl(fd, FD_SOPTS, &fdopts) == -1)
- err(1, "ioctl(FD_SOPTS, FDOPT_NOERRLOG)");
-
- switch(rate) {
- case -1: break;
- case 250: fdt.trans = FDC_250KBPS; break;
- case 300: fdt.trans = FDC_300KBPS; break;
- case 500: fdt.trans = FDC_500KBPS; break;
- default:
- errx(2, "invalid transfer rate: %d", rate);
+ err(EX_OSERR, "ioctl(FD_SOPTS, FDOPT_NOERRLOG)");
+ if (format) {
+ getname(type, &name, &descr);
+ fdtp = get_fmt(format, type);
+ if (fdtp == 0)
+ errx(EX_USAGE,
+ "unknown format %d KB for drive type %s",
+ format, name);
+ fdt = *fdtp;
}
+ if (fmtstring) {
+ parse_fmt(fmtstring, type, fdt, &newft);
+ fdt = newft;
+ }
+ if (fdopts & FDOPT_AUTOSEL) {
+ if (ioctl(fd, FD_STYPE, &fdt) < 0)
+ err(EX_OSERR, "ioctl(FD_STYPE)");
+ } else if (fmtstring || format) {
+ errx(EX_USAGE,
+ "-f fmt or -s fmtstr is only allowed for autoselecting devices");
+ }
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+ err(EX_OSERR, "fcntl(F_GETFL)");
+ flags &= ~O_NONBLOCK;
+ if (fcntl(fd, F_SETFL, flags) == -1)
+ err(EX_OSERR, "fcntl(F_SETFL)");
- if (cyls >= 0) fdt.tracks = cyls;
- if (secs >= 0) fdt.sectrac = secs;
- if (fdt.sectrac > FD_MAX_NSEC)
- errx(2, "too many sectors per track, max value is %d", FD_MAX_NSEC);
- if (heads >= 0) fdt.heads = heads;
- if (gaplen >= 0) fdt.f_gap = gaplen;
- if (secsize >= 0) fdt.secsize = secsize;
- if (steps >= 0) fdt.steptrac = steps;
- if (intleave >= 0) fdt.f_inter = intleave;
-
- bytes_per_track = fdt.sectrac * (1<<fdt.secsize) * 128;
+ bytes_per_track = fdt.sectrac * (128 << fdt.secsize);
/* XXX 20/40 = 0.5 */
tracks_per_dot = (fdt.tracks * fdt.heads + 20) / 40;
@@ -309,9 +295,9 @@ main(int argc, char **argv)
printf("Format %dK floppy `%s'? (y/n): ",
fdt.tracks * fdt.heads * bytes_per_track / 1024,
device);
- if(! yes ()) {
+ if(!yes()) {
printf("Not confirmed.\n");
- return 3;
+ return (EX_UNAVAILABLE);
}
}
@@ -332,7 +318,8 @@ main(int argc, char **argv)
if (!verify_only) {
format_track(fd, track / fdt.heads, fdt.sectrac,
track % fdt.heads, fdt.trans, fdt.f_gap,
- fdt.secsize, fill, fdt.f_inter);
+ fdt.secsize, fill, fdt.f_inter,
+ track % fdt.heads? fdt.offset_side2: 0);
if(!quiet && !((track + 1) % tracks_per_dot)) {
putchar('F');
fflush(stdout);
@@ -344,7 +331,7 @@ main(int argc, char **argv)
if (errs < MAXPRINTERRS && errno == EIO) {
if (ioctl(fd, FD_GSTAT, fdcs + errs) ==
-1)
- errx(1,
+ errx(EX_IOERR,
"floppy IO error, but no FDC status");
errs++;
}
diff --git a/usr.sbin/fdread/fdutil.c b/usr.sbin/fdread/fdutil.c
index fb61b30..9990790 100644
--- a/usr.sbin/fdread/fdutil.c
+++ b/usr.sbin/fdread/fdutil.c
@@ -26,16 +26,23 @@
* $FreeBSD$
*/
-#include <sys/types.h>
+#include <dev/ic/nec765.h>
+
#include <sys/fdcio.h>
+#include <err.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-
-#include <dev/ic/nec765.h>
+#include <sysexits.h>
#include "fdutil.h"
+/*
+ * Decode the FDC status pointed to by `fdcsp', and print a textual
+ * translation to stderr. If `terse' is false, the numerical FDC
+ * register status is printed, too.
+ */
void
printstatus(struct fdc_status *fdcsp, int terse)
{
@@ -78,3 +85,382 @@ printstatus(struct fdc_status *fdcsp, int terse)
fputs(msgbuf, stderr);
}
+static struct fd_type fd_types_288m[] =
+{
+#if 0
+{ 36,2,0xFF,0x1B,80,5760,FDC_1MBPS, 2,0x4C,1,1,FL_MFM|FL_PERPND } /*2.88M*/
+#endif
+{ 21,2,0xFF,0x04,82,3444,FDC_500KBPS,2,0x0C,2,0,FL_MFM }, /* 1.72M */
+{ 18,2,0xFF,0x1B,82,2952,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.48M */
+{ 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.44M */
+{ 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* 1.2M */
+{ 10,2,0xFF,0x10,82,1640,FDC_250KBPS,2,0x2E,1,0,FL_MFM }, /* 820K */
+{ 10,2,0xFF,0x10,80,1600,FDC_250KBPS,2,0x2E,1,0,FL_MFM }, /* 800K */
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+};
+
+static struct fd_type fd_types_144m[] =
+{
+{ 21,2,0xFF,0x04,82,3444,FDC_500KBPS,2,0x0C,2,0,FL_MFM }, /* 1.72M */
+{ 18,2,0xFF,0x1B,82,2952,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.48M */
+{ 18,2,0xFF,0x1B,80,2880,FDC_500KBPS,2,0x6C,1,0,FL_MFM }, /* 1.44M */
+{ 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* 1.2M */
+{ 10,2,0xFF,0x10,82,1640,FDC_250KBPS,2,0x2E,1,0,FL_MFM }, /* 820K */
+{ 10,2,0xFF,0x10,80,1600,FDC_250KBPS,2,0x2E,1,0,FL_MFM }, /* 800K */
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+};
+
+static struct fd_type fd_types_12m[] =
+{
+{ 15,2,0xFF,0x1B,80,2400,FDC_500KBPS,2,0x54,1,0,FL_MFM }, /* 1.2M */
+{ 8,3,0xFF,0x35,77,1232,FDC_500KBPS,2,0x74,1,0,FL_MFM }, /* 1.23M */
+{ 18,2,0xFF,0x02,82,2952,FDC_500KBPS,2,0x02,2,0,FL_MFM }, /* 1.48M */
+{ 18,2,0xFF,0x02,80,2880,FDC_500KBPS,2,0x02,2,0,FL_MFM }, /* 1.44M */
+{ 10,2,0xFF,0x10,82,1640,FDC_300KBPS,2,0x2E,1,0,FL_MFM }, /* 820K */
+{ 10,2,0xFF,0x10,80,1600,FDC_300KBPS,2,0x2E,1,0,FL_MFM }, /* 800K */
+{ 9,2,0xFF,0x20,80,1440,FDC_300KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+{ 9,2,0xFF,0x23,40, 720,FDC_300KBPS,2,0x50,1,0,FL_MFM|FL_2STEP }, /* 360K */
+{ 8,2,0xFF,0x2A,80,1280,FDC_300KBPS,2,0x50,1,0,FL_MFM }, /* 640K */
+};
+
+static struct fd_type fd_types_720k[] =
+{
+{ 9,2,0xFF,0x20,80,1440,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 720K */
+};
+
+static struct fd_type fd_types_360k[] =
+{
+{ 9,2,0xFF,0x2A,40, 720,FDC_250KBPS,2,0x50,1,0,FL_MFM }, /* 360K */
+};
+
+/*
+ * Parse a format string, and fill in the parameter pointed to by `out'.
+ *
+ * sectrac,secsize,datalen,gap,ncyls,speed,heads,f_gap,f_inter,offs2,flags[...]
+ *
+ * sectrac = sectors per track
+ * secsize = sector size in bytes
+ * datalen = length of sector if secsize == 128
+ * gap = gap length when reading
+ * ncyls = number of cylinders
+ * speed = transfer speed 250/300/500/1000 KB/s
+ * heads = number of heads
+ * f_gap = gap length when formatting
+ * f_inter = sector interleave when formatting
+ * offs2 = offset of sectors on side 2
+ * flags = +/-mfm | +/-2step | +/-perpend
+ * mfm - use MFM recording
+ * 2step - use 2 steps between cylinders
+ * perpend - user perpendicular (vertical) recording
+ *
+ * Any omitted value will be passed on from parameter `in'.
+ */
+void
+parse_fmt(const char *s, enum fd_drivetype type,
+ struct fd_type in, struct fd_type *out)
+{
+ int i, j;
+ const char *cp;
+ char *s1;
+
+ *out = in;
+
+ for (i = 0;; i++) {
+ if (s == 0)
+ break;
+
+ if ((cp = strchr(s, ',')) == 0) {
+ s1 = strdup(s);
+ if (s1 == NULL)
+ abort();
+ s = 0;
+ } else {
+ s1 = malloc(cp - s + 1);
+ if (s1 == NULL)
+ abort();
+ memcpy(s1, s, cp - s);
+ s1[cp - s] = 0;
+
+ s = cp + 1;
+ }
+ if (strlen(s1) == 0) {
+ free(s1);
+ continue;
+ }
+
+ switch (i) {
+ case 0: /* sectrac */
+ if (getnum(s1, &out->sectrac))
+ errx(EX_USAGE,
+ "bad numeric value for sectrac: %s", s1);
+ break;
+
+ case 1: /* secsize */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for secsize: %s", s1);
+ if (j == 128) out->secsize = 0;
+ else if (j == 256) out->secsize = 1;
+ else if (j == 512) out->secsize = 2;
+ else if (j == 1024) out->secsize = 3;
+ else
+ errx(EX_USAGE, "bad sector size %d", j);
+ break;
+
+ case 2: /* datalen */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for datalen: %s", s1);
+ if (j >= 256)
+ errx(EX_USAGE, "bad datalen %d", j);
+ out->datalen = j;
+ break;
+
+ case 3: /* gap */
+ if (getnum(s1, &out->gap))
+ errx(EX_USAGE,
+ "bad numeric value for gap: %s", s1);
+ break;
+
+ case 4: /* ncyls */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for ncyls: %s", s1);
+ if (j > 85)
+ errx(EX_USAGE, "bad # of cylinders %d", j);
+ out->tracks = j;
+ break;
+
+ case 5: /* speed */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for speed: %s", s1);
+ switch (type) {
+ default:
+ abort(); /* paranoia */
+
+ case FDT_360K:
+ case FDT_720K:
+ if (j == 250)
+ out->trans = FDC_250KBPS;
+ else {
+ badspeed:
+ errx(EX_USAGE, "bad speed %d", j);
+ }
+ break;
+
+ case FDT_12M:
+ if (j == 300)
+ out->trans = FDC_300KBPS;
+ else if (j == 500)
+ out->trans = FDC_500KBPS;
+ else
+ goto badspeed;
+ break;
+
+ case FDT_288M:
+ if (j == 1000)
+ out->trans = FDC_1MBPS;
+ /* FALLTHROUGH */
+ case FDT_144M:
+ if (j == 250)
+ out->trans = FDC_250KBPS;
+ else if (j == 500)
+ out->trans = FDC_500KBPS;
+ else
+ goto badspeed;
+ break;
+ }
+ break;
+
+ case 6: /* heads */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for heads: %s", s1);
+ if (j == 1 || j == 2)
+ out->heads = j;
+ else
+ errx(EX_USAGE, "bad # of heads %d", j);
+ break;
+
+ case 7: /* f_gap */
+ if (getnum(s1, &out->f_gap))
+ errx(EX_USAGE,
+ "bad numeric value for f_gap: %s", s1);
+ break;
+
+ case 8: /* f_inter */
+ if (getnum(s1, &out->f_inter))
+ errx(EX_USAGE,
+ "bad numeric value for f_inter: %s", s1);
+ break;
+
+ case 9: /* offs2 */
+ if (getnum(s1, &out->offset_side2))
+ errx(EX_USAGE,
+ "bad numeric value for offs2: %s", s1);
+ break;
+
+ default:
+ if (strcmp(s1, "+mfm") == 0)
+ out->flags |= FL_MFM;
+ else if (strcmp(s1, "-mfm") == 0)
+ out->flags &= ~FL_MFM;
+ else if (strcmp(s1, "+2step") == 0)
+ out->flags |= FL_2STEP;
+ else if (strcmp(s1, "-2step") == 0)
+ out->flags &= ~FL_2STEP;
+ else if (strcmp(s1, "+perpnd") == 0)
+ out->flags |= FL_PERPND;
+ else if (strcmp(s1, "-perpnd") == 0)
+ out->flags &= ~FL_PERPND;
+ else
+ errx(EX_USAGE, "bad flag: %s", s1);
+ break;
+ }
+ free(s1);
+ }
+
+ out->size = out->tracks * out->heads * out->sectrac *
+ (128 << out->secsize) / 512;
+}
+
+/*
+ * Print a textual translation of the drive (density) type described
+ * by `in' to stdout. The string uses the same form that is parseable
+ * by parse_fmt().
+ */
+void
+print_fmt(struct fd_type in)
+{
+ int secsize, speed;
+
+ secsize = 128 << in.secsize;
+ switch (in.trans) {
+ case FDC_250KBPS: speed = 250; break;
+ case FDC_300KBPS: speed = 300; break;
+ case FDC_500KBPS: speed = 500; break;
+ case FDC_1MBPS: speed = 1000; break;
+ default: speed = 1; break;
+ }
+
+ printf("%d,%d,%#x,%#x,%d,%d,%d,%#x,%d,%d",
+ in.sectrac, secsize, in.datalen, in.gap, in.tracks,
+ speed, in.heads, in.f_gap, in.f_inter, in.offset_side2);
+ if (in.flags & FL_MFM)
+ printf(",+mfm");
+ if (in.flags & FL_2STEP)
+ printf(",+2step");
+ if (in.flags & FL_PERPND)
+ printf(",+perpnd");
+ putc('\n', stdout);
+}
+
+/*
+ * Based on `size' (in kilobytes), walk through the table of known
+ * densities for drive type `type' and see if we can find one. If
+ * found, return it (as a pointer to static storage), otherwise return
+ * NULL.
+ */
+struct fd_type *
+get_fmt(int size, enum fd_drivetype type)
+{
+ int i, n;
+ struct fd_type *fdtp;
+
+ switch (type) {
+ default:
+ return (0);
+
+ case FDT_360K:
+ fdtp = fd_types_360k;
+ n = sizeof fd_types_360k / sizeof(struct fd_type);
+ break;
+
+ case FDT_720K:
+ fdtp = fd_types_720k;
+ n = sizeof fd_types_720k / sizeof(struct fd_type);
+ break;
+
+ case FDT_12M:
+ fdtp = fd_types_12m;
+ n = sizeof fd_types_12m / sizeof(struct fd_type);
+ break;
+
+ case FDT_144M:
+ fdtp = fd_types_144m;
+ n = sizeof fd_types_144m / sizeof(struct fd_type);
+ break;
+
+ case FDT_288M:
+ fdtp = fd_types_288m;
+ n = sizeof fd_types_288m / sizeof(struct fd_type);
+ break;
+ }
+
+ for (i = 0; i < n; i++, fdtp++)
+ if (fdtp->size / 2 == size)
+ return (fdtp);
+
+ return (0);
+}
+
+/*
+ * Parse a number from `s'. If the string cannot be converted into a
+ * number completely, return -1, otherwise 0. The result is returned
+ * in `*res'.
+ */
+int
+getnum(const char *s, int *res)
+{
+ unsigned long ul;
+ char *cp;
+
+ ul = strtoul(s, &cp, 0);
+ if (*cp != '\0')
+ return (-1);
+
+ *res = (int)ul;
+ return (0);
+}
+
+/*
+ * Return a short name and a verbose description for the drive
+ * described by `t'.
+ */
+void
+getname(enum fd_drivetype t, const char **name, const char **descr)
+{
+
+ switch (t) {
+ default:
+ *name = "unknown";
+ *descr = "unknown drive type";
+ break;
+
+ case FDT_360K:
+ *name = "360K";
+ *descr = "5.25\" double-density";
+ break;
+
+ case FDT_12M:
+ *name = "1.2M";
+ *descr = "5.25\" high-density";
+ break;
+
+ case FDT_720K:
+ *name = "720K";
+ *descr = "3.5\" double-density";
+ break;
+
+ case FDT_144M:
+ *name = "1.44M";
+ *descr = "3.5\" high-density";
+ break;
+
+ case FDT_288M:
+ *name = "2.88M";
+ *descr = "3.5\" extra-density";
+ break;
+ }
+}
diff --git a/usr.sbin/fdread/fdutil.h b/usr.sbin/fdread/fdutil.h
index f499390..a093228 100644
--- a/usr.sbin/fdread/fdutil.h
+++ b/usr.sbin/fdread/fdutil.h
@@ -26,4 +26,12 @@
* $FreeBSD$
*/
+
void printstatus(struct fdc_status *fdcsp, int terse);
+void parse_fmt(const char *, enum fd_drivetype,
+ struct fd_type, struct fd_type *);
+struct fd_type *get_fmt(int, enum fd_drivetype);
+void print_fmt(struct fd_type);
+int getnum(const char *, int *);
+void getname(enum fd_drivetype, const char **, const char **);
+
OpenPOWER on IntegriCloud