summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorjoerg <joerg@FreeBSD.org>2001-12-15 19:09:04 +0000
committerjoerg <joerg@FreeBSD.org>2001-12-15 19:09:04 +0000
commitb2bcdd0ab8129dadaba668862099bdcf62c0a673 (patch)
treec968cf76f6441af16366647eb2faba327bc6777e /sys
parent994cab589b53047338727cf60ea42223f294cc51 (diff)
downloadFreeBSD-src-b2bcdd0ab8129dadaba668862099bdcf62c0a673.zip
FreeBSD-src-b2bcdd0ab8129dadaba668862099bdcf62c0a673.tar.gz
Long promised major enhancement set for the floppy disk driver:
. The main device node now supports automatic density selection for commonly used media densities. So you can stuff your 1.44 MB and 720 KB media into your drive and just access /dev/fd0, no questions asked. It's all that easy, isn't it? :) . Device density handling has been completely overhauled. The old way of hardwired kernel density knowledge is no longer there. Instead, the kernel now implements 16 subdevices per drive. The first subdevice uses automatic density selection, while the remaining 15 devices are freely programmable. They can be assigned an arbitrary name of the form /dev/fd[:digit]+.[:digit:]{1,4}, where the second number is meant to either implement device names that are mnemonic for their raw capacity (as it used to be), or they can alternatively be created as "anonymous" devices like fd0.1 through fd0.15, depending on the taste of the administrator. After creating a subdevice, it is initialized to the maximal native density of the respective drive type, so it needs to be customized for other densities by using fdcontrol(8). Pseudo-partition devices (fd0a through fd0h) are still supported as symlinks. . The old hack to use flags 0x1 to always assume drive 0 were there is no longer supported; this is now supposed to be done by wiring the devices down from the loader via device flags. On IA32 architectures, the first two drives are looked up in the CMOS configuration records though. On PCMCIA (i. e., the Y-E Data controller of the Toshiba Libretto), a single drive is always assumed. . Other specialities like disabling the FIFO and not probing the drive at boot-time are selected by per-controller or per-drive flags, too. . Unit attentions (media has been changed) are supposed to be detected now; density autoselection only occurs after a unit attention. (Can be turned off by a per-drive flag, this will cause each Fdopen() to perform the autoselection.) . FM floppies can be handled now (on controllers that actually support it -- not all do these days). . Fdopen() can be told to avoid density selection by setting O_NONBLOCK; this leaves the descriptor in a half-opened state where only a few ioctls are accepted. This is necessary to run fdformat on a device that uses automatic density selection (since you cannot autoselect on an unformatted medium, obviously). . Just differentiate between a plain old NE765 and the enhanced chips, but don't try more; the existing code was wrong and only misdetected the chips anyway. BUGS and TODOs: . All documentation update still needs to be done. . Formatting not-so-standard format yields unpredictable results; i have yet to figure out why this happens. "Standard" formats like 720 and 1440 KB do work, however. . rc scripts are needed to setup device nodes with nonstandard densities (like the old /dev/fdN.MMM we used to have). . Obtaining device flags from the kernel environment doesn't work yet, thus currently only drives that are present in (IA32) CMOS are really detected. Someone who knows the odds and ends about device flags is needed here, i can't figure out what i'm doing wrong. . 2.88 MB still needs to be done.
Diffstat (limited to 'sys')
-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
5 files changed, 1020 insertions, 576 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_ */
OpenPOWER on IntegriCloud