diff --git a/sys/dev/fdc/fdc.c b/sys/dev/fdc/fdc.c index 845f5204a387..04be1bd7df5d 100644 --- a/sys/dev/fdc/fdc.c +++ b/sys/dev/fdc/fdc.c @@ -65,6 +65,7 @@ #include #include #include +#include #include #include #include @@ -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 . 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 }; -#define DRVS_PER_CTLR 2 /* 2 floppies */ +/* + * 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 */ +}; + +/* 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); + fd->flags = FD_UA; /* make sure fdautoselect() will be called */ -#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->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,58 +1228,77 @@ 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); + 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 */ + /* 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); + switch (fd->type) { + case FDT_12M: + device_set_desc(dev, "1200-KB 5.25\" drive"); + fd->type = FDT_12M; + break; + case FDT_144M: + device_set_desc(dev, "1440-KB 3.5\" drive"); + fd->type = FDT_144M; + break; + case FDT_288M: + device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); + fd->type = FDT_288M; + break; + case FDT_360K: + device_set_desc(dev, "360-KB 5.25\" drive"); + fd->type = FDT_360K; + break; + 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; @@ -1271,35 +1306,10 @@ fd_probe(device_t dev) callout_handle_init(&fd->toffhandle); callout_handle_init(&fd->tohandle); - switch (fdt) { - case RTCFDT_12M: - device_set_desc(dev, "1200-KB 5.25\" drive"); - fd->type = FD_1200; - 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: - device_set_desc(dev, "1440-KB 3.5\" drive"); - fd->type = FD_1440; - break; - case RTCFDT_288M: - case RTCFDT_288M_1: - device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); - fd->type = FD_1440; - break; - case RTCFDT_360K: - device_set_desc(dev, "360-KB 5.25\" drive"); - fd->type = FD_360; - break; - case RTCFDT_720K: - printf("720-KB 3.5\" drive"); - fd->type = FD_720; - break; - default: - return (ENXIO); - } + /* 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", @@ -1815,6 +1831,102 @@ fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count) return(1); } +/* + * 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. * @@ -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 5dee70d0ecb4..c08266873185 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 + * 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 845f5204a387..04be1bd7df5d 100644 --- a/sys/isa/fd.c +++ b/sys/isa/fd.c @@ -65,6 +65,7 @@ #include #include #include +#include #include #include #include @@ -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 . 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 }; -#define DRVS_PER_CTLR 2 /* 2 floppies */ +/* + * 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 */ +}; + +/* 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); + fd->flags = FD_UA; /* make sure fdautoselect() will be called */ -#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->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,58 +1228,77 @@ 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); + 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 */ + /* 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); + switch (fd->type) { + case FDT_12M: + device_set_desc(dev, "1200-KB 5.25\" drive"); + fd->type = FDT_12M; + break; + case FDT_144M: + device_set_desc(dev, "1440-KB 3.5\" drive"); + fd->type = FDT_144M; + break; + case FDT_288M: + device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); + fd->type = FDT_288M; + break; + case FDT_360K: + device_set_desc(dev, "360-KB 5.25\" drive"); + fd->type = FDT_360K; + break; + 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; @@ -1271,35 +1306,10 @@ fd_probe(device_t dev) callout_handle_init(&fd->toffhandle); callout_handle_init(&fd->tohandle); - switch (fdt) { - case RTCFDT_12M: - device_set_desc(dev, "1200-KB 5.25\" drive"); - fd->type = FD_1200; - 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: - device_set_desc(dev, "1440-KB 3.5\" drive"); - fd->type = FD_1440; - break; - case RTCFDT_288M: - case RTCFDT_288M_1: - device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); - fd->type = FD_1440; - break; - case RTCFDT_360K: - device_set_desc(dev, "360-KB 5.25\" drive"); - fd->type = FD_360; - break; - case RTCFDT_720K: - printf("720-KB 3.5\" drive"); - fd->type = FD_720; - break; - default: - return (ENXIO); - } + /* 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", @@ -1815,6 +1831,102 @@ fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count) return(1); } +/* + * 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. * @@ -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 5dee70d0ecb4..c08266873185 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 + * 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 b9a86cd17314..48568ff9e6f1 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 ec411d7a48a0..38f9a1ed3bb0 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 diff --git a/usr.sbin/fdcontrol/fdcontrol.c b/usr.sbin/fdcontrol/fdcontrol.c index e1200592a2b5..9d447a5447a6 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 #include #include +#include +#include #include -static int getnumber(void); +#include "fdutil.h" + + +static int debug = -1, format, verbose, show = 1, showfmt; +static char *fmtstring; + +static void showdev(enum fd_drivetype, const char *); static void usage(void); -static int -getnumber(void) -{ - int i; - char b[80]; - - fgets(b, 80, stdin); - if(b[0] == '\n') return -1; - - sscanf(b, " %i", &i); - return i; -} - 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; + enum fd_drivetype type; + struct fd_type ft, newft, *fdtp; + const char *name, *descr; + int fd, i, mode; - while((i = getopt(argc, argv, "d:s")) != -1) - switch(i) - { - case 'd': - debug = atoi(optarg); - settype = 0; - break; + 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 's': - debug = -1; - settype = 1; - break; + case 'F': + showfmt = 1; + show = 0; + break; - case '?': - default: - usage(); - } + case 'f': + if (getnum(optarg, &format)) { + fprintf(stderr, + "Bad argument %s to -f option; must be numeric\n", + optarg); + usage(); + } + show = 0; + break; - argc -= optind; - argv += optind; + case 's': + fmtstring = optarg; + show = 0; + break; - if(argc != 1) - usage(); + case 'v': + verbose++; + break; - if((fd = open(argv[0], 0)) < 0) - { - warn("open(floppy)"); - return 1; - } + default: + usage(); + } - if(debug != -1) - { - if(ioctl(fd, FD_DEBUG, &debug) < 0) - { - warn("ioctl(FD_DEBUG)"); - return 1; - } - return 0; - } + argc -= optind; + argv += optind; - if(settype) - { - if(ioctl(fd, FD_GTYPE, &ft) < 0) - { - warn("ioctl(FD_GTYPE)"); - return 1; + 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); } - 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 (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; } - return 0; - } - return 0; + 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); + } + + if (debug != -1) { + if (ioctl(fd, FD_DEBUG, &debug) == -1) + err(EX_OSERR, "ioctl(FD_DEBUG)"); + return (0); + } + + return 0; } diff --git a/usr.sbin/fdformat/fdformat.c b/usr.sbin/fdformat/fdformat.c index 0c194b3e0817..0e037b4fec0c 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 +#include +#include #include #include @@ -48,24 +38,24 @@ #include #include #include +#include #include -#include - #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) + format = quiet = verify_only = confirm = 0; + verify = 1; + fill = 0xf6; + fmtstring = 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 'f': /* format in kilobytes */ - format = atoi(optarg); + if (getnum(optarg, &format)) { + fprintf(stderr, + "Bad argument %s to -f option; must be numeric\n", + optarg); + usage(); + } break; - case 'c': /* # of cyls */ - cyls = atoi(optarg); - break; - - case 's': /* # of secs per track */ - secs = atoi(optarg); - break; - - case 'h': /* # of heads */ - heads = atoi(optarg); - break; - - case 'r': /* transfer rate, kilobyte/sec */ - rate = atoi(optarg); - break; - - case 'g': /* length of GAP3 to format with */ - gaplen = atoi(optarg); - break; - - case 'S': /* sector size shift factor (1 << S)*128 */ - secsize = atoi(optarg); - break; - - case 'F': /* fill byte, C-like notation allowed */ - fill = (int)strtol(optarg, (char **)0, 0); - break; - - case 't': /* steps per track */ - steps = atoi(optarg); - break; - - case 'i': /* interleave factor */ - intleave = atoi(optarg); - break; - - case 'q': - quiet = 1; - break; - - case 'y': - confirm = 1; - break; - - case 'n': + case 'n': /* don't verify */ verify = 0; break; - case 'v': + case 'q': /* quiet */ + quiet = 1; + break; + + case 's': /* format string with detailed options */ + fmtstring = optarg; + break; + + 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< +#include + #include +#include #include +#include #include - -#include +#include #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 f4993908f047..a09322850897 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 **); +