diff options
author | dg <dg@FreeBSD.org> | 1994-09-17 16:56:10 +0000 |
---|---|---|
committer | dg <dg@FreeBSD.org> | 1994-09-17 16:56:10 +0000 |
commit | 0285e6bafcab4b972c40c1d78ac266295dc5d80f (patch) | |
tree | 04e7afb126a8687648474c1e093925c58ebecf67 | |
parent | 2097dca3f5f4524ecf441dae533fcf5ee46c98c2 (diff) | |
download | FreeBSD-src-0285e6bafcab4b972c40c1d78ac266295dc5d80f.zip FreeBSD-src-0285e6bafcab4b972c40c1d78ac266295dc5d80f.tar.gz |
Updated driver to the 1.1.5 version:
date: 1994/05/22 12:35:38; author: joerg; state: Exp; lines: +6 -6
First round of floppy changes. Try making `fd' more robust.
New features:
. ioctl command for setting the drive type (density etc.); restricted
to the super-user
. ioctl for getting/seting `drive options'; currently only option
is FDOPT_NORETRY: inhibit the usual retries; used when verifying
a newly formatted track
Fixes:
. function prototypes
. made all internal functions `static'
. cleaned up & corrected .h files
. restructured, to make the chaotic function sequence more rational
. compiled with -Wall, and cleared all warnings
. introduced a mirror for the (write-only) `digital output register',
to avoid the current kludge
. device probing completed by seeking/recalibrating, and looking
for track 0 being found
. holding the controller down in reset state while it is idle (and
thus saving allot of headaches)
. make requests fail that are not a multiple of the (physical)
sector size
. removed the fixed physical sector size (512 bytes), allowing for any
size the controller could handle (128/256/512/1024 bytes)
. replaced some silly messages
. fixed the TRACE* macro usage, debugging reports should be complete
now again (debugging output is HUGE! though)
. removed fd_timeout for SEEK command; seeks are always reported by
the controller to succeed, since the `success' only refers to the
controller's idea of success - there is no hardware line to tell about
the seek end (other than the `track 0' line)
. catch SENSEI's that report about a `terminated due to READY changed'
status - could happen after a controller reset
. converted ``hz / <something>'' divide operations to divisors that are
powers of two, so gcc can optimize them into shifts
. write/format operations are checked against a write-protected medium
now *prior* starting the operation
. error reports of `invalid command' and `wrong cylinder' will cause
shortcuts in the retrier() now
. fixed a bug in the retrier() causing bogus block numbers to be reported
. fdformat() does care for errors now
Known Bugs:
. no attempts have been made (yet) to improve the performance
. sometimes, bogus ``seek/recalib failed'' messages are logged; this
is still a bug in the driver, but it's not harmful since it's
usually caught by the retrier()
Reviewed by:
Submitted by:
Obtained from:
-rw-r--r-- | sys/dev/fdc/fdc.c | 782 | ||||
-rw-r--r-- | sys/dev/fdc/fdcreg.h | 20 | ||||
-rw-r--r-- | sys/dev/ic/nec765.h | 72 | ||||
-rw-r--r-- | sys/i386/include/ioctl_fd.h | 31 | ||||
-rw-r--r-- | sys/i386/isa/fd.c | 782 | ||||
-rw-r--r-- | sys/i386/isa/fdc.h | 12 | ||||
-rw-r--r-- | sys/i386/isa/fdreg.h | 20 | ||||
-rw-r--r-- | sys/i386/isa/ic/nec765.h | 72 | ||||
-rw-r--r-- | sys/isa/fd.c | 782 | ||||
-rw-r--r-- | sys/isa/fdc.h | 12 | ||||
-rw-r--r-- | sys/isa/fdreg.h | 20 | ||||
-rw-r--r-- | sys/isa/ic/nec765.h | 72 | ||||
-rw-r--r-- | sys/sys/fdcio.h | 31 |
13 files changed, 1745 insertions, 963 deletions
diff --git a/sys/dev/fdc/fdc.c b/sys/dev/fdc/fdc.c index 8dd7314..a7d7f88 100644 --- a/sys/dev/fdc/fdc.c +++ b/sys/dev/fdc/fdc.c @@ -1,4 +1,3 @@ -/*#define DEBUG 1*/ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. @@ -6,6 +5,12 @@ * This code is derived from software contributed to Berkeley by * Don Ahn. * + * Portions Copyright (c) 1993, 1994 by + * jc@irbs.UUCP (John Capo) + * vak@zebub.msk.su (Serge Vakulenko) + * ache@astral.msk.su (Andrew A. Chernov) + * joerg_wunsch@uriah.sax.de (Joerg Wunsch) + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -35,7 +40,7 @@ * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fd.c,v 1.28 1994/08/23 07:52:09 paul Exp $ + * $Id: fd.c,v 1.29 1994/08/29 21:32:31 ache Exp $ * */ @@ -59,30 +64,30 @@ #include <sys/buf.h> #include <sys/uio.h> #include <sys/malloc.h> +#include <sys/proc.h> #include <sys/syslog.h> #include <i386/isa/isa.h> #include <i386/isa/isa_device.h> #include <i386/isa/fdreg.h> #include <i386/isa/fdc.h> -#include <i386/isa/icu.h> #include <i386/isa/rtc.h> -#if NFT > 0 -extern int ftopen(), ftintr(), ftattach(), ftclose(), ftioctl(); -#endif - #define RAW_PART 2 - #define b_cylin b_resid -#define FDBLK 512 /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX +/* + * this biotab field doubles as a field for the physical unit number + * on the controller + */ +#define id_physid id_scsiid + #define NUMTYPES 14 #define NUMDENS (NUMTYPES - 6) -/* This defines (-1) must match index for fd_types */ +/* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 @@ -134,16 +139,17 @@ struct fdc_data fdc_data[NFDC]; struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ - int type; /* Drive type (HD, DD */ + int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ 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 */ - int skip; - int hddrv; - int track; /* where we think the head is */ + int skip; + int hddrv; + int track; /* where we think the head is */ + int options; /* user configurable options, see ioctl_fd.h */ } fd_data[NFD]; /***********************************************************************\ @@ -155,10 +161,48 @@ struct fd_data { * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ -#define id_physid id_scsiid /* this biotab field doubles as a field */ - /* for the physical unit number on the controller */ +#if NFT > 0 +int ftopen(dev_t, int); +int ftintr(ftu_t ftu); +int ftclose(dev_t, int); +void ftstrategy(struct buf *); +int ftioctl(dev_t, int, caddr_t, int, struct proc *); +int ftdump(dev_t); +int ftsize(dev_t); +int ftattach(struct isa_device *, struct isa_device *); +#endif +/* autoconfig functions */ +static int fdprobe(struct isa_device *); +static int fdattach(struct isa_device *); + +/* exported functions */ +int fdsize (dev_t); +void fdintr(fdcu_t); +int Fdopen(dev_t, int); +int fdclose(dev_t, int); +void fdstrategy(struct buf *); +int fdioctl(dev_t, int, caddr_t, int, struct proc *); + +/* needed for ft driver, thus exported */ +int in_fdc(fdcu_t); +int out_fdc(fdcu_t, int); + +/* internal functions */ +static void set_motor(fdcu_t, int, int); +# define TURNON 1 +# define TURNOFF 0 +static timeout_t fd_turnoff; +static timeout_t fd_motor_on; +static void fd_turnon(fdu_t); +static void fdc_reset(fdc_p); +static void fdstart(fdcu_t); +static timeout_t fd_timeout; +static timeout_t fd_pseudointr; +static int fdstate(fdcu_t, fdc_p); static int retrier(fdcu_t); +static int fdformat(dev_t, struct fd_formb *, struct proc *); + #define DEVIDLE 0 #define FINDWORK 1 @@ -190,25 +234,18 @@ char *fdstates[] = "IOTIMEDOUT" }; - -int fd_debug = 1; +/* CAUTION: fd_debug causes huge amounts of logging output */ +int fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) -#define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2) +#define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* DEBUG */ #define TRACE0(arg) -#define TRACE1(arg1,arg2) +#define TRACE1(arg1, arg2) #endif /* DEBUG */ -static void fdstart(fdcu_t); -void fdintr(fdcu_t); -static timeout_t fd_turnoff; - /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ -static int fdprobe(struct isa_device *); -static int fdattach(struct isa_device *); - struct isa_driver fdcdriver = { fdprobe, fdattach, "fdc", }; @@ -216,56 +253,55 @@ struct isa_driver fdcdriver = { /* * probe for existance of controller */ -int +static int fdprobe(dev) struct isa_device *dev; { fdcu_t fdcu = dev->id_unit; if(fdc_data[fdcu].flags & FDC_ATTACHED) { - printf("fdc: same unit (%d) used multiple times\n",fdcu); + printf("fdc: same unit (%d) used multiple times\n", fdcu); return 0; } fdc_data[fdcu].baseport = dev->id_iobase; /* First - lets reset the floppy controller */ - - outb(dev->id_iobase+fdout,0); + outb(dev->id_iobase+FDOUT, 0); DELAY(100); - outb(dev->id_iobase+fdout,FDO_FRST); + outb(dev->id_iobase+FDOUT, FDO_FRST); /* see if it can handle a command */ - if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0) + if (out_fdc(fdcu, NE7CMD_SPECIFY) < 0) { return(0); } - out_fdc(fdcu,0xDF); - out_fdc(fdcu,2); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); return (IO_FDCSIZE); } /* * wire controller into system, look for floppy units */ -int +static int fdattach(dev) struct isa_device *dev; { - unsigned fdt,st0, cyl; - int hdr; + unsigned fdt; fdu_t fdu; fdcu_t fdcu = dev->id_unit; fdc_p fdc = fdc_data + fdcu; fd_p fd; - int fdsu; + int fdsu, st0; struct isa_device *fdup; fdc->fdcu = fdcu; fdc->flags |= FDC_ATTACHED; fdc->dmachan = dev->id_drq; fdc->state = DEVIDLE; - hdr = 0; + /* reset controller, turn motor off, clear fdout mirror reg */ + outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); printf("fdc%d:", fdcu); /* check for each floppy drive */ @@ -305,25 +341,33 @@ fdattach(dev) continue; } -#ifdef notyet /* select it */ - fd_turnon1(fdu); - spinwait(1000); /* 1 sec */ - out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ - out_fdc(fdcu,fdsu); + set_motor(fdcu, fdsu, TURNON); spinwait(1000); /* 1 sec */ + out_fdc(fdcu, NE7CMD_SEEK); /* seek some steps... */ + out_fdc(fdcu, fdsu); + out_fdc(fdcu, 10); + spinwait(300); /* ...wait a moment... */ + out_fdc(fdcu, NE7CMD_SENSEI); /* make controller happy */ + (void)in_fdc(fdcu); + (void)in_fdc(fdcu); + out_fdc(fdcu, NE7CMD_RECAL); /* ...and go back to 0 */ + out_fdc(fdcu, fdsu); + spinwait(1000); /* a second be enough for full stroke seek */ /* anything responding */ - out_fdc(fdcu,NE7CMD_SENSEI); + out_fdc(fdcu, NE7CMD_SENSEI); st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - if (st0 & 0xd0) + (void)in_fdc(fdcu); + set_motor(fdcu, fdsu, TURNOFF); + + if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ continue; -#endif fd->track = -2; fd->fdc = fdc; fd->fdsu = fdsu; + fd->options = 0; printf(" [%d: fd%d: ", fdsu, fdu); switch (fdt) { @@ -348,15 +392,10 @@ fdattach(dev) fd->type = NO_TYPE; break; } - - fd_turnoff((caddr_t)fdu); - hdr = 1; } printf("\n"); - /* Set transfer to 500kbps */ - outb(fdc->baseport+fdctl,0); /*XXX*/ - return 1; + return (1); } int @@ -367,103 +406,47 @@ fdsize(dev) } /****************************************************************************/ -/* fdstrategy */ -/****************************************************************************/ -void fdstrategy(struct buf *bp) -{ - register struct buf *dp,*dp0,*dp1; - long nblocks,blknum; - int s; - fdcu_t fdcu; - fdu_t fdu; - fdc_p fdc; - fd_p fd; - - fdu = FDUNIT(minor(bp->b_dev)); - fd = &fd_data[fdu]; - fdc = fd->fdc; - fdcu = fdc->fdcu; - -#if NFT > 0 - /* check for controller already busy with tape */ - if (fdc->flags & FDC_TAPE_BUSY) { - bp->b_error = EBUSY; - bp->b_flags |= B_ERROR; - return; - } -#endif - if ((fdu >= NFD) || (bp->b_blkno < 0)) { - printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n", - fdu, bp->b_blkno, bp->b_bcount); - pg("fd:error in fdstrategy"); - bp->b_error = EINVAL; - bp->b_flags |= B_ERROR; - goto bad; - } - /* - * Set up block calculations. - */ - blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK; - nblocks = fd->ft->size; - if (blknum + (bp->b_bcount / FDBLK) > nblocks) { - if (blknum == nblocks) { - bp->b_resid = bp->b_bcount; - } else { - bp->b_error = ENOSPC; - bp->b_flags |= B_ERROR; - } - goto bad; - } - bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); - bp->b_pblkno = bp->b_blkno; - dp = &(fdc->head); - s = splbio(); - disksort(dp, bp); - untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */ - fdstart(fdcu); - splx(s); - return; - -bad: - biodone(bp); - return; -} - -/****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ -void -set_motor(fdcu, fdu, reset) +static void +set_motor(fdcu, fdsu, turnon) fdcu_t fdcu; - fdu_t fdu; - int reset; + int fdsu; + int turnon; { - int m0,m1; - int selunit; - fd_p fd; - if(fd = fdc_data[fdcu].fd)/* yes an assign! */ - { - selunit = fd->fdsu; + int fdout = fdc_data[fdcu].fdout; + int needspecify = 0; + + if(turnon) { + fdout &= ~FDO_FDSEL; + fdout |= (FDO_MOEN0 << fdsu) + fdsu; + } else + fdout &= ~(FDO_MOEN0 << fdsu); + + if(!turnon + && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) + /* gonna turn off the last drive, put FDC to bed */ + fdout &= ~ (FDO_FRST|FDO_FDMAEN); + else { + /* make sure controller is selected and specified */ + if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) + needspecify = 1; + fdout |= (FDO_FRST|FDO_FDMAEN); } - else - { - selunit = 0; + + outb(fdc_data[fdcu].baseport+FDOUT, fdout); + fdc_data[fdcu].fdout = fdout; + TRACE1("[0x%x->FDOUT]", fdout); + + if(needspecify) { + out_fdc(fdcu, NE7CMD_SPECIFY); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); } - m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR; - m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR; - outb(fdc_data[fdcu].baseport+fdout, - selunit - | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) - | (m0 ? FDO_MOEN0 : 0) - | (m1 ? FDO_MOEN1 : 0)); - TRACE1("[0x%x->fdout]",( - selunit - | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) - | (m0 ? FDO_MOEN0 : 0) - | (m1 ? FDO_MOEN1 : 0))); } +/* ARGSUSED */ static void fd_turnoff(void *arg1) { @@ -473,11 +456,12 @@ fd_turnoff(void *arg1) fd_p fd = fd_data + fdu; s = splbio(); fd->flags &= ~FD_MOTOR; - set_motor(fd->fdc->fdcu,fd->fdsu,0); + set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF); splx(s); } -void +/* ARGSUSED */ +static void fd_motor_on(void *arg1) { fdu_t fdu = (fdu_t)arg1; @@ -493,27 +477,39 @@ fd_motor_on(void *arg1) splx(s); } -static void fd_turnon1(fdu_t); - -void +static void fd_turnon(fdu) fdu_t fdu; { fd_p fd = fd_data + fdu; if(!(fd->flags & FD_MOTOR)) { - fd_turnon1(fdu); - fd->flags |= FD_MOTOR_WAIT; + fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); + set_motor(fd->fdc->fdcu, fd->fdsu, TURNON); timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */ } } static void -fd_turnon1(fdu_t fdu) +fdc_reset(fdc) + fdc_p fdc; { - fd_p fd = fd_data + fdu; - fd->flags |= FD_MOTOR; - set_motor(fd->fdc->fdcu,fd->fdsu,0); + fdcu_t fdcu = fdc->fdcu; + + /* Try a reset, keep motor on */ + outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); + TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); + DELAY(100); + /* enable FDC, but defer interrupts a moment */ + outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN); + TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); + DELAY(100); + outb(fdc->baseport + FDOUT, fdc->fdout); + TRACE1("[0x%x->FDOUT]", fdc->fdout); + + out_fdc(fdcu, NE7CMD_SPECIFY); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); } /****************************************************************************/ @@ -525,17 +521,17 @@ in_fdc(fdcu) { int baseport = fdc_data[fdcu].baseport; int i, j = 100000; - while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM)) + while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return -1; if (j <= 0) return(-1); #ifdef DEBUG - i = inb(baseport+fddata); - TRACE1("[fddata->0x%x]",(unsigned char)i); + i = inb(baseport+FDDATA); + TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else - return inb(baseport+fddata); + return inb(baseport+FDDATA); #endif } @@ -549,17 +545,17 @@ out_fdc(fdcu, x) /* Check that the direction bit is set */ i = 100000; - while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0); + while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0); if (i <= 0) return (-1); /* Floppy timed out */ /* Check that the floppy controller is ready for a command */ i = 100000; - while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0); + while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) return (-1); /* Floppy timed out */ /* Send the command and return */ - outb(baseport+fddata,x); - TRACE1("[0x%x->fddata]",x); + outb(baseport+FDDATA, x); + TRACE1("[0x%x->FDDATA]", x); return (0); } @@ -650,17 +646,97 @@ fdclose(dev, flags) int flags; { fdu_t fdu = FDUNIT(minor(dev)); - int type = FDTYPE(minor(dev)); #if NFT > 0 + int type = FDTYPE(minor(dev)); + if (type & F_TAPE_TYPE) - return ftclose(0); + return ftclose(dev, flags); #endif fd_data[fdu].flags &= ~FD_OPEN; + fd_data[fdu].options &= ~FDOPT_NORETRY; return(0); } +/****************************************************************************/ +/* fdstrategy */ +/****************************************************************************/ +void +fdstrategy(struct buf *bp) +{ + register struct buf *dp; + long nblocks, blknum; + int s; + fdcu_t fdcu; + fdu_t fdu; + fdc_p fdc; + fd_p fd; + size_t fdblk; + + fdu = FDUNIT(minor(bp->b_dev)); + fd = &fd_data[fdu]; + fdc = fd->fdc; + fdcu = fdc->fdcu; + fdblk = 128 << (fd->ft->secsize); + +#if NFT > 0 + if (FDTYPE(minor(bp->b_dev)) & F_TAPE_TYPE) { + /* ft tapes do not (yet) support strategy i/o */ + bp->b_error = ENXIO; + bp->b_flags |= B_ERROR; + goto bad; + } + /* check for controller already busy with tape */ + if (fdc->flags & FDC_TAPE_BUSY) { + bp->b_error = EBUSY; + bp->b_flags |= B_ERROR; + goto bad; + } +#endif + if (!(bp->b_flags & B_FORMAT)) { + if ((fdu >= NFD) || (bp->b_blkno < 0)) { + printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n", + fdu, bp->b_blkno, bp->b_bcount); + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto bad; + } + if ((bp->b_bcount % fdblk) != 0) { + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto bad; + } + } + + /* + * Set up block calculations. + */ + blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/fdblk; + nblocks = fd->ft->size; + if (blknum + (bp->b_bcount / fdblk) > nblocks) { + if (blknum == nblocks) { + bp->b_resid = bp->b_bcount; + } else { + bp->b_error = ENOSPC; + bp->b_flags |= B_ERROR; + } + goto bad; + } + bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); + dp = &(fdc->head); + s = splbio(); + disksort(dp, bp); + untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */ + fdstart(fdcu); + splx(s); + return; + +bad: + biodone(bp); + return; +} + /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * @@ -674,9 +750,7 @@ static void fdstart(fdcu) fdcu_t fdcu; { - register struct buf *dp,*bp; int s; - fdu_t fdu; s = splbio(); if(fdc_data[fdcu].state == DEVIDLE) @@ -686,38 +760,43 @@ fdstart(fdcu) splx(s); } +/* ARGSUSED */ static void fd_timeout(void *arg1) { fdcu_t fdcu = (fdcu_t)arg1; fdu_t fdu = fdc_data[fdcu].fdu; - int st0, st3, cyl; - struct buf *dp,*bp; - int s; + int baseport = fdc_data[fdcu].baseport; + struct buf *dp, *bp; + int s; dp = &fdc_data[fdcu].head; - s = splbio(); bp = dp->b_actf; - out_fdc(fdcu,NE7CMD_SENSED); - out_fdc(fdcu,fd_data[fdu].hddrv); - st3 = in_fdc(fdcu); - - out_fdc(fdcu,NE7CMD_SENSEI); - st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n", - fdu, - st0, - NE7_ST0BITS, - cyl, - st3, - NE7_ST3BITS); + /* + * Due to IBM's brain-dead design, the FDC has a faked ready + * signal, hardwired to ready == true. Thus, any command + * issued if there's no diskette in the drive will _never_ + * complete, and must be aborted by resetting the FDC. + * Many thanks, Big Blue! + */ + + s = splbio(); + + TRACE1("fd%d[fd_timeout()]", fdu); + /* See if the controller is still busy (patiently awaiting data) */ + if(((inb(baseport + FDSTS)) & (NE7_CB|NE7_RQM)) == NE7_CB) + { + TRACE1("[FDSTS->0x%x]", inb(baseport + FDSTS)); + /* yup, it is; kill it now */ + fdc_reset(&fdc_data[fdcu]); + printf("fd%d: Operation timeout\n", fdu); + } if (bp) { retrier(fdcu); - fdc_data[fdcu].status[0] = 0xc0; + fdc_data[fdcu].status[0] = NE7_ST0_IC_RC; fdc_data[fdcu].state = IOTIMEDOUT; if( fdc_data[fdcu].retry < 6) fdc_data[fdcu].retry = 6; @@ -733,11 +812,13 @@ fd_timeout(void *arg1) } /* just ensure it has the right spl */ +/* ARGSUSED */ static void fd_pseudointr(void *arg1) { fdcu_t fdcu = (fdcu_t)arg1; int s; + s = splbio(); fdintr(fdcu); splx(s); @@ -759,25 +840,26 @@ fdintr(fdcu_t fdcu) (ftintr(fdu)); else #endif - while(fdstate(fdcu, fdc)) - ; + while(fdstate(fdcu, fdc)) + ; } /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ -int +static int fdstate(fdcu, fdc) fdcu_t fdcu; fdc_p fdc; { - int read, format, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0; + int read, format, head, sec = 0, i = 0, sectrac, st0, cyl, st3; unsigned long blknum; fdu_t fdu = fdc->fdu; fd_p fd; - register struct buf *dp,*bp; + register struct buf *dp, *bp; struct fd_formb *finfo = NULL; + size_t fdblk; dp = &(fdc->head); bp = dp->b_actf; @@ -790,16 +872,17 @@ fdstate(fdcu, fdc) fdc->state = DEVIDLE; if(fdc->fd) { - printf("unexpected valid fd pointer (fdu = %d)\n" - ,fdc->fdu); + printf("unexpected valid fd pointer (fdu = %d)\n", + fdc->fdu); fdc->fd = (fd_p) 0; fdc->fdu = -1; } - TRACE1("[fdc%d IDLE]",fdcu); + TRACE1("[fdc%d IDLE]", fdcu); return(0); } fdu = FDUNIT(minor(bp->b_dev)); fd = fd_data + fdu; + fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) { printf("confused fd pointers\n"); @@ -808,9 +891,9 @@ fdstate(fdcu, fdc) format = bp->b_flags & B_FORMAT; if(format) finfo = (struct fd_formb *)bp->b_un.b_addr; - TRACE1("fd%d",fdu); - TRACE1("[%s]",fdstates[fdc->state]); - TRACE1("(0x%x)",fd->flags); + TRACE1("fd%d", fdu); + TRACE1("[%s]", fdstates[fdc->state]); + TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, (caddr_t)fdu); timeout(fd_turnoff, (caddr_t)fdu, 4 * hz); switch (fdc->state) @@ -821,7 +904,8 @@ fdstate(fdcu, fdc) fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; - outb(fdc->baseport+fdctl, fd->ft->trans); + outb(fdc->baseport+FDCTL, fd->ft->trans); + TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in it's own good time * @@ -842,7 +926,7 @@ fdstate(fdcu, fdc) } else /* at least make sure we are selected */ { - set_motor(fdcu,fd->fdsu,0); + set_motor(fdcu, fd->fdsu, TURNON); } fdc->state = DOSEEK; break; @@ -852,33 +936,55 @@ fdstate(fdcu, fdc) fdc->state = SEEKCOMPLETE; break; } - out_fdc(fdcu,NE7CMD_SEEK); /* Seek function */ - out_fdc(fdcu,fd->fdsu); /* Drive number */ - out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac); + out_fdc(fdcu, NE7CMD_SEEK); /* Seek function */ + out_fdc(fdcu, fd->fdsu); /* Drive number */ + out_fdc(fdcu, bp->b_cylin * fd->ft->steptrac); fd->track = -2; fdc->state = SEEKWAIT; - timeout(fd_timeout, (caddr_t)fdcu, 2 * hz); return(0); /* will return later */ case SEEKWAIT: - untimeout(fd_timeout, (caddr_t)fdcu); /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 50); + timeout(fd_pseudointr, (caddr_t)fdcu, hz / 32); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ - break; - case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == -2) { int descyl = bp->b_cylin * fd->ft->steptrac; - out_fdc(fdcu,NE7CMD_SENSEI); - i = in_fdc(fdcu); - cyl = in_fdc(fdcu); + do { + out_fdc(fdcu, NE7CMD_SENSEI); + st0 = in_fdc(fdcu); + cyl = in_fdc(fdcu); + /* + * if this was a "ready changed" interrupt, + * fetch status again (can happen after + * enabling controller from reset state) + */ + } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); + if (0 == descyl) + { + /* + * seek to cyl 0 requested; make sure we are + * really there + */ + out_fdc(fdcu, NE7CMD_SENSED); + out_fdc(fdcu, fdu); + st3 = in_fdc(fdcu); + if ((st3 & NE7_ST3_T0) == 0) { + printf( + "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", + fdu, st3, NE7_ST3BITS); + if(fdc->retry < 3) + fdc->retry = 3; + return(retrier(fdcu)); + } + } if (cyl != descyl) { - printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", - fdu, descyl, cyl, i, NE7_ST0BITS); + printf( + "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", + fdu, descyl, cyl, st0, NE7_ST0BITS); return(retrier(fdcu)); } } @@ -888,46 +994,73 @@ fdstate(fdcu, fdc) fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip, - format ? bp->b_bcount : FDBLK, fdc->dmachan); - blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK - + fd->skip/FDBLK; + format ? bp->b_bcount : fdblk, fdc->dmachan); + blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + + fd->skip/fdblk; sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; -/*XXX*/ fd->hddrv = ((head&1)<<2)+fdu; + fd->hddrv = ((head&1)<<2)+fdu; + + if(format || !read) + { + /* make sure the drive is writable */ + out_fdc(fdcu, NE7CMD_SENSED); + out_fdc(fdcu, fdu); + st3 = in_fdc(fdcu); + if(st3 & NE7_ST3_WP) + { + /* + * XXX YES! this is ugly. + * in order to force the current operation + * to fail, we will have to fake an FDC + * error - all error handling is done + * by the retrier() + */ + fdc->status[0] = NE7_ST0_IC_AT; + fdc->status[1] = NE7_ST1_NW; + fdc->status[2] = 0; + fdc->status[3] = fd->track; + fdc->status[4] = head; + fdc->status[5] = sec; + fdc->retry = 8; /* break out immediately */ + fdc->state = IOTIMEDOUT; /* not really... */ + return (1); + } + } if(format) { /* formatting */ - out_fdc(fdcu,/* NE7CMD_FORMAT */ 0x4d); - out_fdc(fdcu,head << 2 | fdu); - out_fdc(fdcu,finfo->fd_formb_secshift); - out_fdc(fdcu,finfo->fd_formb_nsecs); - out_fdc(fdcu,finfo->fd_formb_gaplen); - out_fdc(fdcu,finfo->fd_formb_fillbyte); + out_fdc(fdcu, NE7CMD_FORMAT); + out_fdc(fdcu, head << 2 | fdu); + out_fdc(fdcu, finfo->fd_formb_secshift); + out_fdc(fdcu, finfo->fd_formb_nsecs); + out_fdc(fdcu, finfo->fd_formb_gaplen); + out_fdc(fdcu, finfo->fd_formb_fillbyte); } else { if (read) { - out_fdc(fdcu,NE7CMD_READ); /* READ */ + out_fdc(fdcu, NE7CMD_READ); /* READ */ } else { - out_fdc(fdcu,NE7CMD_WRITE); /* WRITE */ + out_fdc(fdcu, NE7CMD_WRITE); /* WRITE */ } - out_fdc(fdcu,head << 2 | fdu); /* head & unit */ - out_fdc(fdcu,fd->track); /* track */ - out_fdc(fdcu,head); - out_fdc(fdcu,sec); /* sector XXX +1? */ - out_fdc(fdcu,fd->ft->secsize); /* sector size */ - out_fdc(fdcu,sectrac); /* sectors/track */ - out_fdc(fdcu,fd->ft->gap); /* gap size */ - out_fdc(fdcu,fd->ft->datalen); /* data length */ + out_fdc(fdcu, head << 2 | fdu); /* head & unit */ + out_fdc(fdcu, fd->track); /* track */ + out_fdc(fdcu, head); + out_fdc(fdcu, sec); /* sector XXX +1? */ + out_fdc(fdcu, fd->ft->secsize); /* sector size */ + out_fdc(fdcu, sectrac); /* sectors/track */ + out_fdc(fdcu, fd->ft->gap); /* gap size */ + out_fdc(fdcu, fd->ft->datalen); /* data length */ } fdc->state = IOCOMPLETE; - timeout(fd_timeout, (caddr_t)fdcu, 2 * hz); + timeout(fd_timeout, (caddr_t)fdcu, hz); return(0); /* will return later */ case IOCOMPLETE: /* IO DONE, post-analyze */ untimeout(fd_timeout, (caddr_t)fdcu); @@ -935,30 +1068,43 @@ fdstate(fdcu, fdc) { fdc->status[i] = in_fdc(fdcu); } - case IOTIMEDOUT: /*XXX*/ + fdc->state = IOTIMEDOUT; + /* FALLTHROUGH */ + case IOTIMEDOUT: isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip, - format ? bp->b_bcount : FDBLK, fdc->dmachan); - if (fdc->status[0]&0xF8) + format ? bp->b_bcount : fdblk, fdc->dmachan); + if (fdc->status[0] & NE7_ST0_IC) { - if (fdc->status[1] & 0x10) { + if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT + && fdc->status[1] & NE7_ST1_OR) { /* - * Operation not completed in reasonable time. - * Just restart it, don't increment retry count. - * (vak) + * DMA overrun. Someone hogged the bus + * and didn't release it in time for the + * next FDC transfer. + * Just restart it, don't increment retry + * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } + else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV + && fdc->retry < 6) + fdc->retry = 6; /* force a reset */ + else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT + && fdc->status[2] & NE7_ST2_WC + && fdc->retry < 3) + fdc->retry = 3; /* force recalibrate */ return(retrier(fdcu)); } /* All OK */ - fd->skip += FDBLK; + fd->skip += fdblk; if (!format && fd->skip < bp->b_bcount) { /* set up next transfer */ - blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK - + fd->skip/FDBLK; - bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads)); + blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + + fd->skip/fdblk; + bp->b_cylin = + (blknum / (fd->ft->sectrac * fd->ft->heads)); fdc->state = DOSEEK; } else @@ -974,68 +1120,76 @@ fdstate(fdcu, fdc) } return(1); case RESETCTLR: - /* Try a reset, keep motor on */ - set_motor(fdcu,fd->fdsu,1); - DELAY(100); - set_motor(fdcu,fd->fdsu,0); - outb(fdc->baseport+fdctl,fd->ft->trans); - TRACE1("[0x%x->fdctl]",fd->ft->trans); + fdc_reset(fdc); fdc->retry++; fdc->state = STARTRECAL; break; case STARTRECAL: - out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */ - out_fdc(fdcu,0xDF); - out_fdc(fdcu,2); - out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ - out_fdc(fdcu,fdu); + out_fdc(fdcu, NE7CMD_RECAL); /* Recalibrate Function */ + out_fdc(fdcu, fdu); fdc->state = RECALWAIT; return(0); /* will return later */ case RECALWAIT: /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 30); + timeout(fd_pseudointr, (caddr_t)fdcu, hz / 32); fdc->state = RECALCOMPLETE; return(0); /* will return later */ case RECALCOMPLETE: - out_fdc(fdcu,NE7CMD_SENSEI); - st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - if (cyl != 0) + do { + out_fdc(fdcu, NE7CMD_SENSEI); + st0 = in_fdc(fdcu); + cyl = in_fdc(fdcu); + /* + * if this was a "ready changed" interrupt, + * fetch status again (can happen after + * enabling controller from reset state) + */ + } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); + if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); + if(fdc->retry < 3) fdc->retry = 3; return(retrier(fdcu)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return(1); /* will return immediatly */ - case MOTORWAIT: + case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return(0); /* time's not up yet */ } - fdc->state = DOSEEK; + /* + * since the controller was off, it has lost its + * idea about the current track it were; thus, + * recalibrate the bastard + */ + fdc->state = STARTRECAL; return(1); /* will return immediatly */ default: printf("Unexpected FD int->"); - out_fdc(fdcu,NE7CMD_SENSEI); + out_fdc(fdcu, NE7CMD_SENSEI); st0 = in_fdc(fdcu); cyl = in_fdc(fdcu); - printf("ST0 = %lx, PCN = %lx\n",i,sec); - out_fdc(fdcu,0x4A); - out_fdc(fdcu,fd->fdsu); + printf("ST0 = %x, PCN = %x\n", st0, cyl); + out_fdc(fdcu, NE7CMD_READID); + out_fdc(fdcu, fd->fdsu); for(i=0;i<7;i++) { fdc->status[i] = in_fdc(fdcu); } - printf("intr status :%lx %lx %lx %lx %lx %lx %lx ", - fdc->status[0], - fdc->status[1], - fdc->status[2], - fdc->status[3], - fdc->status[4], - fdc->status[5], - fdc->status[6] ); + if(fdc->status[0] != -1) + printf("intr status :%lx %lx %lx %lx %lx %lx %lx\n", + fdc->status[0], + fdc->status[1], + fdc->status[2], + fdc->status[3], + fdc->status[4], + fdc->status[5], + fdc->status[6] ); + else + printf("FDC timed out\n"); return(0); } return(1); /* Come back immediatly to new state */ @@ -1046,11 +1200,13 @@ retrier(fdcu) fdcu_t fdcu; { fdc_p fdc = fdc_data + fdcu; - register struct buf *dp,*bp; + register struct buf *dp, *bp; dp = &(fdc->head); bp = dp->b_actf; + if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY) + goto fail; switch(fdc->retry) { case 0: case 1: case 2: @@ -1065,12 +1221,15 @@ retrier(fdcu) case 7: break; default: + fail: { dev_t sav_b_dev = bp->b_dev; /* Trick diskerr */ - bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); + bp->b_dev = makedev(major(bp->b_dev), + (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); diskerr(bp, "fd", "hard error", LOG_PRINTF, - fdc->fd->skip, (struct disklabel *)NULL); + fdc->fd->skip / DEV_BSIZE, + (struct disklabel *)NULL); bp->b_dev = sav_b_dev; printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS); printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS); @@ -1105,9 +1264,11 @@ fdformat(dev, finfo, p) struct buf *bp; int rv = 0, s; - + size_t fdblk; + fdu = FDUNIT(minor(dev)); fd = &fd_data[fdu]; + fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); @@ -1123,7 +1284,7 @@ fdformat(dev, finfo, p) * seek to the requested cylinder */ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) - + finfo->head * fd->ft->sectrac) * FDBLK / DEV_BSIZE; + + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->b_un.b_addr = (caddr_t)finfo; @@ -1142,41 +1303,39 @@ fdformat(dev, finfo, p) splx(s); if(rv == EWOULDBLOCK) - { /* timed out */ - biodone(bp); rv = EIO; - } + if(bp->b_flags & B_ERROR) + rv = bp->b_error; + biodone(bp); free(bp, M_TEMP); return rv; } /* - * fdioctl() from jc@irbs.UUCP (John Capo) - * i386/i386/conf.c needs to have fdioctl() declared and remove the line that - * defines fdioctl to be enxio. * - * TODO: Reformat. - * Think about allocating buffer off stack. + * TODO: Think about allocating buffer off stack. * Don't pass uncast 0's and NULL's to read/write/setdisklabel(). * Watch out for NetBSD's different *disklabel() interface. * - * Added functionality for floppy formatting - * joerg_wunsch@uriah.sax.de (Joerg Wunsch) */ int -fdioctl (dev, cmd, addr, flag, p) +fdioctl(dev, cmd, addr, flag, p) dev_t dev; int cmd; caddr_t addr; int flag; struct proc *p; { + fdu_t fdu = FDUNIT(minor(dev)); + fd_p fd = &fd_data[fdu]; + size_t fdblk; + struct fd_type *fdt; struct disklabel *dl; char buffer[DEV_BSIZE]; - int error; + int error = 0; #if NFT > 0 int type = FDTYPE(minor(dev)); @@ -1186,14 +1345,14 @@ fdioctl (dev, cmd, addr, flag, p) return ftioctl(dev, cmd, addr, flag, p); #endif - error = 0; + fdblk = 128 << fd->ft->secsize; switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; - dl->d_secsize = FDBLK; + dl->d_secsize = fdblk; fdt = fd_data[FDUNIT(minor(dev))].ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; @@ -1225,12 +1384,12 @@ fdioctl (dev, cmd, addr, flag, p) dl = (struct disklabel *)addr; - if (error = setdisklabel ((struct disklabel *)buffer, - dl, 0, NULL)) + if ((error = + setdisklabel ((struct disklabel *)buffer, dl, 0, NULL))) break; error = writedisklabel(dev, fdstrategy, - (struct disklabel *)buffer, NULL); + (struct disklabel *)buffer, NULL); break; case FD_FORM: @@ -1247,11 +1406,42 @@ fdioctl (dev, cmd, addr, flag, p) *(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft; break; + case FD_STYPE: /* set drive type */ + /* this is considered harmful; only allow for superuser */ + if(suser(p->p_ucred, &p->p_acflag) != 0) + return EPERM; + *fd_data[FDUNIT(minor(dev))].ft = *(struct fd_type *)addr; + break; + + case FD_GOPTS: /* get drive options */ + *(int *)addr = fd_data[FDUNIT(minor(dev))].options; + break; + + case FD_SOPTS: /* set drive options */ + fd_data[FDUNIT(minor(dev))].options = *(int *)addr; + break; + default: - error = EINVAL; + error = ENOTTY; break; } return (error); } #endif +/* + * Hello emacs, these are the + * Local Variables: + * c-indent-level: 8 + * c-continued-statement-offset: 8 + * c-continued-brace-offset: 0 + * c-brace-offset: -8 + * c-brace-imaginary-offset: 0 + * c-argdecl-indent: 8 + * c-label-offset: -8 + * c++-hanging-braces: 1 + * c++-access-specifier-offset: -8 + * c++-empty-arglist-indent: 8 + * c++-friend-offset: 0 + * End: + */ diff --git a/sys/dev/fdc/fdcreg.h b/sys/dev/fdc/fdcreg.h index d0d69e2..ad5c344 100644 --- a/sys/dev/fdc/fdcreg.h +++ b/sys/dev/fdc/fdcreg.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)fdreg.h 7.1 (Berkeley) 5/9/91 - * $Id: fdreg.h,v 1.4 1994/02/07 22:12:42 alm Exp $ + * $Id: fdreg.h,v 1.5 1994/08/13 03:49:58 wollman Exp $ */ /* @@ -42,24 +42,26 @@ #include <i386/isa/ic/nec765.h> /* registers */ -#define fdout 2 /* Digital Output Register (W) */ +#define FDOUT 2 /* Digital Output Register (W) */ #define FDO_FDSEL 0x03 /* floppy device select */ #define FDO_FRST 0x04 /* floppy controller reset */ #define FDO_FDMAEN 0x08 /* enable floppy DMA and Interrupt */ #define FDO_MOEN0 0x10 /* motor enable drive 0 */ #define FDO_MOEN1 0x20 /* motor enable drive 1 */ -#define FDO_MOEN2 0x30 /* motor enable drive 2 */ -#define FDO_MOEN3 0x40 /* motor enable drive 3 */ +#define FDO_MOEN2 0x40 /* motor enable drive 2 */ +#define FDO_MOEN3 0x80 /* motor enable drive 3 */ -#define fdsts 4 /* NEC 765 Main Status Register (R) */ -#define fddata 5 /* NEC 765 Data Register (R/W) */ +#define FDSTS 4 /* NEC 765 Main Status Register (R) */ +#define FDDATA 5 /* NEC 765 Data Register (R/W) */ -#define fdctl 7 /* Control Register (W) */ +#define FDCTL 7 /* Control Register (W) */ #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 */ -#define fdin 7 /* Digital Input Register (R) */ +#define FDIN 7 /* Digital Input Register (R) */ #define FDI_DCHG 0x80 /* diskette has been changed */ - + /* requires drive and motor being selected */ + /* is cleared by any step pulse to drive */ diff --git a/sys/dev/ic/nec765.h b/sys/dev/ic/nec765.h index 1895db7..9f93b70 100644 --- a/sys/dev/ic/nec765.h +++ b/sys/dev/ic/nec765.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)nec765.h 7.1 (Berkeley) 5/9/91 - * $Id$ + * $Id: nec765.h,v 1.2 1993/10/16 13:48:50 rgrimes Exp $ */ /* @@ -47,26 +47,90 @@ #define NE7_RQM 0x80 /* Diskette Controller ReQuest for Master */ /* Status register ST0 */ -#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005drv_chck\004drive_rdy\003top_head" +#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005equ_chck\004drive_notrdy\003top_head" + +#define NE7_ST0_IC 0xc0 /* interrupt completion code */ + +#define NE7_ST0_IC_RC 0xc0 /* terminated due to ready changed, n/a */ +#define NE7_ST0_IC_IV 0x80 /* invalid command; must reset FDC */ +#define NE7_ST0_IC_AT 0x40 /* abnormal termination, check error stat */ +#define NE7_ST0_IC_NT 0x00 /* normal termination */ + +#define NE7_ST0_SE 0x20 /* seek end */ +#define NE7_ST0_EC 0x10 /* equipment check, recalibrated but no trk0 */ +#define NE7_ST0_NR 0x08 /* not ready (n/a) */ +#define NE7_ST0_HD 0x04 /* upper head selected */ +#define NE7_ST0_DR 0x03 /* drive code */ /* Status register ST1 */ #define NE7_ST1BITS "\020\010end_of_cyl\006bad_crc\005data_overrun\003sec_not_fnd\002write_protect\001no_am" +#define NE7_ST1_EN 0x80 /* end of cylinder, access past last record */ +#define NE7_ST1_DE 0x20 /* data error, CRC fail in ID or data */ +#define NE7_ST1_OR 0x10 /* DMA overrun, DMA failed to do i/o quickly */ +#define NE7_ST1_ND 0x04 /* no data, sector not found or CRC in ID f. */ +#define NE7_ST1_NW 0x02 /* not writeable, attempt to violate WP */ +#define NE7_ST1_MA 0x01 /* missing address mark (in ID or data field)*/ + /* Status register ST2 */ #define NE7_ST2BITS "\020\007ctrl_mrk\006bad_crc\005wrong_cyl\004scn_eq\003scn_not_fnd\002bad_cyl\001no_dam" +#define NE7_ST2_CM 0x40 /* control mark; found deleted data */ +#define NE7_ST2_DD 0x20 /* data error in data field, CRC fail */ +#define NE7_ST2_WC 0x10 /* wrong cylinder, ID field mismatches cmd */ +#define NE7_ST2_SH 0x08 /* scan equal hit */ +#define NE7_ST2_SN 0x04 /* scan not satisfied */ +#define NE7_ST2_BC 0x02 /* bad cylinder, cylinder marked 0xff */ +#define NE7_ST2_MD 0x01 /* missing address mark in data field */ + /* Status register ST3 */ #define NE7_ST3BITS "\020\010fault\007write_protect\006drdy\005tk0\004two_side\003side_sel\002" +#define NE7_ST3_FT 0x80 /* fault; PC: n/a */ +#define NE7_ST3_WP 0x40 /* write protected */ +#define NE7_ST3_RD 0x20 /* ready; PC: always true */ +#define NE7_ST3_T0 0x10 /* track 0 */ +#define NE7_ST3_TS 0x08 /* two-sided; PC: n/a */ +#define NE7_ST3_HD 0x04 /* upper head select */ +#define NE7_ST3_US 0x03 /* unit select */ + /* Commands */ +/* + * the top three bits -- where appropriate -- are set as follows: + * + * 0x80 - MT multi-track; allow both sides to be handled in single cmd + * 0x40 - MFM modified frequency modulation; use MFM encoding + * 0x20 - SK skip; skip sectors marked as "deleted" + */ +#define NE7CMD_READTRK 0x42 /* read whole track */ #define NE7CMD_SPECIFY 3 /* specify drive parameters - requires unit parameters byte */ #define NE7CMD_SENSED 4 /* sense drive - requires unit select byte */ #define NE7CMD_WRITE 0xc5 /* write - requires eight additional bytes */ #define NE7CMD_READ 0xe6 /* read - requires eight additional bytes */ -#define NE7CMD_FORMAT 0x4c /* format - requires five additional bytes */ #define NE7CMD_RECAL 7 /* recalibrate drive - requires unit select byte */ #define NE7CMD_SENSEI 8 /* sense controller interrupt status */ -#define NE7CMD_SEEK 15 /* seek drive - requires unit select byte +#define NE7CMD_WRITEDEL 0xc9 /* write deleted data */ +#define NE7CMD_READID 0x4a /* read ID field */ +#define NE7CMD_READDEL 0xec /* read deleted data */ +#define NE7CMD_FORMAT 0x4d /* format - requires five additional bytes */ +#define NE7CMD_SEEK 0x0f /* seek drive - requires unit select byte and new cyl byte */ +#define NE7CMD_SCNEQU 0xf1 /* scan equal */ +#define NE7CMD_SCNLE 0xf9 /* scan less or equal */ +#define NE7CMD_SCNGE 0xfd /* scan greater or equal */ + + +/* + * "specify" definitions + * + * acronyms (times are relative to a FDC clock of 8 MHz): + * srt - step rate; PC usually 3 ms + * hut - head unload time; PC usually maximum of 240 ms + * hlt - head load time; PC usually minimum of 2 ms + * nd - no DMA flag; PC usually not set (0) + */ + +#define NE7_SPEC_1(srt, hut) (((16 - (srt)) << 4) | (((hut) / 16))) +#define NE7_SPEC_2(hlt, nd) (((hlt) & 0xFE) | ((nd) & 1)) diff --git a/sys/i386/include/ioctl_fd.h b/sys/i386/include/ioctl_fd.h index 103f11b..f3bf953 100644 --- a/sys/i386/include/ioctl_fd.h +++ b/sys/i386/include/ioctl_fd.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1992-1993 by Joerg Wunsch, Dresden + * Copyright (C) 1992-1994 by Joerg Wunsch, Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,17 +11,18 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. * * $Id$ */ @@ -96,5 +97,11 @@ struct fd_type { #define FD_FORM _IOW('F', 61, struct fd_formb) /* format a track */ #define FD_GTYPE _IOR('F', 62, struct fd_type) /* get drive type */ +#define FD_STYPE _IOW('F', 63, struct fd_type) /* set drive type */ + +#define FD_GOPTS _IOR('F', 64, int) /* drive options, see below */ +#define FD_SOPTS _IOW('F', 65, int) + +#define FDOPT_NORETRY 0x0001 /* no retries on failure (cleared on close) */ #endif /* !def _IOCTL_FD_H */ diff --git a/sys/i386/isa/fd.c b/sys/i386/isa/fd.c index 8dd7314..a7d7f88 100644 --- a/sys/i386/isa/fd.c +++ b/sys/i386/isa/fd.c @@ -1,4 +1,3 @@ -/*#define DEBUG 1*/ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. @@ -6,6 +5,12 @@ * This code is derived from software contributed to Berkeley by * Don Ahn. * + * Portions Copyright (c) 1993, 1994 by + * jc@irbs.UUCP (John Capo) + * vak@zebub.msk.su (Serge Vakulenko) + * ache@astral.msk.su (Andrew A. Chernov) + * joerg_wunsch@uriah.sax.de (Joerg Wunsch) + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -35,7 +40,7 @@ * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fd.c,v 1.28 1994/08/23 07:52:09 paul Exp $ + * $Id: fd.c,v 1.29 1994/08/29 21:32:31 ache Exp $ * */ @@ -59,30 +64,30 @@ #include <sys/buf.h> #include <sys/uio.h> #include <sys/malloc.h> +#include <sys/proc.h> #include <sys/syslog.h> #include <i386/isa/isa.h> #include <i386/isa/isa_device.h> #include <i386/isa/fdreg.h> #include <i386/isa/fdc.h> -#include <i386/isa/icu.h> #include <i386/isa/rtc.h> -#if NFT > 0 -extern int ftopen(), ftintr(), ftattach(), ftclose(), ftioctl(); -#endif - #define RAW_PART 2 - #define b_cylin b_resid -#define FDBLK 512 /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX +/* + * this biotab field doubles as a field for the physical unit number + * on the controller + */ +#define id_physid id_scsiid + #define NUMTYPES 14 #define NUMDENS (NUMTYPES - 6) -/* This defines (-1) must match index for fd_types */ +/* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 @@ -134,16 +139,17 @@ struct fdc_data fdc_data[NFDC]; struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ - int type; /* Drive type (HD, DD */ + int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ 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 */ - int skip; - int hddrv; - int track; /* where we think the head is */ + int skip; + int hddrv; + int track; /* where we think the head is */ + int options; /* user configurable options, see ioctl_fd.h */ } fd_data[NFD]; /***********************************************************************\ @@ -155,10 +161,48 @@ struct fd_data { * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ -#define id_physid id_scsiid /* this biotab field doubles as a field */ - /* for the physical unit number on the controller */ +#if NFT > 0 +int ftopen(dev_t, int); +int ftintr(ftu_t ftu); +int ftclose(dev_t, int); +void ftstrategy(struct buf *); +int ftioctl(dev_t, int, caddr_t, int, struct proc *); +int ftdump(dev_t); +int ftsize(dev_t); +int ftattach(struct isa_device *, struct isa_device *); +#endif +/* autoconfig functions */ +static int fdprobe(struct isa_device *); +static int fdattach(struct isa_device *); + +/* exported functions */ +int fdsize (dev_t); +void fdintr(fdcu_t); +int Fdopen(dev_t, int); +int fdclose(dev_t, int); +void fdstrategy(struct buf *); +int fdioctl(dev_t, int, caddr_t, int, struct proc *); + +/* needed for ft driver, thus exported */ +int in_fdc(fdcu_t); +int out_fdc(fdcu_t, int); + +/* internal functions */ +static void set_motor(fdcu_t, int, int); +# define TURNON 1 +# define TURNOFF 0 +static timeout_t fd_turnoff; +static timeout_t fd_motor_on; +static void fd_turnon(fdu_t); +static void fdc_reset(fdc_p); +static void fdstart(fdcu_t); +static timeout_t fd_timeout; +static timeout_t fd_pseudointr; +static int fdstate(fdcu_t, fdc_p); static int retrier(fdcu_t); +static int fdformat(dev_t, struct fd_formb *, struct proc *); + #define DEVIDLE 0 #define FINDWORK 1 @@ -190,25 +234,18 @@ char *fdstates[] = "IOTIMEDOUT" }; - -int fd_debug = 1; +/* CAUTION: fd_debug causes huge amounts of logging output */ +int fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) -#define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2) +#define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* DEBUG */ #define TRACE0(arg) -#define TRACE1(arg1,arg2) +#define TRACE1(arg1, arg2) #endif /* DEBUG */ -static void fdstart(fdcu_t); -void fdintr(fdcu_t); -static timeout_t fd_turnoff; - /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ -static int fdprobe(struct isa_device *); -static int fdattach(struct isa_device *); - struct isa_driver fdcdriver = { fdprobe, fdattach, "fdc", }; @@ -216,56 +253,55 @@ struct isa_driver fdcdriver = { /* * probe for existance of controller */ -int +static int fdprobe(dev) struct isa_device *dev; { fdcu_t fdcu = dev->id_unit; if(fdc_data[fdcu].flags & FDC_ATTACHED) { - printf("fdc: same unit (%d) used multiple times\n",fdcu); + printf("fdc: same unit (%d) used multiple times\n", fdcu); return 0; } fdc_data[fdcu].baseport = dev->id_iobase; /* First - lets reset the floppy controller */ - - outb(dev->id_iobase+fdout,0); + outb(dev->id_iobase+FDOUT, 0); DELAY(100); - outb(dev->id_iobase+fdout,FDO_FRST); + outb(dev->id_iobase+FDOUT, FDO_FRST); /* see if it can handle a command */ - if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0) + if (out_fdc(fdcu, NE7CMD_SPECIFY) < 0) { return(0); } - out_fdc(fdcu,0xDF); - out_fdc(fdcu,2); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); return (IO_FDCSIZE); } /* * wire controller into system, look for floppy units */ -int +static int fdattach(dev) struct isa_device *dev; { - unsigned fdt,st0, cyl; - int hdr; + unsigned fdt; fdu_t fdu; fdcu_t fdcu = dev->id_unit; fdc_p fdc = fdc_data + fdcu; fd_p fd; - int fdsu; + int fdsu, st0; struct isa_device *fdup; fdc->fdcu = fdcu; fdc->flags |= FDC_ATTACHED; fdc->dmachan = dev->id_drq; fdc->state = DEVIDLE; - hdr = 0; + /* reset controller, turn motor off, clear fdout mirror reg */ + outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); printf("fdc%d:", fdcu); /* check for each floppy drive */ @@ -305,25 +341,33 @@ fdattach(dev) continue; } -#ifdef notyet /* select it */ - fd_turnon1(fdu); - spinwait(1000); /* 1 sec */ - out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ - out_fdc(fdcu,fdsu); + set_motor(fdcu, fdsu, TURNON); spinwait(1000); /* 1 sec */ + out_fdc(fdcu, NE7CMD_SEEK); /* seek some steps... */ + out_fdc(fdcu, fdsu); + out_fdc(fdcu, 10); + spinwait(300); /* ...wait a moment... */ + out_fdc(fdcu, NE7CMD_SENSEI); /* make controller happy */ + (void)in_fdc(fdcu); + (void)in_fdc(fdcu); + out_fdc(fdcu, NE7CMD_RECAL); /* ...and go back to 0 */ + out_fdc(fdcu, fdsu); + spinwait(1000); /* a second be enough for full stroke seek */ /* anything responding */ - out_fdc(fdcu,NE7CMD_SENSEI); + out_fdc(fdcu, NE7CMD_SENSEI); st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - if (st0 & 0xd0) + (void)in_fdc(fdcu); + set_motor(fdcu, fdsu, TURNOFF); + + if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ continue; -#endif fd->track = -2; fd->fdc = fdc; fd->fdsu = fdsu; + fd->options = 0; printf(" [%d: fd%d: ", fdsu, fdu); switch (fdt) { @@ -348,15 +392,10 @@ fdattach(dev) fd->type = NO_TYPE; break; } - - fd_turnoff((caddr_t)fdu); - hdr = 1; } printf("\n"); - /* Set transfer to 500kbps */ - outb(fdc->baseport+fdctl,0); /*XXX*/ - return 1; + return (1); } int @@ -367,103 +406,47 @@ fdsize(dev) } /****************************************************************************/ -/* fdstrategy */ -/****************************************************************************/ -void fdstrategy(struct buf *bp) -{ - register struct buf *dp,*dp0,*dp1; - long nblocks,blknum; - int s; - fdcu_t fdcu; - fdu_t fdu; - fdc_p fdc; - fd_p fd; - - fdu = FDUNIT(minor(bp->b_dev)); - fd = &fd_data[fdu]; - fdc = fd->fdc; - fdcu = fdc->fdcu; - -#if NFT > 0 - /* check for controller already busy with tape */ - if (fdc->flags & FDC_TAPE_BUSY) { - bp->b_error = EBUSY; - bp->b_flags |= B_ERROR; - return; - } -#endif - if ((fdu >= NFD) || (bp->b_blkno < 0)) { - printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n", - fdu, bp->b_blkno, bp->b_bcount); - pg("fd:error in fdstrategy"); - bp->b_error = EINVAL; - bp->b_flags |= B_ERROR; - goto bad; - } - /* - * Set up block calculations. - */ - blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK; - nblocks = fd->ft->size; - if (blknum + (bp->b_bcount / FDBLK) > nblocks) { - if (blknum == nblocks) { - bp->b_resid = bp->b_bcount; - } else { - bp->b_error = ENOSPC; - bp->b_flags |= B_ERROR; - } - goto bad; - } - bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); - bp->b_pblkno = bp->b_blkno; - dp = &(fdc->head); - s = splbio(); - disksort(dp, bp); - untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */ - fdstart(fdcu); - splx(s); - return; - -bad: - biodone(bp); - return; -} - -/****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ -void -set_motor(fdcu, fdu, reset) +static void +set_motor(fdcu, fdsu, turnon) fdcu_t fdcu; - fdu_t fdu; - int reset; + int fdsu; + int turnon; { - int m0,m1; - int selunit; - fd_p fd; - if(fd = fdc_data[fdcu].fd)/* yes an assign! */ - { - selunit = fd->fdsu; + int fdout = fdc_data[fdcu].fdout; + int needspecify = 0; + + if(turnon) { + fdout &= ~FDO_FDSEL; + fdout |= (FDO_MOEN0 << fdsu) + fdsu; + } else + fdout &= ~(FDO_MOEN0 << fdsu); + + if(!turnon + && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) + /* gonna turn off the last drive, put FDC to bed */ + fdout &= ~ (FDO_FRST|FDO_FDMAEN); + else { + /* make sure controller is selected and specified */ + if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) + needspecify = 1; + fdout |= (FDO_FRST|FDO_FDMAEN); } - else - { - selunit = 0; + + outb(fdc_data[fdcu].baseport+FDOUT, fdout); + fdc_data[fdcu].fdout = fdout; + TRACE1("[0x%x->FDOUT]", fdout); + + if(needspecify) { + out_fdc(fdcu, NE7CMD_SPECIFY); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); } - m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR; - m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR; - outb(fdc_data[fdcu].baseport+fdout, - selunit - | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) - | (m0 ? FDO_MOEN0 : 0) - | (m1 ? FDO_MOEN1 : 0)); - TRACE1("[0x%x->fdout]",( - selunit - | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) - | (m0 ? FDO_MOEN0 : 0) - | (m1 ? FDO_MOEN1 : 0))); } +/* ARGSUSED */ static void fd_turnoff(void *arg1) { @@ -473,11 +456,12 @@ fd_turnoff(void *arg1) fd_p fd = fd_data + fdu; s = splbio(); fd->flags &= ~FD_MOTOR; - set_motor(fd->fdc->fdcu,fd->fdsu,0); + set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF); splx(s); } -void +/* ARGSUSED */ +static void fd_motor_on(void *arg1) { fdu_t fdu = (fdu_t)arg1; @@ -493,27 +477,39 @@ fd_motor_on(void *arg1) splx(s); } -static void fd_turnon1(fdu_t); - -void +static void fd_turnon(fdu) fdu_t fdu; { fd_p fd = fd_data + fdu; if(!(fd->flags & FD_MOTOR)) { - fd_turnon1(fdu); - fd->flags |= FD_MOTOR_WAIT; + fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); + set_motor(fd->fdc->fdcu, fd->fdsu, TURNON); timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */ } } static void -fd_turnon1(fdu_t fdu) +fdc_reset(fdc) + fdc_p fdc; { - fd_p fd = fd_data + fdu; - fd->flags |= FD_MOTOR; - set_motor(fd->fdc->fdcu,fd->fdsu,0); + fdcu_t fdcu = fdc->fdcu; + + /* Try a reset, keep motor on */ + outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); + TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); + DELAY(100); + /* enable FDC, but defer interrupts a moment */ + outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN); + TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); + DELAY(100); + outb(fdc->baseport + FDOUT, fdc->fdout); + TRACE1("[0x%x->FDOUT]", fdc->fdout); + + out_fdc(fdcu, NE7CMD_SPECIFY); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); } /****************************************************************************/ @@ -525,17 +521,17 @@ in_fdc(fdcu) { int baseport = fdc_data[fdcu].baseport; int i, j = 100000; - while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM)) + while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return -1; if (j <= 0) return(-1); #ifdef DEBUG - i = inb(baseport+fddata); - TRACE1("[fddata->0x%x]",(unsigned char)i); + i = inb(baseport+FDDATA); + TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else - return inb(baseport+fddata); + return inb(baseport+FDDATA); #endif } @@ -549,17 +545,17 @@ out_fdc(fdcu, x) /* Check that the direction bit is set */ i = 100000; - while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0); + while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0); if (i <= 0) return (-1); /* Floppy timed out */ /* Check that the floppy controller is ready for a command */ i = 100000; - while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0); + while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) return (-1); /* Floppy timed out */ /* Send the command and return */ - outb(baseport+fddata,x); - TRACE1("[0x%x->fddata]",x); + outb(baseport+FDDATA, x); + TRACE1("[0x%x->FDDATA]", x); return (0); } @@ -650,17 +646,97 @@ fdclose(dev, flags) int flags; { fdu_t fdu = FDUNIT(minor(dev)); - int type = FDTYPE(minor(dev)); #if NFT > 0 + int type = FDTYPE(minor(dev)); + if (type & F_TAPE_TYPE) - return ftclose(0); + return ftclose(dev, flags); #endif fd_data[fdu].flags &= ~FD_OPEN; + fd_data[fdu].options &= ~FDOPT_NORETRY; return(0); } +/****************************************************************************/ +/* fdstrategy */ +/****************************************************************************/ +void +fdstrategy(struct buf *bp) +{ + register struct buf *dp; + long nblocks, blknum; + int s; + fdcu_t fdcu; + fdu_t fdu; + fdc_p fdc; + fd_p fd; + size_t fdblk; + + fdu = FDUNIT(minor(bp->b_dev)); + fd = &fd_data[fdu]; + fdc = fd->fdc; + fdcu = fdc->fdcu; + fdblk = 128 << (fd->ft->secsize); + +#if NFT > 0 + if (FDTYPE(minor(bp->b_dev)) & F_TAPE_TYPE) { + /* ft tapes do not (yet) support strategy i/o */ + bp->b_error = ENXIO; + bp->b_flags |= B_ERROR; + goto bad; + } + /* check for controller already busy with tape */ + if (fdc->flags & FDC_TAPE_BUSY) { + bp->b_error = EBUSY; + bp->b_flags |= B_ERROR; + goto bad; + } +#endif + if (!(bp->b_flags & B_FORMAT)) { + if ((fdu >= NFD) || (bp->b_blkno < 0)) { + printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n", + fdu, bp->b_blkno, bp->b_bcount); + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto bad; + } + if ((bp->b_bcount % fdblk) != 0) { + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto bad; + } + } + + /* + * Set up block calculations. + */ + blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/fdblk; + nblocks = fd->ft->size; + if (blknum + (bp->b_bcount / fdblk) > nblocks) { + if (blknum == nblocks) { + bp->b_resid = bp->b_bcount; + } else { + bp->b_error = ENOSPC; + bp->b_flags |= B_ERROR; + } + goto bad; + } + bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); + dp = &(fdc->head); + s = splbio(); + disksort(dp, bp); + untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */ + fdstart(fdcu); + splx(s); + return; + +bad: + biodone(bp); + return; +} + /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * @@ -674,9 +750,7 @@ static void fdstart(fdcu) fdcu_t fdcu; { - register struct buf *dp,*bp; int s; - fdu_t fdu; s = splbio(); if(fdc_data[fdcu].state == DEVIDLE) @@ -686,38 +760,43 @@ fdstart(fdcu) splx(s); } +/* ARGSUSED */ static void fd_timeout(void *arg1) { fdcu_t fdcu = (fdcu_t)arg1; fdu_t fdu = fdc_data[fdcu].fdu; - int st0, st3, cyl; - struct buf *dp,*bp; - int s; + int baseport = fdc_data[fdcu].baseport; + struct buf *dp, *bp; + int s; dp = &fdc_data[fdcu].head; - s = splbio(); bp = dp->b_actf; - out_fdc(fdcu,NE7CMD_SENSED); - out_fdc(fdcu,fd_data[fdu].hddrv); - st3 = in_fdc(fdcu); - - out_fdc(fdcu,NE7CMD_SENSEI); - st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n", - fdu, - st0, - NE7_ST0BITS, - cyl, - st3, - NE7_ST3BITS); + /* + * Due to IBM's brain-dead design, the FDC has a faked ready + * signal, hardwired to ready == true. Thus, any command + * issued if there's no diskette in the drive will _never_ + * complete, and must be aborted by resetting the FDC. + * Many thanks, Big Blue! + */ + + s = splbio(); + + TRACE1("fd%d[fd_timeout()]", fdu); + /* See if the controller is still busy (patiently awaiting data) */ + if(((inb(baseport + FDSTS)) & (NE7_CB|NE7_RQM)) == NE7_CB) + { + TRACE1("[FDSTS->0x%x]", inb(baseport + FDSTS)); + /* yup, it is; kill it now */ + fdc_reset(&fdc_data[fdcu]); + printf("fd%d: Operation timeout\n", fdu); + } if (bp) { retrier(fdcu); - fdc_data[fdcu].status[0] = 0xc0; + fdc_data[fdcu].status[0] = NE7_ST0_IC_RC; fdc_data[fdcu].state = IOTIMEDOUT; if( fdc_data[fdcu].retry < 6) fdc_data[fdcu].retry = 6; @@ -733,11 +812,13 @@ fd_timeout(void *arg1) } /* just ensure it has the right spl */ +/* ARGSUSED */ static void fd_pseudointr(void *arg1) { fdcu_t fdcu = (fdcu_t)arg1; int s; + s = splbio(); fdintr(fdcu); splx(s); @@ -759,25 +840,26 @@ fdintr(fdcu_t fdcu) (ftintr(fdu)); else #endif - while(fdstate(fdcu, fdc)) - ; + while(fdstate(fdcu, fdc)) + ; } /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ -int +static int fdstate(fdcu, fdc) fdcu_t fdcu; fdc_p fdc; { - int read, format, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0; + int read, format, head, sec = 0, i = 0, sectrac, st0, cyl, st3; unsigned long blknum; fdu_t fdu = fdc->fdu; fd_p fd; - register struct buf *dp,*bp; + register struct buf *dp, *bp; struct fd_formb *finfo = NULL; + size_t fdblk; dp = &(fdc->head); bp = dp->b_actf; @@ -790,16 +872,17 @@ fdstate(fdcu, fdc) fdc->state = DEVIDLE; if(fdc->fd) { - printf("unexpected valid fd pointer (fdu = %d)\n" - ,fdc->fdu); + printf("unexpected valid fd pointer (fdu = %d)\n", + fdc->fdu); fdc->fd = (fd_p) 0; fdc->fdu = -1; } - TRACE1("[fdc%d IDLE]",fdcu); + TRACE1("[fdc%d IDLE]", fdcu); return(0); } fdu = FDUNIT(minor(bp->b_dev)); fd = fd_data + fdu; + fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) { printf("confused fd pointers\n"); @@ -808,9 +891,9 @@ fdstate(fdcu, fdc) format = bp->b_flags & B_FORMAT; if(format) finfo = (struct fd_formb *)bp->b_un.b_addr; - TRACE1("fd%d",fdu); - TRACE1("[%s]",fdstates[fdc->state]); - TRACE1("(0x%x)",fd->flags); + TRACE1("fd%d", fdu); + TRACE1("[%s]", fdstates[fdc->state]); + TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, (caddr_t)fdu); timeout(fd_turnoff, (caddr_t)fdu, 4 * hz); switch (fdc->state) @@ -821,7 +904,8 @@ fdstate(fdcu, fdc) fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; - outb(fdc->baseport+fdctl, fd->ft->trans); + outb(fdc->baseport+FDCTL, fd->ft->trans); + TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in it's own good time * @@ -842,7 +926,7 @@ fdstate(fdcu, fdc) } else /* at least make sure we are selected */ { - set_motor(fdcu,fd->fdsu,0); + set_motor(fdcu, fd->fdsu, TURNON); } fdc->state = DOSEEK; break; @@ -852,33 +936,55 @@ fdstate(fdcu, fdc) fdc->state = SEEKCOMPLETE; break; } - out_fdc(fdcu,NE7CMD_SEEK); /* Seek function */ - out_fdc(fdcu,fd->fdsu); /* Drive number */ - out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac); + out_fdc(fdcu, NE7CMD_SEEK); /* Seek function */ + out_fdc(fdcu, fd->fdsu); /* Drive number */ + out_fdc(fdcu, bp->b_cylin * fd->ft->steptrac); fd->track = -2; fdc->state = SEEKWAIT; - timeout(fd_timeout, (caddr_t)fdcu, 2 * hz); return(0); /* will return later */ case SEEKWAIT: - untimeout(fd_timeout, (caddr_t)fdcu); /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 50); + timeout(fd_pseudointr, (caddr_t)fdcu, hz / 32); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ - break; - case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == -2) { int descyl = bp->b_cylin * fd->ft->steptrac; - out_fdc(fdcu,NE7CMD_SENSEI); - i = in_fdc(fdcu); - cyl = in_fdc(fdcu); + do { + out_fdc(fdcu, NE7CMD_SENSEI); + st0 = in_fdc(fdcu); + cyl = in_fdc(fdcu); + /* + * if this was a "ready changed" interrupt, + * fetch status again (can happen after + * enabling controller from reset state) + */ + } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); + if (0 == descyl) + { + /* + * seek to cyl 0 requested; make sure we are + * really there + */ + out_fdc(fdcu, NE7CMD_SENSED); + out_fdc(fdcu, fdu); + st3 = in_fdc(fdcu); + if ((st3 & NE7_ST3_T0) == 0) { + printf( + "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", + fdu, st3, NE7_ST3BITS); + if(fdc->retry < 3) + fdc->retry = 3; + return(retrier(fdcu)); + } + } if (cyl != descyl) { - printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", - fdu, descyl, cyl, i, NE7_ST0BITS); + printf( + "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", + fdu, descyl, cyl, st0, NE7_ST0BITS); return(retrier(fdcu)); } } @@ -888,46 +994,73 @@ fdstate(fdcu, fdc) fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip, - format ? bp->b_bcount : FDBLK, fdc->dmachan); - blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK - + fd->skip/FDBLK; + format ? bp->b_bcount : fdblk, fdc->dmachan); + blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + + fd->skip/fdblk; sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; -/*XXX*/ fd->hddrv = ((head&1)<<2)+fdu; + fd->hddrv = ((head&1)<<2)+fdu; + + if(format || !read) + { + /* make sure the drive is writable */ + out_fdc(fdcu, NE7CMD_SENSED); + out_fdc(fdcu, fdu); + st3 = in_fdc(fdcu); + if(st3 & NE7_ST3_WP) + { + /* + * XXX YES! this is ugly. + * in order to force the current operation + * to fail, we will have to fake an FDC + * error - all error handling is done + * by the retrier() + */ + fdc->status[0] = NE7_ST0_IC_AT; + fdc->status[1] = NE7_ST1_NW; + fdc->status[2] = 0; + fdc->status[3] = fd->track; + fdc->status[4] = head; + fdc->status[5] = sec; + fdc->retry = 8; /* break out immediately */ + fdc->state = IOTIMEDOUT; /* not really... */ + return (1); + } + } if(format) { /* formatting */ - out_fdc(fdcu,/* NE7CMD_FORMAT */ 0x4d); - out_fdc(fdcu,head << 2 | fdu); - out_fdc(fdcu,finfo->fd_formb_secshift); - out_fdc(fdcu,finfo->fd_formb_nsecs); - out_fdc(fdcu,finfo->fd_formb_gaplen); - out_fdc(fdcu,finfo->fd_formb_fillbyte); + out_fdc(fdcu, NE7CMD_FORMAT); + out_fdc(fdcu, head << 2 | fdu); + out_fdc(fdcu, finfo->fd_formb_secshift); + out_fdc(fdcu, finfo->fd_formb_nsecs); + out_fdc(fdcu, finfo->fd_formb_gaplen); + out_fdc(fdcu, finfo->fd_formb_fillbyte); } else { if (read) { - out_fdc(fdcu,NE7CMD_READ); /* READ */ + out_fdc(fdcu, NE7CMD_READ); /* READ */ } else { - out_fdc(fdcu,NE7CMD_WRITE); /* WRITE */ + out_fdc(fdcu, NE7CMD_WRITE); /* WRITE */ } - out_fdc(fdcu,head << 2 | fdu); /* head & unit */ - out_fdc(fdcu,fd->track); /* track */ - out_fdc(fdcu,head); - out_fdc(fdcu,sec); /* sector XXX +1? */ - out_fdc(fdcu,fd->ft->secsize); /* sector size */ - out_fdc(fdcu,sectrac); /* sectors/track */ - out_fdc(fdcu,fd->ft->gap); /* gap size */ - out_fdc(fdcu,fd->ft->datalen); /* data length */ + out_fdc(fdcu, head << 2 | fdu); /* head & unit */ + out_fdc(fdcu, fd->track); /* track */ + out_fdc(fdcu, head); + out_fdc(fdcu, sec); /* sector XXX +1? */ + out_fdc(fdcu, fd->ft->secsize); /* sector size */ + out_fdc(fdcu, sectrac); /* sectors/track */ + out_fdc(fdcu, fd->ft->gap); /* gap size */ + out_fdc(fdcu, fd->ft->datalen); /* data length */ } fdc->state = IOCOMPLETE; - timeout(fd_timeout, (caddr_t)fdcu, 2 * hz); + timeout(fd_timeout, (caddr_t)fdcu, hz); return(0); /* will return later */ case IOCOMPLETE: /* IO DONE, post-analyze */ untimeout(fd_timeout, (caddr_t)fdcu); @@ -935,30 +1068,43 @@ fdstate(fdcu, fdc) { fdc->status[i] = in_fdc(fdcu); } - case IOTIMEDOUT: /*XXX*/ + fdc->state = IOTIMEDOUT; + /* FALLTHROUGH */ + case IOTIMEDOUT: isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip, - format ? bp->b_bcount : FDBLK, fdc->dmachan); - if (fdc->status[0]&0xF8) + format ? bp->b_bcount : fdblk, fdc->dmachan); + if (fdc->status[0] & NE7_ST0_IC) { - if (fdc->status[1] & 0x10) { + if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT + && fdc->status[1] & NE7_ST1_OR) { /* - * Operation not completed in reasonable time. - * Just restart it, don't increment retry count. - * (vak) + * DMA overrun. Someone hogged the bus + * and didn't release it in time for the + * next FDC transfer. + * Just restart it, don't increment retry + * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } + else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV + && fdc->retry < 6) + fdc->retry = 6; /* force a reset */ + else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT + && fdc->status[2] & NE7_ST2_WC + && fdc->retry < 3) + fdc->retry = 3; /* force recalibrate */ return(retrier(fdcu)); } /* All OK */ - fd->skip += FDBLK; + fd->skip += fdblk; if (!format && fd->skip < bp->b_bcount) { /* set up next transfer */ - blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK - + fd->skip/FDBLK; - bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads)); + blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + + fd->skip/fdblk; + bp->b_cylin = + (blknum / (fd->ft->sectrac * fd->ft->heads)); fdc->state = DOSEEK; } else @@ -974,68 +1120,76 @@ fdstate(fdcu, fdc) } return(1); case RESETCTLR: - /* Try a reset, keep motor on */ - set_motor(fdcu,fd->fdsu,1); - DELAY(100); - set_motor(fdcu,fd->fdsu,0); - outb(fdc->baseport+fdctl,fd->ft->trans); - TRACE1("[0x%x->fdctl]",fd->ft->trans); + fdc_reset(fdc); fdc->retry++; fdc->state = STARTRECAL; break; case STARTRECAL: - out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */ - out_fdc(fdcu,0xDF); - out_fdc(fdcu,2); - out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ - out_fdc(fdcu,fdu); + out_fdc(fdcu, NE7CMD_RECAL); /* Recalibrate Function */ + out_fdc(fdcu, fdu); fdc->state = RECALWAIT; return(0); /* will return later */ case RECALWAIT: /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 30); + timeout(fd_pseudointr, (caddr_t)fdcu, hz / 32); fdc->state = RECALCOMPLETE; return(0); /* will return later */ case RECALCOMPLETE: - out_fdc(fdcu,NE7CMD_SENSEI); - st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - if (cyl != 0) + do { + out_fdc(fdcu, NE7CMD_SENSEI); + st0 = in_fdc(fdcu); + cyl = in_fdc(fdcu); + /* + * if this was a "ready changed" interrupt, + * fetch status again (can happen after + * enabling controller from reset state) + */ + } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); + if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); + if(fdc->retry < 3) fdc->retry = 3; return(retrier(fdcu)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return(1); /* will return immediatly */ - case MOTORWAIT: + case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return(0); /* time's not up yet */ } - fdc->state = DOSEEK; + /* + * since the controller was off, it has lost its + * idea about the current track it were; thus, + * recalibrate the bastard + */ + fdc->state = STARTRECAL; return(1); /* will return immediatly */ default: printf("Unexpected FD int->"); - out_fdc(fdcu,NE7CMD_SENSEI); + out_fdc(fdcu, NE7CMD_SENSEI); st0 = in_fdc(fdcu); cyl = in_fdc(fdcu); - printf("ST0 = %lx, PCN = %lx\n",i,sec); - out_fdc(fdcu,0x4A); - out_fdc(fdcu,fd->fdsu); + printf("ST0 = %x, PCN = %x\n", st0, cyl); + out_fdc(fdcu, NE7CMD_READID); + out_fdc(fdcu, fd->fdsu); for(i=0;i<7;i++) { fdc->status[i] = in_fdc(fdcu); } - printf("intr status :%lx %lx %lx %lx %lx %lx %lx ", - fdc->status[0], - fdc->status[1], - fdc->status[2], - fdc->status[3], - fdc->status[4], - fdc->status[5], - fdc->status[6] ); + if(fdc->status[0] != -1) + printf("intr status :%lx %lx %lx %lx %lx %lx %lx\n", + fdc->status[0], + fdc->status[1], + fdc->status[2], + fdc->status[3], + fdc->status[4], + fdc->status[5], + fdc->status[6] ); + else + printf("FDC timed out\n"); return(0); } return(1); /* Come back immediatly to new state */ @@ -1046,11 +1200,13 @@ retrier(fdcu) fdcu_t fdcu; { fdc_p fdc = fdc_data + fdcu; - register struct buf *dp,*bp; + register struct buf *dp, *bp; dp = &(fdc->head); bp = dp->b_actf; + if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY) + goto fail; switch(fdc->retry) { case 0: case 1: case 2: @@ -1065,12 +1221,15 @@ retrier(fdcu) case 7: break; default: + fail: { dev_t sav_b_dev = bp->b_dev; /* Trick diskerr */ - bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); + bp->b_dev = makedev(major(bp->b_dev), + (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); diskerr(bp, "fd", "hard error", LOG_PRINTF, - fdc->fd->skip, (struct disklabel *)NULL); + fdc->fd->skip / DEV_BSIZE, + (struct disklabel *)NULL); bp->b_dev = sav_b_dev; printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS); printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS); @@ -1105,9 +1264,11 @@ fdformat(dev, finfo, p) struct buf *bp; int rv = 0, s; - + size_t fdblk; + fdu = FDUNIT(minor(dev)); fd = &fd_data[fdu]; + fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); @@ -1123,7 +1284,7 @@ fdformat(dev, finfo, p) * seek to the requested cylinder */ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) - + finfo->head * fd->ft->sectrac) * FDBLK / DEV_BSIZE; + + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->b_un.b_addr = (caddr_t)finfo; @@ -1142,41 +1303,39 @@ fdformat(dev, finfo, p) splx(s); if(rv == EWOULDBLOCK) - { /* timed out */ - biodone(bp); rv = EIO; - } + if(bp->b_flags & B_ERROR) + rv = bp->b_error; + biodone(bp); free(bp, M_TEMP); return rv; } /* - * fdioctl() from jc@irbs.UUCP (John Capo) - * i386/i386/conf.c needs to have fdioctl() declared and remove the line that - * defines fdioctl to be enxio. * - * TODO: Reformat. - * Think about allocating buffer off stack. + * TODO: Think about allocating buffer off stack. * Don't pass uncast 0's and NULL's to read/write/setdisklabel(). * Watch out for NetBSD's different *disklabel() interface. * - * Added functionality for floppy formatting - * joerg_wunsch@uriah.sax.de (Joerg Wunsch) */ int -fdioctl (dev, cmd, addr, flag, p) +fdioctl(dev, cmd, addr, flag, p) dev_t dev; int cmd; caddr_t addr; int flag; struct proc *p; { + fdu_t fdu = FDUNIT(minor(dev)); + fd_p fd = &fd_data[fdu]; + size_t fdblk; + struct fd_type *fdt; struct disklabel *dl; char buffer[DEV_BSIZE]; - int error; + int error = 0; #if NFT > 0 int type = FDTYPE(minor(dev)); @@ -1186,14 +1345,14 @@ fdioctl (dev, cmd, addr, flag, p) return ftioctl(dev, cmd, addr, flag, p); #endif - error = 0; + fdblk = 128 << fd->ft->secsize; switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; - dl->d_secsize = FDBLK; + dl->d_secsize = fdblk; fdt = fd_data[FDUNIT(minor(dev))].ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; @@ -1225,12 +1384,12 @@ fdioctl (dev, cmd, addr, flag, p) dl = (struct disklabel *)addr; - if (error = setdisklabel ((struct disklabel *)buffer, - dl, 0, NULL)) + if ((error = + setdisklabel ((struct disklabel *)buffer, dl, 0, NULL))) break; error = writedisklabel(dev, fdstrategy, - (struct disklabel *)buffer, NULL); + (struct disklabel *)buffer, NULL); break; case FD_FORM: @@ -1247,11 +1406,42 @@ fdioctl (dev, cmd, addr, flag, p) *(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft; break; + case FD_STYPE: /* set drive type */ + /* this is considered harmful; only allow for superuser */ + if(suser(p->p_ucred, &p->p_acflag) != 0) + return EPERM; + *fd_data[FDUNIT(minor(dev))].ft = *(struct fd_type *)addr; + break; + + case FD_GOPTS: /* get drive options */ + *(int *)addr = fd_data[FDUNIT(minor(dev))].options; + break; + + case FD_SOPTS: /* set drive options */ + fd_data[FDUNIT(minor(dev))].options = *(int *)addr; + break; + default: - error = EINVAL; + error = ENOTTY; break; } return (error); } #endif +/* + * Hello emacs, these are the + * Local Variables: + * c-indent-level: 8 + * c-continued-statement-offset: 8 + * c-continued-brace-offset: 0 + * c-brace-offset: -8 + * c-brace-imaginary-offset: 0 + * c-argdecl-indent: 8 + * c-label-offset: -8 + * c++-hanging-braces: 1 + * c++-access-specifier-offset: -8 + * c++-empty-arglist-indent: 8 + * c++-friend-offset: 0 + * End: + */ diff --git a/sys/i386/isa/fdc.h b/sys/i386/isa/fdc.h index 1542f0e..c938eb4 100644 --- a/sys/i386/isa/fdc.h +++ b/sys/i386/isa/fdc.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id:$ + * $Id: fdc.h,v 1.2 1994/02/14 22:24:25 nate Exp $ * */ @@ -49,12 +49,12 @@ struct fdc_data #define FDC_HASFTAPE 0x02 #define FDC_TAPE_BUSY 0x04 struct fd_data *fd; - int fdu; /* the active drive */ + int fdu; /* the active drive */ + int state; + int retry; + int fdout; /* mirror of the w/o digital output reg */ + int status[7]; /* copy of the registers */ struct buf head; /* Head of buf chain */ - struct buf rhead; /* Raw head of buf chain */ - int state; - int retry; - int status[7]; /* copy of the registers */ }; /***********************************************************************\ diff --git a/sys/i386/isa/fdreg.h b/sys/i386/isa/fdreg.h index d0d69e2..ad5c344 100644 --- a/sys/i386/isa/fdreg.h +++ b/sys/i386/isa/fdreg.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)fdreg.h 7.1 (Berkeley) 5/9/91 - * $Id: fdreg.h,v 1.4 1994/02/07 22:12:42 alm Exp $ + * $Id: fdreg.h,v 1.5 1994/08/13 03:49:58 wollman Exp $ */ /* @@ -42,24 +42,26 @@ #include <i386/isa/ic/nec765.h> /* registers */ -#define fdout 2 /* Digital Output Register (W) */ +#define FDOUT 2 /* Digital Output Register (W) */ #define FDO_FDSEL 0x03 /* floppy device select */ #define FDO_FRST 0x04 /* floppy controller reset */ #define FDO_FDMAEN 0x08 /* enable floppy DMA and Interrupt */ #define FDO_MOEN0 0x10 /* motor enable drive 0 */ #define FDO_MOEN1 0x20 /* motor enable drive 1 */ -#define FDO_MOEN2 0x30 /* motor enable drive 2 */ -#define FDO_MOEN3 0x40 /* motor enable drive 3 */ +#define FDO_MOEN2 0x40 /* motor enable drive 2 */ +#define FDO_MOEN3 0x80 /* motor enable drive 3 */ -#define fdsts 4 /* NEC 765 Main Status Register (R) */ -#define fddata 5 /* NEC 765 Data Register (R/W) */ +#define FDSTS 4 /* NEC 765 Main Status Register (R) */ +#define FDDATA 5 /* NEC 765 Data Register (R/W) */ -#define fdctl 7 /* Control Register (W) */ +#define FDCTL 7 /* Control Register (W) */ #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 */ -#define fdin 7 /* Digital Input Register (R) */ +#define FDIN 7 /* Digital Input Register (R) */ #define FDI_DCHG 0x80 /* diskette has been changed */ - + /* requires drive and motor being selected */ + /* is cleared by any step pulse to drive */ diff --git a/sys/i386/isa/ic/nec765.h b/sys/i386/isa/ic/nec765.h index 1895db7..9f93b70 100644 --- a/sys/i386/isa/ic/nec765.h +++ b/sys/i386/isa/ic/nec765.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)nec765.h 7.1 (Berkeley) 5/9/91 - * $Id$ + * $Id: nec765.h,v 1.2 1993/10/16 13:48:50 rgrimes Exp $ */ /* @@ -47,26 +47,90 @@ #define NE7_RQM 0x80 /* Diskette Controller ReQuest for Master */ /* Status register ST0 */ -#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005drv_chck\004drive_rdy\003top_head" +#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005equ_chck\004drive_notrdy\003top_head" + +#define NE7_ST0_IC 0xc0 /* interrupt completion code */ + +#define NE7_ST0_IC_RC 0xc0 /* terminated due to ready changed, n/a */ +#define NE7_ST0_IC_IV 0x80 /* invalid command; must reset FDC */ +#define NE7_ST0_IC_AT 0x40 /* abnormal termination, check error stat */ +#define NE7_ST0_IC_NT 0x00 /* normal termination */ + +#define NE7_ST0_SE 0x20 /* seek end */ +#define NE7_ST0_EC 0x10 /* equipment check, recalibrated but no trk0 */ +#define NE7_ST0_NR 0x08 /* not ready (n/a) */ +#define NE7_ST0_HD 0x04 /* upper head selected */ +#define NE7_ST0_DR 0x03 /* drive code */ /* Status register ST1 */ #define NE7_ST1BITS "\020\010end_of_cyl\006bad_crc\005data_overrun\003sec_not_fnd\002write_protect\001no_am" +#define NE7_ST1_EN 0x80 /* end of cylinder, access past last record */ +#define NE7_ST1_DE 0x20 /* data error, CRC fail in ID or data */ +#define NE7_ST1_OR 0x10 /* DMA overrun, DMA failed to do i/o quickly */ +#define NE7_ST1_ND 0x04 /* no data, sector not found or CRC in ID f. */ +#define NE7_ST1_NW 0x02 /* not writeable, attempt to violate WP */ +#define NE7_ST1_MA 0x01 /* missing address mark (in ID or data field)*/ + /* Status register ST2 */ #define NE7_ST2BITS "\020\007ctrl_mrk\006bad_crc\005wrong_cyl\004scn_eq\003scn_not_fnd\002bad_cyl\001no_dam" +#define NE7_ST2_CM 0x40 /* control mark; found deleted data */ +#define NE7_ST2_DD 0x20 /* data error in data field, CRC fail */ +#define NE7_ST2_WC 0x10 /* wrong cylinder, ID field mismatches cmd */ +#define NE7_ST2_SH 0x08 /* scan equal hit */ +#define NE7_ST2_SN 0x04 /* scan not satisfied */ +#define NE7_ST2_BC 0x02 /* bad cylinder, cylinder marked 0xff */ +#define NE7_ST2_MD 0x01 /* missing address mark in data field */ + /* Status register ST3 */ #define NE7_ST3BITS "\020\010fault\007write_protect\006drdy\005tk0\004two_side\003side_sel\002" +#define NE7_ST3_FT 0x80 /* fault; PC: n/a */ +#define NE7_ST3_WP 0x40 /* write protected */ +#define NE7_ST3_RD 0x20 /* ready; PC: always true */ +#define NE7_ST3_T0 0x10 /* track 0 */ +#define NE7_ST3_TS 0x08 /* two-sided; PC: n/a */ +#define NE7_ST3_HD 0x04 /* upper head select */ +#define NE7_ST3_US 0x03 /* unit select */ + /* Commands */ +/* + * the top three bits -- where appropriate -- are set as follows: + * + * 0x80 - MT multi-track; allow both sides to be handled in single cmd + * 0x40 - MFM modified frequency modulation; use MFM encoding + * 0x20 - SK skip; skip sectors marked as "deleted" + */ +#define NE7CMD_READTRK 0x42 /* read whole track */ #define NE7CMD_SPECIFY 3 /* specify drive parameters - requires unit parameters byte */ #define NE7CMD_SENSED 4 /* sense drive - requires unit select byte */ #define NE7CMD_WRITE 0xc5 /* write - requires eight additional bytes */ #define NE7CMD_READ 0xe6 /* read - requires eight additional bytes */ -#define NE7CMD_FORMAT 0x4c /* format - requires five additional bytes */ #define NE7CMD_RECAL 7 /* recalibrate drive - requires unit select byte */ #define NE7CMD_SENSEI 8 /* sense controller interrupt status */ -#define NE7CMD_SEEK 15 /* seek drive - requires unit select byte +#define NE7CMD_WRITEDEL 0xc9 /* write deleted data */ +#define NE7CMD_READID 0x4a /* read ID field */ +#define NE7CMD_READDEL 0xec /* read deleted data */ +#define NE7CMD_FORMAT 0x4d /* format - requires five additional bytes */ +#define NE7CMD_SEEK 0x0f /* seek drive - requires unit select byte and new cyl byte */ +#define NE7CMD_SCNEQU 0xf1 /* scan equal */ +#define NE7CMD_SCNLE 0xf9 /* scan less or equal */ +#define NE7CMD_SCNGE 0xfd /* scan greater or equal */ + + +/* + * "specify" definitions + * + * acronyms (times are relative to a FDC clock of 8 MHz): + * srt - step rate; PC usually 3 ms + * hut - head unload time; PC usually maximum of 240 ms + * hlt - head load time; PC usually minimum of 2 ms + * nd - no DMA flag; PC usually not set (0) + */ + +#define NE7_SPEC_1(srt, hut) (((16 - (srt)) << 4) | (((hut) / 16))) +#define NE7_SPEC_2(hlt, nd) (((hlt) & 0xFE) | ((nd) & 1)) diff --git a/sys/isa/fd.c b/sys/isa/fd.c index 8dd7314..a7d7f88 100644 --- a/sys/isa/fd.c +++ b/sys/isa/fd.c @@ -1,4 +1,3 @@ -/*#define DEBUG 1*/ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. @@ -6,6 +5,12 @@ * This code is derived from software contributed to Berkeley by * Don Ahn. * + * Portions Copyright (c) 1993, 1994 by + * jc@irbs.UUCP (John Capo) + * vak@zebub.msk.su (Serge Vakulenko) + * ache@astral.msk.su (Andrew A. Chernov) + * joerg_wunsch@uriah.sax.de (Joerg Wunsch) + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -35,7 +40,7 @@ * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fd.c,v 1.28 1994/08/23 07:52:09 paul Exp $ + * $Id: fd.c,v 1.29 1994/08/29 21:32:31 ache Exp $ * */ @@ -59,30 +64,30 @@ #include <sys/buf.h> #include <sys/uio.h> #include <sys/malloc.h> +#include <sys/proc.h> #include <sys/syslog.h> #include <i386/isa/isa.h> #include <i386/isa/isa_device.h> #include <i386/isa/fdreg.h> #include <i386/isa/fdc.h> -#include <i386/isa/icu.h> #include <i386/isa/rtc.h> -#if NFT > 0 -extern int ftopen(), ftintr(), ftattach(), ftclose(), ftioctl(); -#endif - #define RAW_PART 2 - #define b_cylin b_resid -#define FDBLK 512 /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX +/* + * this biotab field doubles as a field for the physical unit number + * on the controller + */ +#define id_physid id_scsiid + #define NUMTYPES 14 #define NUMDENS (NUMTYPES - 6) -/* This defines (-1) must match index for fd_types */ +/* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 @@ -134,16 +139,17 @@ struct fdc_data fdc_data[NFDC]; struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ - int type; /* Drive type (HD, DD */ + int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ 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 */ - int skip; - int hddrv; - int track; /* where we think the head is */ + int skip; + int hddrv; + int track; /* where we think the head is */ + int options; /* user configurable options, see ioctl_fd.h */ } fd_data[NFD]; /***********************************************************************\ @@ -155,10 +161,48 @@ struct fd_data { * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ -#define id_physid id_scsiid /* this biotab field doubles as a field */ - /* for the physical unit number on the controller */ +#if NFT > 0 +int ftopen(dev_t, int); +int ftintr(ftu_t ftu); +int ftclose(dev_t, int); +void ftstrategy(struct buf *); +int ftioctl(dev_t, int, caddr_t, int, struct proc *); +int ftdump(dev_t); +int ftsize(dev_t); +int ftattach(struct isa_device *, struct isa_device *); +#endif +/* autoconfig functions */ +static int fdprobe(struct isa_device *); +static int fdattach(struct isa_device *); + +/* exported functions */ +int fdsize (dev_t); +void fdintr(fdcu_t); +int Fdopen(dev_t, int); +int fdclose(dev_t, int); +void fdstrategy(struct buf *); +int fdioctl(dev_t, int, caddr_t, int, struct proc *); + +/* needed for ft driver, thus exported */ +int in_fdc(fdcu_t); +int out_fdc(fdcu_t, int); + +/* internal functions */ +static void set_motor(fdcu_t, int, int); +# define TURNON 1 +# define TURNOFF 0 +static timeout_t fd_turnoff; +static timeout_t fd_motor_on; +static void fd_turnon(fdu_t); +static void fdc_reset(fdc_p); +static void fdstart(fdcu_t); +static timeout_t fd_timeout; +static timeout_t fd_pseudointr; +static int fdstate(fdcu_t, fdc_p); static int retrier(fdcu_t); +static int fdformat(dev_t, struct fd_formb *, struct proc *); + #define DEVIDLE 0 #define FINDWORK 1 @@ -190,25 +234,18 @@ char *fdstates[] = "IOTIMEDOUT" }; - -int fd_debug = 1; +/* CAUTION: fd_debug causes huge amounts of logging output */ +int fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) -#define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2) +#define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* DEBUG */ #define TRACE0(arg) -#define TRACE1(arg1,arg2) +#define TRACE1(arg1, arg2) #endif /* DEBUG */ -static void fdstart(fdcu_t); -void fdintr(fdcu_t); -static timeout_t fd_turnoff; - /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ -static int fdprobe(struct isa_device *); -static int fdattach(struct isa_device *); - struct isa_driver fdcdriver = { fdprobe, fdattach, "fdc", }; @@ -216,56 +253,55 @@ struct isa_driver fdcdriver = { /* * probe for existance of controller */ -int +static int fdprobe(dev) struct isa_device *dev; { fdcu_t fdcu = dev->id_unit; if(fdc_data[fdcu].flags & FDC_ATTACHED) { - printf("fdc: same unit (%d) used multiple times\n",fdcu); + printf("fdc: same unit (%d) used multiple times\n", fdcu); return 0; } fdc_data[fdcu].baseport = dev->id_iobase; /* First - lets reset the floppy controller */ - - outb(dev->id_iobase+fdout,0); + outb(dev->id_iobase+FDOUT, 0); DELAY(100); - outb(dev->id_iobase+fdout,FDO_FRST); + outb(dev->id_iobase+FDOUT, FDO_FRST); /* see if it can handle a command */ - if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0) + if (out_fdc(fdcu, NE7CMD_SPECIFY) < 0) { return(0); } - out_fdc(fdcu,0xDF); - out_fdc(fdcu,2); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); return (IO_FDCSIZE); } /* * wire controller into system, look for floppy units */ -int +static int fdattach(dev) struct isa_device *dev; { - unsigned fdt,st0, cyl; - int hdr; + unsigned fdt; fdu_t fdu; fdcu_t fdcu = dev->id_unit; fdc_p fdc = fdc_data + fdcu; fd_p fd; - int fdsu; + int fdsu, st0; struct isa_device *fdup; fdc->fdcu = fdcu; fdc->flags |= FDC_ATTACHED; fdc->dmachan = dev->id_drq; fdc->state = DEVIDLE; - hdr = 0; + /* reset controller, turn motor off, clear fdout mirror reg */ + outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); printf("fdc%d:", fdcu); /* check for each floppy drive */ @@ -305,25 +341,33 @@ fdattach(dev) continue; } -#ifdef notyet /* select it */ - fd_turnon1(fdu); - spinwait(1000); /* 1 sec */ - out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ - out_fdc(fdcu,fdsu); + set_motor(fdcu, fdsu, TURNON); spinwait(1000); /* 1 sec */ + out_fdc(fdcu, NE7CMD_SEEK); /* seek some steps... */ + out_fdc(fdcu, fdsu); + out_fdc(fdcu, 10); + spinwait(300); /* ...wait a moment... */ + out_fdc(fdcu, NE7CMD_SENSEI); /* make controller happy */ + (void)in_fdc(fdcu); + (void)in_fdc(fdcu); + out_fdc(fdcu, NE7CMD_RECAL); /* ...and go back to 0 */ + out_fdc(fdcu, fdsu); + spinwait(1000); /* a second be enough for full stroke seek */ /* anything responding */ - out_fdc(fdcu,NE7CMD_SENSEI); + out_fdc(fdcu, NE7CMD_SENSEI); st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - if (st0 & 0xd0) + (void)in_fdc(fdcu); + set_motor(fdcu, fdsu, TURNOFF); + + if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ continue; -#endif fd->track = -2; fd->fdc = fdc; fd->fdsu = fdsu; + fd->options = 0; printf(" [%d: fd%d: ", fdsu, fdu); switch (fdt) { @@ -348,15 +392,10 @@ fdattach(dev) fd->type = NO_TYPE; break; } - - fd_turnoff((caddr_t)fdu); - hdr = 1; } printf("\n"); - /* Set transfer to 500kbps */ - outb(fdc->baseport+fdctl,0); /*XXX*/ - return 1; + return (1); } int @@ -367,103 +406,47 @@ fdsize(dev) } /****************************************************************************/ -/* fdstrategy */ -/****************************************************************************/ -void fdstrategy(struct buf *bp) -{ - register struct buf *dp,*dp0,*dp1; - long nblocks,blknum; - int s; - fdcu_t fdcu; - fdu_t fdu; - fdc_p fdc; - fd_p fd; - - fdu = FDUNIT(minor(bp->b_dev)); - fd = &fd_data[fdu]; - fdc = fd->fdc; - fdcu = fdc->fdcu; - -#if NFT > 0 - /* check for controller already busy with tape */ - if (fdc->flags & FDC_TAPE_BUSY) { - bp->b_error = EBUSY; - bp->b_flags |= B_ERROR; - return; - } -#endif - if ((fdu >= NFD) || (bp->b_blkno < 0)) { - printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n", - fdu, bp->b_blkno, bp->b_bcount); - pg("fd:error in fdstrategy"); - bp->b_error = EINVAL; - bp->b_flags |= B_ERROR; - goto bad; - } - /* - * Set up block calculations. - */ - blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK; - nblocks = fd->ft->size; - if (blknum + (bp->b_bcount / FDBLK) > nblocks) { - if (blknum == nblocks) { - bp->b_resid = bp->b_bcount; - } else { - bp->b_error = ENOSPC; - bp->b_flags |= B_ERROR; - } - goto bad; - } - bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); - bp->b_pblkno = bp->b_blkno; - dp = &(fdc->head); - s = splbio(); - disksort(dp, bp); - untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */ - fdstart(fdcu); - splx(s); - return; - -bad: - biodone(bp); - return; -} - -/****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ -void -set_motor(fdcu, fdu, reset) +static void +set_motor(fdcu, fdsu, turnon) fdcu_t fdcu; - fdu_t fdu; - int reset; + int fdsu; + int turnon; { - int m0,m1; - int selunit; - fd_p fd; - if(fd = fdc_data[fdcu].fd)/* yes an assign! */ - { - selunit = fd->fdsu; + int fdout = fdc_data[fdcu].fdout; + int needspecify = 0; + + if(turnon) { + fdout &= ~FDO_FDSEL; + fdout |= (FDO_MOEN0 << fdsu) + fdsu; + } else + fdout &= ~(FDO_MOEN0 << fdsu); + + if(!turnon + && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) + /* gonna turn off the last drive, put FDC to bed */ + fdout &= ~ (FDO_FRST|FDO_FDMAEN); + else { + /* make sure controller is selected and specified */ + if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) + needspecify = 1; + fdout |= (FDO_FRST|FDO_FDMAEN); } - else - { - selunit = 0; + + outb(fdc_data[fdcu].baseport+FDOUT, fdout); + fdc_data[fdcu].fdout = fdout; + TRACE1("[0x%x->FDOUT]", fdout); + + if(needspecify) { + out_fdc(fdcu, NE7CMD_SPECIFY); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); } - m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR; - m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR; - outb(fdc_data[fdcu].baseport+fdout, - selunit - | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) - | (m0 ? FDO_MOEN0 : 0) - | (m1 ? FDO_MOEN1 : 0)); - TRACE1("[0x%x->fdout]",( - selunit - | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) - | (m0 ? FDO_MOEN0 : 0) - | (m1 ? FDO_MOEN1 : 0))); } +/* ARGSUSED */ static void fd_turnoff(void *arg1) { @@ -473,11 +456,12 @@ fd_turnoff(void *arg1) fd_p fd = fd_data + fdu; s = splbio(); fd->flags &= ~FD_MOTOR; - set_motor(fd->fdc->fdcu,fd->fdsu,0); + set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF); splx(s); } -void +/* ARGSUSED */ +static void fd_motor_on(void *arg1) { fdu_t fdu = (fdu_t)arg1; @@ -493,27 +477,39 @@ fd_motor_on(void *arg1) splx(s); } -static void fd_turnon1(fdu_t); - -void +static void fd_turnon(fdu) fdu_t fdu; { fd_p fd = fd_data + fdu; if(!(fd->flags & FD_MOTOR)) { - fd_turnon1(fdu); - fd->flags |= FD_MOTOR_WAIT; + fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); + set_motor(fd->fdc->fdcu, fd->fdsu, TURNON); timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */ } } static void -fd_turnon1(fdu_t fdu) +fdc_reset(fdc) + fdc_p fdc; { - fd_p fd = fd_data + fdu; - fd->flags |= FD_MOTOR; - set_motor(fd->fdc->fdcu,fd->fdsu,0); + fdcu_t fdcu = fdc->fdcu; + + /* Try a reset, keep motor on */ + outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); + TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); + DELAY(100); + /* enable FDC, but defer interrupts a moment */ + outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN); + TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); + DELAY(100); + outb(fdc->baseport + FDOUT, fdc->fdout); + TRACE1("[0x%x->FDOUT]", fdc->fdout); + + out_fdc(fdcu, NE7CMD_SPECIFY); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); } /****************************************************************************/ @@ -525,17 +521,17 @@ in_fdc(fdcu) { int baseport = fdc_data[fdcu].baseport; int i, j = 100000; - while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM)) + while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return -1; if (j <= 0) return(-1); #ifdef DEBUG - i = inb(baseport+fddata); - TRACE1("[fddata->0x%x]",(unsigned char)i); + i = inb(baseport+FDDATA); + TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else - return inb(baseport+fddata); + return inb(baseport+FDDATA); #endif } @@ -549,17 +545,17 @@ out_fdc(fdcu, x) /* Check that the direction bit is set */ i = 100000; - while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0); + while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0); if (i <= 0) return (-1); /* Floppy timed out */ /* Check that the floppy controller is ready for a command */ i = 100000; - while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0); + while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) return (-1); /* Floppy timed out */ /* Send the command and return */ - outb(baseport+fddata,x); - TRACE1("[0x%x->fddata]",x); + outb(baseport+FDDATA, x); + TRACE1("[0x%x->FDDATA]", x); return (0); } @@ -650,17 +646,97 @@ fdclose(dev, flags) int flags; { fdu_t fdu = FDUNIT(minor(dev)); - int type = FDTYPE(minor(dev)); #if NFT > 0 + int type = FDTYPE(minor(dev)); + if (type & F_TAPE_TYPE) - return ftclose(0); + return ftclose(dev, flags); #endif fd_data[fdu].flags &= ~FD_OPEN; + fd_data[fdu].options &= ~FDOPT_NORETRY; return(0); } +/****************************************************************************/ +/* fdstrategy */ +/****************************************************************************/ +void +fdstrategy(struct buf *bp) +{ + register struct buf *dp; + long nblocks, blknum; + int s; + fdcu_t fdcu; + fdu_t fdu; + fdc_p fdc; + fd_p fd; + size_t fdblk; + + fdu = FDUNIT(minor(bp->b_dev)); + fd = &fd_data[fdu]; + fdc = fd->fdc; + fdcu = fdc->fdcu; + fdblk = 128 << (fd->ft->secsize); + +#if NFT > 0 + if (FDTYPE(minor(bp->b_dev)) & F_TAPE_TYPE) { + /* ft tapes do not (yet) support strategy i/o */ + bp->b_error = ENXIO; + bp->b_flags |= B_ERROR; + goto bad; + } + /* check for controller already busy with tape */ + if (fdc->flags & FDC_TAPE_BUSY) { + bp->b_error = EBUSY; + bp->b_flags |= B_ERROR; + goto bad; + } +#endif + if (!(bp->b_flags & B_FORMAT)) { + if ((fdu >= NFD) || (bp->b_blkno < 0)) { + printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n", + fdu, bp->b_blkno, bp->b_bcount); + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto bad; + } + if ((bp->b_bcount % fdblk) != 0) { + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto bad; + } + } + + /* + * Set up block calculations. + */ + blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/fdblk; + nblocks = fd->ft->size; + if (blknum + (bp->b_bcount / fdblk) > nblocks) { + if (blknum == nblocks) { + bp->b_resid = bp->b_bcount; + } else { + bp->b_error = ENOSPC; + bp->b_flags |= B_ERROR; + } + goto bad; + } + bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); + dp = &(fdc->head); + s = splbio(); + disksort(dp, bp); + untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */ + fdstart(fdcu); + splx(s); + return; + +bad: + biodone(bp); + return; +} + /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * @@ -674,9 +750,7 @@ static void fdstart(fdcu) fdcu_t fdcu; { - register struct buf *dp,*bp; int s; - fdu_t fdu; s = splbio(); if(fdc_data[fdcu].state == DEVIDLE) @@ -686,38 +760,43 @@ fdstart(fdcu) splx(s); } +/* ARGSUSED */ static void fd_timeout(void *arg1) { fdcu_t fdcu = (fdcu_t)arg1; fdu_t fdu = fdc_data[fdcu].fdu; - int st0, st3, cyl; - struct buf *dp,*bp; - int s; + int baseport = fdc_data[fdcu].baseport; + struct buf *dp, *bp; + int s; dp = &fdc_data[fdcu].head; - s = splbio(); bp = dp->b_actf; - out_fdc(fdcu,NE7CMD_SENSED); - out_fdc(fdcu,fd_data[fdu].hddrv); - st3 = in_fdc(fdcu); - - out_fdc(fdcu,NE7CMD_SENSEI); - st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n", - fdu, - st0, - NE7_ST0BITS, - cyl, - st3, - NE7_ST3BITS); + /* + * Due to IBM's brain-dead design, the FDC has a faked ready + * signal, hardwired to ready == true. Thus, any command + * issued if there's no diskette in the drive will _never_ + * complete, and must be aborted by resetting the FDC. + * Many thanks, Big Blue! + */ + + s = splbio(); + + TRACE1("fd%d[fd_timeout()]", fdu); + /* See if the controller is still busy (patiently awaiting data) */ + if(((inb(baseport + FDSTS)) & (NE7_CB|NE7_RQM)) == NE7_CB) + { + TRACE1("[FDSTS->0x%x]", inb(baseport + FDSTS)); + /* yup, it is; kill it now */ + fdc_reset(&fdc_data[fdcu]); + printf("fd%d: Operation timeout\n", fdu); + } if (bp) { retrier(fdcu); - fdc_data[fdcu].status[0] = 0xc0; + fdc_data[fdcu].status[0] = NE7_ST0_IC_RC; fdc_data[fdcu].state = IOTIMEDOUT; if( fdc_data[fdcu].retry < 6) fdc_data[fdcu].retry = 6; @@ -733,11 +812,13 @@ fd_timeout(void *arg1) } /* just ensure it has the right spl */ +/* ARGSUSED */ static void fd_pseudointr(void *arg1) { fdcu_t fdcu = (fdcu_t)arg1; int s; + s = splbio(); fdintr(fdcu); splx(s); @@ -759,25 +840,26 @@ fdintr(fdcu_t fdcu) (ftintr(fdu)); else #endif - while(fdstate(fdcu, fdc)) - ; + while(fdstate(fdcu, fdc)) + ; } /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ -int +static int fdstate(fdcu, fdc) fdcu_t fdcu; fdc_p fdc; { - int read, format, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0; + int read, format, head, sec = 0, i = 0, sectrac, st0, cyl, st3; unsigned long blknum; fdu_t fdu = fdc->fdu; fd_p fd; - register struct buf *dp,*bp; + register struct buf *dp, *bp; struct fd_formb *finfo = NULL; + size_t fdblk; dp = &(fdc->head); bp = dp->b_actf; @@ -790,16 +872,17 @@ fdstate(fdcu, fdc) fdc->state = DEVIDLE; if(fdc->fd) { - printf("unexpected valid fd pointer (fdu = %d)\n" - ,fdc->fdu); + printf("unexpected valid fd pointer (fdu = %d)\n", + fdc->fdu); fdc->fd = (fd_p) 0; fdc->fdu = -1; } - TRACE1("[fdc%d IDLE]",fdcu); + TRACE1("[fdc%d IDLE]", fdcu); return(0); } fdu = FDUNIT(minor(bp->b_dev)); fd = fd_data + fdu; + fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) { printf("confused fd pointers\n"); @@ -808,9 +891,9 @@ fdstate(fdcu, fdc) format = bp->b_flags & B_FORMAT; if(format) finfo = (struct fd_formb *)bp->b_un.b_addr; - TRACE1("fd%d",fdu); - TRACE1("[%s]",fdstates[fdc->state]); - TRACE1("(0x%x)",fd->flags); + TRACE1("fd%d", fdu); + TRACE1("[%s]", fdstates[fdc->state]); + TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, (caddr_t)fdu); timeout(fd_turnoff, (caddr_t)fdu, 4 * hz); switch (fdc->state) @@ -821,7 +904,8 @@ fdstate(fdcu, fdc) fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; - outb(fdc->baseport+fdctl, fd->ft->trans); + outb(fdc->baseport+FDCTL, fd->ft->trans); + TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in it's own good time * @@ -842,7 +926,7 @@ fdstate(fdcu, fdc) } else /* at least make sure we are selected */ { - set_motor(fdcu,fd->fdsu,0); + set_motor(fdcu, fd->fdsu, TURNON); } fdc->state = DOSEEK; break; @@ -852,33 +936,55 @@ fdstate(fdcu, fdc) fdc->state = SEEKCOMPLETE; break; } - out_fdc(fdcu,NE7CMD_SEEK); /* Seek function */ - out_fdc(fdcu,fd->fdsu); /* Drive number */ - out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac); + out_fdc(fdcu, NE7CMD_SEEK); /* Seek function */ + out_fdc(fdcu, fd->fdsu); /* Drive number */ + out_fdc(fdcu, bp->b_cylin * fd->ft->steptrac); fd->track = -2; fdc->state = SEEKWAIT; - timeout(fd_timeout, (caddr_t)fdcu, 2 * hz); return(0); /* will return later */ case SEEKWAIT: - untimeout(fd_timeout, (caddr_t)fdcu); /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 50); + timeout(fd_pseudointr, (caddr_t)fdcu, hz / 32); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ - break; - case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == -2) { int descyl = bp->b_cylin * fd->ft->steptrac; - out_fdc(fdcu,NE7CMD_SENSEI); - i = in_fdc(fdcu); - cyl = in_fdc(fdcu); + do { + out_fdc(fdcu, NE7CMD_SENSEI); + st0 = in_fdc(fdcu); + cyl = in_fdc(fdcu); + /* + * if this was a "ready changed" interrupt, + * fetch status again (can happen after + * enabling controller from reset state) + */ + } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); + if (0 == descyl) + { + /* + * seek to cyl 0 requested; make sure we are + * really there + */ + out_fdc(fdcu, NE7CMD_SENSED); + out_fdc(fdcu, fdu); + st3 = in_fdc(fdcu); + if ((st3 & NE7_ST3_T0) == 0) { + printf( + "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", + fdu, st3, NE7_ST3BITS); + if(fdc->retry < 3) + fdc->retry = 3; + return(retrier(fdcu)); + } + } if (cyl != descyl) { - printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", - fdu, descyl, cyl, i, NE7_ST0BITS); + printf( + "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", + fdu, descyl, cyl, st0, NE7_ST0BITS); return(retrier(fdcu)); } } @@ -888,46 +994,73 @@ fdstate(fdcu, fdc) fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip, - format ? bp->b_bcount : FDBLK, fdc->dmachan); - blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK - + fd->skip/FDBLK; + format ? bp->b_bcount : fdblk, fdc->dmachan); + blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + + fd->skip/fdblk; sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; -/*XXX*/ fd->hddrv = ((head&1)<<2)+fdu; + fd->hddrv = ((head&1)<<2)+fdu; + + if(format || !read) + { + /* make sure the drive is writable */ + out_fdc(fdcu, NE7CMD_SENSED); + out_fdc(fdcu, fdu); + st3 = in_fdc(fdcu); + if(st3 & NE7_ST3_WP) + { + /* + * XXX YES! this is ugly. + * in order to force the current operation + * to fail, we will have to fake an FDC + * error - all error handling is done + * by the retrier() + */ + fdc->status[0] = NE7_ST0_IC_AT; + fdc->status[1] = NE7_ST1_NW; + fdc->status[2] = 0; + fdc->status[3] = fd->track; + fdc->status[4] = head; + fdc->status[5] = sec; + fdc->retry = 8; /* break out immediately */ + fdc->state = IOTIMEDOUT; /* not really... */ + return (1); + } + } if(format) { /* formatting */ - out_fdc(fdcu,/* NE7CMD_FORMAT */ 0x4d); - out_fdc(fdcu,head << 2 | fdu); - out_fdc(fdcu,finfo->fd_formb_secshift); - out_fdc(fdcu,finfo->fd_formb_nsecs); - out_fdc(fdcu,finfo->fd_formb_gaplen); - out_fdc(fdcu,finfo->fd_formb_fillbyte); + out_fdc(fdcu, NE7CMD_FORMAT); + out_fdc(fdcu, head << 2 | fdu); + out_fdc(fdcu, finfo->fd_formb_secshift); + out_fdc(fdcu, finfo->fd_formb_nsecs); + out_fdc(fdcu, finfo->fd_formb_gaplen); + out_fdc(fdcu, finfo->fd_formb_fillbyte); } else { if (read) { - out_fdc(fdcu,NE7CMD_READ); /* READ */ + out_fdc(fdcu, NE7CMD_READ); /* READ */ } else { - out_fdc(fdcu,NE7CMD_WRITE); /* WRITE */ + out_fdc(fdcu, NE7CMD_WRITE); /* WRITE */ } - out_fdc(fdcu,head << 2 | fdu); /* head & unit */ - out_fdc(fdcu,fd->track); /* track */ - out_fdc(fdcu,head); - out_fdc(fdcu,sec); /* sector XXX +1? */ - out_fdc(fdcu,fd->ft->secsize); /* sector size */ - out_fdc(fdcu,sectrac); /* sectors/track */ - out_fdc(fdcu,fd->ft->gap); /* gap size */ - out_fdc(fdcu,fd->ft->datalen); /* data length */ + out_fdc(fdcu, head << 2 | fdu); /* head & unit */ + out_fdc(fdcu, fd->track); /* track */ + out_fdc(fdcu, head); + out_fdc(fdcu, sec); /* sector XXX +1? */ + out_fdc(fdcu, fd->ft->secsize); /* sector size */ + out_fdc(fdcu, sectrac); /* sectors/track */ + out_fdc(fdcu, fd->ft->gap); /* gap size */ + out_fdc(fdcu, fd->ft->datalen); /* data length */ } fdc->state = IOCOMPLETE; - timeout(fd_timeout, (caddr_t)fdcu, 2 * hz); + timeout(fd_timeout, (caddr_t)fdcu, hz); return(0); /* will return later */ case IOCOMPLETE: /* IO DONE, post-analyze */ untimeout(fd_timeout, (caddr_t)fdcu); @@ -935,30 +1068,43 @@ fdstate(fdcu, fdc) { fdc->status[i] = in_fdc(fdcu); } - case IOTIMEDOUT: /*XXX*/ + fdc->state = IOTIMEDOUT; + /* FALLTHROUGH */ + case IOTIMEDOUT: isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip, - format ? bp->b_bcount : FDBLK, fdc->dmachan); - if (fdc->status[0]&0xF8) + format ? bp->b_bcount : fdblk, fdc->dmachan); + if (fdc->status[0] & NE7_ST0_IC) { - if (fdc->status[1] & 0x10) { + if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT + && fdc->status[1] & NE7_ST1_OR) { /* - * Operation not completed in reasonable time. - * Just restart it, don't increment retry count. - * (vak) + * DMA overrun. Someone hogged the bus + * and didn't release it in time for the + * next FDC transfer. + * Just restart it, don't increment retry + * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } + else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV + && fdc->retry < 6) + fdc->retry = 6; /* force a reset */ + else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT + && fdc->status[2] & NE7_ST2_WC + && fdc->retry < 3) + fdc->retry = 3; /* force recalibrate */ return(retrier(fdcu)); } /* All OK */ - fd->skip += FDBLK; + fd->skip += fdblk; if (!format && fd->skip < bp->b_bcount) { /* set up next transfer */ - blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK - + fd->skip/FDBLK; - bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads)); + blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + + fd->skip/fdblk; + bp->b_cylin = + (blknum / (fd->ft->sectrac * fd->ft->heads)); fdc->state = DOSEEK; } else @@ -974,68 +1120,76 @@ fdstate(fdcu, fdc) } return(1); case RESETCTLR: - /* Try a reset, keep motor on */ - set_motor(fdcu,fd->fdsu,1); - DELAY(100); - set_motor(fdcu,fd->fdsu,0); - outb(fdc->baseport+fdctl,fd->ft->trans); - TRACE1("[0x%x->fdctl]",fd->ft->trans); + fdc_reset(fdc); fdc->retry++; fdc->state = STARTRECAL; break; case STARTRECAL: - out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */ - out_fdc(fdcu,0xDF); - out_fdc(fdcu,2); - out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ - out_fdc(fdcu,fdu); + out_fdc(fdcu, NE7CMD_RECAL); /* Recalibrate Function */ + out_fdc(fdcu, fdu); fdc->state = RECALWAIT; return(0); /* will return later */ case RECALWAIT: /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 30); + timeout(fd_pseudointr, (caddr_t)fdcu, hz / 32); fdc->state = RECALCOMPLETE; return(0); /* will return later */ case RECALCOMPLETE: - out_fdc(fdcu,NE7CMD_SENSEI); - st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - if (cyl != 0) + do { + out_fdc(fdcu, NE7CMD_SENSEI); + st0 = in_fdc(fdcu); + cyl = in_fdc(fdcu); + /* + * if this was a "ready changed" interrupt, + * fetch status again (can happen after + * enabling controller from reset state) + */ + } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); + if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); + if(fdc->retry < 3) fdc->retry = 3; return(retrier(fdcu)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return(1); /* will return immediatly */ - case MOTORWAIT: + case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return(0); /* time's not up yet */ } - fdc->state = DOSEEK; + /* + * since the controller was off, it has lost its + * idea about the current track it were; thus, + * recalibrate the bastard + */ + fdc->state = STARTRECAL; return(1); /* will return immediatly */ default: printf("Unexpected FD int->"); - out_fdc(fdcu,NE7CMD_SENSEI); + out_fdc(fdcu, NE7CMD_SENSEI); st0 = in_fdc(fdcu); cyl = in_fdc(fdcu); - printf("ST0 = %lx, PCN = %lx\n",i,sec); - out_fdc(fdcu,0x4A); - out_fdc(fdcu,fd->fdsu); + printf("ST0 = %x, PCN = %x\n", st0, cyl); + out_fdc(fdcu, NE7CMD_READID); + out_fdc(fdcu, fd->fdsu); for(i=0;i<7;i++) { fdc->status[i] = in_fdc(fdcu); } - printf("intr status :%lx %lx %lx %lx %lx %lx %lx ", - fdc->status[0], - fdc->status[1], - fdc->status[2], - fdc->status[3], - fdc->status[4], - fdc->status[5], - fdc->status[6] ); + if(fdc->status[0] != -1) + printf("intr status :%lx %lx %lx %lx %lx %lx %lx\n", + fdc->status[0], + fdc->status[1], + fdc->status[2], + fdc->status[3], + fdc->status[4], + fdc->status[5], + fdc->status[6] ); + else + printf("FDC timed out\n"); return(0); } return(1); /* Come back immediatly to new state */ @@ -1046,11 +1200,13 @@ retrier(fdcu) fdcu_t fdcu; { fdc_p fdc = fdc_data + fdcu; - register struct buf *dp,*bp; + register struct buf *dp, *bp; dp = &(fdc->head); bp = dp->b_actf; + if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY) + goto fail; switch(fdc->retry) { case 0: case 1: case 2: @@ -1065,12 +1221,15 @@ retrier(fdcu) case 7: break; default: + fail: { dev_t sav_b_dev = bp->b_dev; /* Trick diskerr */ - bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); + bp->b_dev = makedev(major(bp->b_dev), + (FDUNIT(minor(bp->b_dev))<<3)|RAW_PART); diskerr(bp, "fd", "hard error", LOG_PRINTF, - fdc->fd->skip, (struct disklabel *)NULL); + fdc->fd->skip / DEV_BSIZE, + (struct disklabel *)NULL); bp->b_dev = sav_b_dev; printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS); printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS); @@ -1105,9 +1264,11 @@ fdformat(dev, finfo, p) struct buf *bp; int rv = 0, s; - + size_t fdblk; + fdu = FDUNIT(minor(dev)); fd = &fd_data[fdu]; + fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); @@ -1123,7 +1284,7 @@ fdformat(dev, finfo, p) * seek to the requested cylinder */ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) - + finfo->head * fd->ft->sectrac) * FDBLK / DEV_BSIZE; + + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->b_un.b_addr = (caddr_t)finfo; @@ -1142,41 +1303,39 @@ fdformat(dev, finfo, p) splx(s); if(rv == EWOULDBLOCK) - { /* timed out */ - biodone(bp); rv = EIO; - } + if(bp->b_flags & B_ERROR) + rv = bp->b_error; + biodone(bp); free(bp, M_TEMP); return rv; } /* - * fdioctl() from jc@irbs.UUCP (John Capo) - * i386/i386/conf.c needs to have fdioctl() declared and remove the line that - * defines fdioctl to be enxio. * - * TODO: Reformat. - * Think about allocating buffer off stack. + * TODO: Think about allocating buffer off stack. * Don't pass uncast 0's and NULL's to read/write/setdisklabel(). * Watch out for NetBSD's different *disklabel() interface. * - * Added functionality for floppy formatting - * joerg_wunsch@uriah.sax.de (Joerg Wunsch) */ int -fdioctl (dev, cmd, addr, flag, p) +fdioctl(dev, cmd, addr, flag, p) dev_t dev; int cmd; caddr_t addr; int flag; struct proc *p; { + fdu_t fdu = FDUNIT(minor(dev)); + fd_p fd = &fd_data[fdu]; + size_t fdblk; + struct fd_type *fdt; struct disklabel *dl; char buffer[DEV_BSIZE]; - int error; + int error = 0; #if NFT > 0 int type = FDTYPE(minor(dev)); @@ -1186,14 +1345,14 @@ fdioctl (dev, cmd, addr, flag, p) return ftioctl(dev, cmd, addr, flag, p); #endif - error = 0; + fdblk = 128 << fd->ft->secsize; switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; - dl->d_secsize = FDBLK; + dl->d_secsize = fdblk; fdt = fd_data[FDUNIT(minor(dev))].ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; @@ -1225,12 +1384,12 @@ fdioctl (dev, cmd, addr, flag, p) dl = (struct disklabel *)addr; - if (error = setdisklabel ((struct disklabel *)buffer, - dl, 0, NULL)) + if ((error = + setdisklabel ((struct disklabel *)buffer, dl, 0, NULL))) break; error = writedisklabel(dev, fdstrategy, - (struct disklabel *)buffer, NULL); + (struct disklabel *)buffer, NULL); break; case FD_FORM: @@ -1247,11 +1406,42 @@ fdioctl (dev, cmd, addr, flag, p) *(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft; break; + case FD_STYPE: /* set drive type */ + /* this is considered harmful; only allow for superuser */ + if(suser(p->p_ucred, &p->p_acflag) != 0) + return EPERM; + *fd_data[FDUNIT(minor(dev))].ft = *(struct fd_type *)addr; + break; + + case FD_GOPTS: /* get drive options */ + *(int *)addr = fd_data[FDUNIT(minor(dev))].options; + break; + + case FD_SOPTS: /* set drive options */ + fd_data[FDUNIT(minor(dev))].options = *(int *)addr; + break; + default: - error = EINVAL; + error = ENOTTY; break; } return (error); } #endif +/* + * Hello emacs, these are the + * Local Variables: + * c-indent-level: 8 + * c-continued-statement-offset: 8 + * c-continued-brace-offset: 0 + * c-brace-offset: -8 + * c-brace-imaginary-offset: 0 + * c-argdecl-indent: 8 + * c-label-offset: -8 + * c++-hanging-braces: 1 + * c++-access-specifier-offset: -8 + * c++-empty-arglist-indent: 8 + * c++-friend-offset: 0 + * End: + */ diff --git a/sys/isa/fdc.h b/sys/isa/fdc.h index 1542f0e..c938eb4 100644 --- a/sys/isa/fdc.h +++ b/sys/isa/fdc.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id:$ + * $Id: fdc.h,v 1.2 1994/02/14 22:24:25 nate Exp $ * */ @@ -49,12 +49,12 @@ struct fdc_data #define FDC_HASFTAPE 0x02 #define FDC_TAPE_BUSY 0x04 struct fd_data *fd; - int fdu; /* the active drive */ + int fdu; /* the active drive */ + int state; + int retry; + int fdout; /* mirror of the w/o digital output reg */ + int status[7]; /* copy of the registers */ struct buf head; /* Head of buf chain */ - struct buf rhead; /* Raw head of buf chain */ - int state; - int retry; - int status[7]; /* copy of the registers */ }; /***********************************************************************\ diff --git a/sys/isa/fdreg.h b/sys/isa/fdreg.h index d0d69e2..ad5c344 100644 --- a/sys/isa/fdreg.h +++ b/sys/isa/fdreg.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)fdreg.h 7.1 (Berkeley) 5/9/91 - * $Id: fdreg.h,v 1.4 1994/02/07 22:12:42 alm Exp $ + * $Id: fdreg.h,v 1.5 1994/08/13 03:49:58 wollman Exp $ */ /* @@ -42,24 +42,26 @@ #include <i386/isa/ic/nec765.h> /* registers */ -#define fdout 2 /* Digital Output Register (W) */ +#define FDOUT 2 /* Digital Output Register (W) */ #define FDO_FDSEL 0x03 /* floppy device select */ #define FDO_FRST 0x04 /* floppy controller reset */ #define FDO_FDMAEN 0x08 /* enable floppy DMA and Interrupt */ #define FDO_MOEN0 0x10 /* motor enable drive 0 */ #define FDO_MOEN1 0x20 /* motor enable drive 1 */ -#define FDO_MOEN2 0x30 /* motor enable drive 2 */ -#define FDO_MOEN3 0x40 /* motor enable drive 3 */ +#define FDO_MOEN2 0x40 /* motor enable drive 2 */ +#define FDO_MOEN3 0x80 /* motor enable drive 3 */ -#define fdsts 4 /* NEC 765 Main Status Register (R) */ -#define fddata 5 /* NEC 765 Data Register (R/W) */ +#define FDSTS 4 /* NEC 765 Main Status Register (R) */ +#define FDDATA 5 /* NEC 765 Data Register (R/W) */ -#define fdctl 7 /* Control Register (W) */ +#define FDCTL 7 /* Control Register (W) */ #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 */ -#define fdin 7 /* Digital Input Register (R) */ +#define FDIN 7 /* Digital Input Register (R) */ #define FDI_DCHG 0x80 /* diskette has been changed */ - + /* requires drive and motor being selected */ + /* is cleared by any step pulse to drive */ diff --git a/sys/isa/ic/nec765.h b/sys/isa/ic/nec765.h index 1895db7..9f93b70 100644 --- a/sys/isa/ic/nec765.h +++ b/sys/isa/ic/nec765.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)nec765.h 7.1 (Berkeley) 5/9/91 - * $Id$ + * $Id: nec765.h,v 1.2 1993/10/16 13:48:50 rgrimes Exp $ */ /* @@ -47,26 +47,90 @@ #define NE7_RQM 0x80 /* Diskette Controller ReQuest for Master */ /* Status register ST0 */ -#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005drv_chck\004drive_rdy\003top_head" +#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005equ_chck\004drive_notrdy\003top_head" + +#define NE7_ST0_IC 0xc0 /* interrupt completion code */ + +#define NE7_ST0_IC_RC 0xc0 /* terminated due to ready changed, n/a */ +#define NE7_ST0_IC_IV 0x80 /* invalid command; must reset FDC */ +#define NE7_ST0_IC_AT 0x40 /* abnormal termination, check error stat */ +#define NE7_ST0_IC_NT 0x00 /* normal termination */ + +#define NE7_ST0_SE 0x20 /* seek end */ +#define NE7_ST0_EC 0x10 /* equipment check, recalibrated but no trk0 */ +#define NE7_ST0_NR 0x08 /* not ready (n/a) */ +#define NE7_ST0_HD 0x04 /* upper head selected */ +#define NE7_ST0_DR 0x03 /* drive code */ /* Status register ST1 */ #define NE7_ST1BITS "\020\010end_of_cyl\006bad_crc\005data_overrun\003sec_not_fnd\002write_protect\001no_am" +#define NE7_ST1_EN 0x80 /* end of cylinder, access past last record */ +#define NE7_ST1_DE 0x20 /* data error, CRC fail in ID or data */ +#define NE7_ST1_OR 0x10 /* DMA overrun, DMA failed to do i/o quickly */ +#define NE7_ST1_ND 0x04 /* no data, sector not found or CRC in ID f. */ +#define NE7_ST1_NW 0x02 /* not writeable, attempt to violate WP */ +#define NE7_ST1_MA 0x01 /* missing address mark (in ID or data field)*/ + /* Status register ST2 */ #define NE7_ST2BITS "\020\007ctrl_mrk\006bad_crc\005wrong_cyl\004scn_eq\003scn_not_fnd\002bad_cyl\001no_dam" +#define NE7_ST2_CM 0x40 /* control mark; found deleted data */ +#define NE7_ST2_DD 0x20 /* data error in data field, CRC fail */ +#define NE7_ST2_WC 0x10 /* wrong cylinder, ID field mismatches cmd */ +#define NE7_ST2_SH 0x08 /* scan equal hit */ +#define NE7_ST2_SN 0x04 /* scan not satisfied */ +#define NE7_ST2_BC 0x02 /* bad cylinder, cylinder marked 0xff */ +#define NE7_ST2_MD 0x01 /* missing address mark in data field */ + /* Status register ST3 */ #define NE7_ST3BITS "\020\010fault\007write_protect\006drdy\005tk0\004two_side\003side_sel\002" +#define NE7_ST3_FT 0x80 /* fault; PC: n/a */ +#define NE7_ST3_WP 0x40 /* write protected */ +#define NE7_ST3_RD 0x20 /* ready; PC: always true */ +#define NE7_ST3_T0 0x10 /* track 0 */ +#define NE7_ST3_TS 0x08 /* two-sided; PC: n/a */ +#define NE7_ST3_HD 0x04 /* upper head select */ +#define NE7_ST3_US 0x03 /* unit select */ + /* Commands */ +/* + * the top three bits -- where appropriate -- are set as follows: + * + * 0x80 - MT multi-track; allow both sides to be handled in single cmd + * 0x40 - MFM modified frequency modulation; use MFM encoding + * 0x20 - SK skip; skip sectors marked as "deleted" + */ +#define NE7CMD_READTRK 0x42 /* read whole track */ #define NE7CMD_SPECIFY 3 /* specify drive parameters - requires unit parameters byte */ #define NE7CMD_SENSED 4 /* sense drive - requires unit select byte */ #define NE7CMD_WRITE 0xc5 /* write - requires eight additional bytes */ #define NE7CMD_READ 0xe6 /* read - requires eight additional bytes */ -#define NE7CMD_FORMAT 0x4c /* format - requires five additional bytes */ #define NE7CMD_RECAL 7 /* recalibrate drive - requires unit select byte */ #define NE7CMD_SENSEI 8 /* sense controller interrupt status */ -#define NE7CMD_SEEK 15 /* seek drive - requires unit select byte +#define NE7CMD_WRITEDEL 0xc9 /* write deleted data */ +#define NE7CMD_READID 0x4a /* read ID field */ +#define NE7CMD_READDEL 0xec /* read deleted data */ +#define NE7CMD_FORMAT 0x4d /* format - requires five additional bytes */ +#define NE7CMD_SEEK 0x0f /* seek drive - requires unit select byte and new cyl byte */ +#define NE7CMD_SCNEQU 0xf1 /* scan equal */ +#define NE7CMD_SCNLE 0xf9 /* scan less or equal */ +#define NE7CMD_SCNGE 0xfd /* scan greater or equal */ + + +/* + * "specify" definitions + * + * acronyms (times are relative to a FDC clock of 8 MHz): + * srt - step rate; PC usually 3 ms + * hut - head unload time; PC usually maximum of 240 ms + * hlt - head load time; PC usually minimum of 2 ms + * nd - no DMA flag; PC usually not set (0) + */ + +#define NE7_SPEC_1(srt, hut) (((16 - (srt)) << 4) | (((hut) / 16))) +#define NE7_SPEC_2(hlt, nd) (((hlt) & 0xFE) | ((nd) & 1)) diff --git a/sys/sys/fdcio.h b/sys/sys/fdcio.h index 103f11b..f3bf953 100644 --- a/sys/sys/fdcio.h +++ b/sys/sys/fdcio.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 1992-1993 by Joerg Wunsch, Dresden + * Copyright (C) 1992-1994 by Joerg Wunsch, Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,17 +11,18 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. * * $Id$ */ @@ -96,5 +97,11 @@ struct fd_type { #define FD_FORM _IOW('F', 61, struct fd_formb) /* format a track */ #define FD_GTYPE _IOR('F', 62, struct fd_type) /* get drive type */ +#define FD_STYPE _IOW('F', 63, struct fd_type) /* set drive type */ + +#define FD_GOPTS _IOR('F', 64, int) /* drive options, see below */ +#define FD_SOPTS _IOW('F', 65, int) + +#define FDOPT_NORETRY 0x0001 /* no retries on failure (cleared on close) */ #endif /* !def _IOCTL_FD_H */ |