summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbde <bde@FreeBSD.org>1998-07-29 13:00:42 +0000
committerbde <bde@FreeBSD.org>1998-07-29 13:00:42 +0000
commit12ec611ec6ad9af841215b368ec1c5212a2bef71 (patch)
tree0e9aafc415fe03d6250f2053ee6e59f835182079
parent57a5f9023d7ba6eb9c0fe02789312a0d6ae4e363 (diff)
downloadFreeBSD-src-12ec611ec6ad9af841215b368ec1c5212a2bef71.zip
FreeBSD-src-12ec611ec6ad9af841215b368ec1c5212a2bef71.tar.gz
Fixed error handling:
- Call isa_dmadone() whenever necessary to stop DMA and/or free bounce buffers. Undead DMA corrupted the malloc freelist fairly consistently in the following configuration: SLICE kernel, 2 floppy drives, no disk in fd0, disk in fd1. - Don't call fdc_reset() from fd_timeout(). Doing so gave an "extra" interrupt which was usually misinterpreted as being for completion of the next FDC command; the interrupt for completion of the next FDC command was then usually misinterpreted... There were further complications for interrupts latched by the soft-spl mechanism so that they were delivered after all the h/w interrupts went away. This caused at least wrong head settle delays and may be why the FreeBSD floppy driver seems to munch floppies more than most floppy drivers. The reset was unnecessary anyway in cases that didn't have the bug described next, since is was repeated a little later for the IOTIMEDOUT state. The state machine has complications to handle resets correctly, so just use it. - Don't call retrier() from fd_timeout(). The IOTIMEDOUT state needs to be processed next, and it isn't valid to set to that state if retrier() has aborted the current transfer. Doing so caused null pointer panics after the previous bug was fixed. Improved error handling: - If an i/o is aborted, arrange to reset in the state machine before doing the next i/o. New fdc flag for this. This fixes spurious warnings and lengthy busy-waiting for the next i/o. - Split STARTRECAL into RESETCOMPLETE and STARTRECAL and only check for the results from reset if we actually reset. This fixes spurious warnings for other paths to STARTRECAL. [Oops, it may break reset handling for motor-off resets.] Cleanups in fd_timeout(): - Renamed to fd_iotimeout() to make it clearer that it is only used for i/o. - Don't handle the bp == 0 case. This case can't happen for i/o. - Don't check for controller-busy. We know it must be. - Don't print anything. retrier() already prints too much for normal errors. - Fudge the state differently so that the state machine advances fdc->retry and the status is invalid (perhaps this should fudge a valid state like the one for WP). - Style fixes.
-rw-r--r--sys/dev/fdc/fdc.c117
-rw-r--r--sys/i386/isa/fd.c117
-rw-r--r--sys/i386/isa/fdc.h3
-rw-r--r--sys/isa/fd.c117
-rw-r--r--sys/isa/fdc.h3
5 files changed, 187 insertions, 170 deletions
diff --git a/sys/dev/fdc/fdc.c b/sys/dev/fdc/fdc.c
index f432e51..bf39b9b 100644
--- a/sys/dev/fdc/fdc.c
+++ b/sys/dev/fdc/fdc.c
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
@@ -44,7 +43,7 @@
* SUCH DAMAGE.
*
* from: @(#)fd.c 7.4 (Berkeley) 5/25/91
- * $Id: fd.c,v 1.118 1998/07/15 09:59:30 bde Exp $
+ * $Id: fd.c,v 1.119 1998/07/18 03:15:33 bde Exp $
*
*/
@@ -241,7 +240,7 @@ static void fd_turnon(fdu_t);
static void fdc_reset(fdc_p);
static int fd_in(fdcu_t, int *);
static void fdstart(fdcu_t);
-static timeout_t fd_timeout;
+static timeout_t fd_iotimeout;
static timeout_t fd_pseudointr;
static int fdstate(fdcu_t, fdc_p);
static int retrier(fdcu_t);
@@ -266,6 +265,7 @@ static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */
#define RECALWAIT 9
#define MOTORWAIT 10
#define IOTIMEDOUT 11
+#define RESETCOMPLETE 12
#ifdef FDC_DEBUG
static char const * const fdstates[] =
@@ -281,7 +281,8 @@ static char const * const fdstates[] =
"SEEKWAIT",
"RECALWAIT",
"MOTORWAIT",
-"IOTIMEDOUT"
+"IOTIMEDOUT",
+"RESETCOMPLETE",
};
/* CAUTION: fd_debug causes huge amounts of logging output */
@@ -1382,15 +1383,15 @@ fdstart(fdcu_t fdcu)
}
static void
-fd_timeout(void *arg1)
+fd_iotimeout(void *arg1)
{
- fdcu_t fdcu = (fdcu_t)arg1;
- fdu_t fdu = fdc_data[fdcu].fdu;
- int baseport = fdc_data[fdcu].baseport;
- struct buf *bp;
+ fdc_p fdc;
+ fdcu_t fdcu;
int s;
- bp = bufq_first(&fdc_data[fdcu].head);
+ fdcu = (fdcu_t)arg1;
+ fdc = fdc_data + fdcu;
+ TRACE1("fd%d[fd_iotimeout()]", fdc->fdu);
/*
* Due to IBM's brain-dead design, the FDC has a faked ready
@@ -1398,34 +1399,15 @@ fd_timeout(void *arg1)
* issued if there's no diskette in the drive will _never_
* complete, and must be aborted by resetting the FDC.
* Many thanks, Big Blue!
+ * The FDC must not be reset directly, since that would
+ * interfere with the state machine. Instead, pretend that
+ * the command completed but was invalid. The state machine
+ * will reset the FDC and retry once.
*/
-
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] = NE7_ST0_IC_RC;
- fdc_data[fdcu].state = IOTIMEDOUT;
- if( fdc_data[fdcu].retry < 6)
- fdc_data[fdcu].retry = 6;
- }
- else
- {
- fdc_data[fdcu].fd = (fd_p) 0;
- fdc_data[fdcu].fdu = -1;
- fdc_data[fdcu].state = DEVIDLE;
- }
+ fdc->status[0] = NE7_ST0_IC_IV;
+ fdc->flags &= ~FDC_STAT_VALID;
+ fdc->state = IOTIMEDOUT;
fdintr(fdcu);
splx(s);
}
@@ -1470,7 +1452,7 @@ static int
fdstate(fdcu_t fdcu, fdc_p fdc)
{
struct subdev *sd;
- int read, format, head, sec = 0, sectrac, st0, cyl, st3;
+ int read, format, head, i, sec = 0, sectrac, st0, cyl, st3;
unsigned blknum = 0, b_cylinder = 0;
fdu_t fdu = fdc->fdu;
fd_p fd;
@@ -1557,7 +1539,11 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
{
set_motor(fdcu, fd->fdsu, TURNON);
}
- fdc->state = DOSEEK;
+ if (fdc->flags & FDC_NEEDS_RESET) {
+ fdc->state = RESETCTLR;
+ fdc->flags &= ~FDC_NEEDS_RESET;
+ } else
+ fdc->state = DOSEEK;
break;
case DOSEEK:
if (b_cylinder == (unsigned)fd->track)
@@ -1669,6 +1655,9 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
if(fd_sense_drive_status(fdc, &st3) != 0)
{
/* stuck controller? */
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
+ format ? bp->b_bcount : fdblk,
+ fdc->dmachan);
fdc->retry = 6; /* reset the beast */
return(retrier(fdcu));
}
@@ -1706,6 +1695,9 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
0))
{
/* controller fell over */
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
+ format ? bp->b_bcount : fdblk,
+ fdc->dmachan);
fdc->retry = 6;
return(retrier(fdcu));
}
@@ -1725,18 +1717,24 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
0))
{
/* the beast is sleeping again */
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
+ format ? bp->b_bcount : fdblk,
+ fdc->dmachan);
fdc->retry = 6;
return(retrier(fdcu));
}
}
fdc->state = IOCOMPLETE;
- fd->tohandle = timeout(fd_timeout, (caddr_t)fdcu, hz);
+ fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz);
return(0); /* will return later */
case IOCOMPLETE: /* IO DONE, post-analyze */
- untimeout(fd_timeout, (caddr_t)fdcu, fd->tohandle);
+ untimeout(fd_iotimeout, (caddr_t)fdcu, fd->tohandle);
if (fd_read_status(fdc, fd->fdsu))
{
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
+ format ? bp->b_bcount : fdblk,
+ fdc->dmachan);
if (fdc->retry < 6)
fdc->retry = 6; /* force a reset */
return retrier(fdcu);
@@ -1747,7 +1745,7 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
/* FALLTHROUGH */
case IOTIMEDOUT:
- isa_dmadone(bp->b_flags, bp->b_data+fd->skip,
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
format ? bp->b_bcount : fdblk, fdc->dmachan);
if (fdc->status[0] & NE7_ST0_IC)
{
@@ -1793,16 +1791,18 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
case RESETCTLR:
fdc_reset(fdc);
fdc->retry++;
+ fdc->state = RESETCOMPLETE;
+ return (0);
+ case RESETCOMPLETE:
+ /*
+ * Discard all the results from the reset so that they
+ * can't cause an unexpected interrupt later.
+ */
+ for (i = 0; i < 4; i++)
+ (void)fd_sense_int(fdc, &st0, &cyl);
fdc->state = STARTRECAL;
- break;
+ /* Fall through. */
case STARTRECAL:
- /* XXX clear the fdc results from the last reset, if any. */
- {
- int i;
- for (i = 0; i < 4; i++)
- (void)fd_sense_int(fdc, &st0, &cyl);
- }
-
if(fd_cmd(fdcu,
2, NE7CMD_RECAL, fdu,
0)) /* Recalibrate Function */
@@ -1854,12 +1854,17 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
{
return(0); /* time's not up yet */
}
- /*
- * since the controller was off, it has lost its
- * idea about the current track it were; thus,
- * recalibrate the bastard
- */
- fdc->state = STARTRECAL;
+ if (fdc->flags & FDC_NEEDS_RESET) {
+ fdc->state = RESETCTLR;
+ fdc->flags &= ~FDC_NEEDS_RESET;
+ } else {
+ /*
+ * If all motors were off, then the controller was
+ * reset, so it has lost track of the current
+ * cylinder. Recalibrate to handle this case.
+ */
+ fdc->state = STARTRECAL;
+ }
return(1); /* will return immediatly */
default:
printf("fdc%d: Unexpected FD int->", fdcu);
@@ -1959,9 +1964,9 @@ retrier(fdcu)
fdc->fd->skip = 0;
biodone(bp);
fdc->state = FINDWORK;
+ fdc->flags |= FDC_NEEDS_RESET;
fdc->fd = (fd_p) 0;
fdc->fdu = -1;
- /* XXX abort current command, if any. */
return(1);
}
fdc->retry++;
diff --git a/sys/i386/isa/fd.c b/sys/i386/isa/fd.c
index f432e51..bf39b9b 100644
--- a/sys/i386/isa/fd.c
+++ b/sys/i386/isa/fd.c
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
@@ -44,7 +43,7 @@
* SUCH DAMAGE.
*
* from: @(#)fd.c 7.4 (Berkeley) 5/25/91
- * $Id: fd.c,v 1.118 1998/07/15 09:59:30 bde Exp $
+ * $Id: fd.c,v 1.119 1998/07/18 03:15:33 bde Exp $
*
*/
@@ -241,7 +240,7 @@ static void fd_turnon(fdu_t);
static void fdc_reset(fdc_p);
static int fd_in(fdcu_t, int *);
static void fdstart(fdcu_t);
-static timeout_t fd_timeout;
+static timeout_t fd_iotimeout;
static timeout_t fd_pseudointr;
static int fdstate(fdcu_t, fdc_p);
static int retrier(fdcu_t);
@@ -266,6 +265,7 @@ static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */
#define RECALWAIT 9
#define MOTORWAIT 10
#define IOTIMEDOUT 11
+#define RESETCOMPLETE 12
#ifdef FDC_DEBUG
static char const * const fdstates[] =
@@ -281,7 +281,8 @@ static char const * const fdstates[] =
"SEEKWAIT",
"RECALWAIT",
"MOTORWAIT",
-"IOTIMEDOUT"
+"IOTIMEDOUT",
+"RESETCOMPLETE",
};
/* CAUTION: fd_debug causes huge amounts of logging output */
@@ -1382,15 +1383,15 @@ fdstart(fdcu_t fdcu)
}
static void
-fd_timeout(void *arg1)
+fd_iotimeout(void *arg1)
{
- fdcu_t fdcu = (fdcu_t)arg1;
- fdu_t fdu = fdc_data[fdcu].fdu;
- int baseport = fdc_data[fdcu].baseport;
- struct buf *bp;
+ fdc_p fdc;
+ fdcu_t fdcu;
int s;
- bp = bufq_first(&fdc_data[fdcu].head);
+ fdcu = (fdcu_t)arg1;
+ fdc = fdc_data + fdcu;
+ TRACE1("fd%d[fd_iotimeout()]", fdc->fdu);
/*
* Due to IBM's brain-dead design, the FDC has a faked ready
@@ -1398,34 +1399,15 @@ fd_timeout(void *arg1)
* issued if there's no diskette in the drive will _never_
* complete, and must be aborted by resetting the FDC.
* Many thanks, Big Blue!
+ * The FDC must not be reset directly, since that would
+ * interfere with the state machine. Instead, pretend that
+ * the command completed but was invalid. The state machine
+ * will reset the FDC and retry once.
*/
-
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] = NE7_ST0_IC_RC;
- fdc_data[fdcu].state = IOTIMEDOUT;
- if( fdc_data[fdcu].retry < 6)
- fdc_data[fdcu].retry = 6;
- }
- else
- {
- fdc_data[fdcu].fd = (fd_p) 0;
- fdc_data[fdcu].fdu = -1;
- fdc_data[fdcu].state = DEVIDLE;
- }
+ fdc->status[0] = NE7_ST0_IC_IV;
+ fdc->flags &= ~FDC_STAT_VALID;
+ fdc->state = IOTIMEDOUT;
fdintr(fdcu);
splx(s);
}
@@ -1470,7 +1452,7 @@ static int
fdstate(fdcu_t fdcu, fdc_p fdc)
{
struct subdev *sd;
- int read, format, head, sec = 0, sectrac, st0, cyl, st3;
+ int read, format, head, i, sec = 0, sectrac, st0, cyl, st3;
unsigned blknum = 0, b_cylinder = 0;
fdu_t fdu = fdc->fdu;
fd_p fd;
@@ -1557,7 +1539,11 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
{
set_motor(fdcu, fd->fdsu, TURNON);
}
- fdc->state = DOSEEK;
+ if (fdc->flags & FDC_NEEDS_RESET) {
+ fdc->state = RESETCTLR;
+ fdc->flags &= ~FDC_NEEDS_RESET;
+ } else
+ fdc->state = DOSEEK;
break;
case DOSEEK:
if (b_cylinder == (unsigned)fd->track)
@@ -1669,6 +1655,9 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
if(fd_sense_drive_status(fdc, &st3) != 0)
{
/* stuck controller? */
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
+ format ? bp->b_bcount : fdblk,
+ fdc->dmachan);
fdc->retry = 6; /* reset the beast */
return(retrier(fdcu));
}
@@ -1706,6 +1695,9 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
0))
{
/* controller fell over */
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
+ format ? bp->b_bcount : fdblk,
+ fdc->dmachan);
fdc->retry = 6;
return(retrier(fdcu));
}
@@ -1725,18 +1717,24 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
0))
{
/* the beast is sleeping again */
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
+ format ? bp->b_bcount : fdblk,
+ fdc->dmachan);
fdc->retry = 6;
return(retrier(fdcu));
}
}
fdc->state = IOCOMPLETE;
- fd->tohandle = timeout(fd_timeout, (caddr_t)fdcu, hz);
+ fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz);
return(0); /* will return later */
case IOCOMPLETE: /* IO DONE, post-analyze */
- untimeout(fd_timeout, (caddr_t)fdcu, fd->tohandle);
+ untimeout(fd_iotimeout, (caddr_t)fdcu, fd->tohandle);
if (fd_read_status(fdc, fd->fdsu))
{
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
+ format ? bp->b_bcount : fdblk,
+ fdc->dmachan);
if (fdc->retry < 6)
fdc->retry = 6; /* force a reset */
return retrier(fdcu);
@@ -1747,7 +1745,7 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
/* FALLTHROUGH */
case IOTIMEDOUT:
- isa_dmadone(bp->b_flags, bp->b_data+fd->skip,
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
format ? bp->b_bcount : fdblk, fdc->dmachan);
if (fdc->status[0] & NE7_ST0_IC)
{
@@ -1793,16 +1791,18 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
case RESETCTLR:
fdc_reset(fdc);
fdc->retry++;
+ fdc->state = RESETCOMPLETE;
+ return (0);
+ case RESETCOMPLETE:
+ /*
+ * Discard all the results from the reset so that they
+ * can't cause an unexpected interrupt later.
+ */
+ for (i = 0; i < 4; i++)
+ (void)fd_sense_int(fdc, &st0, &cyl);
fdc->state = STARTRECAL;
- break;
+ /* Fall through. */
case STARTRECAL:
- /* XXX clear the fdc results from the last reset, if any. */
- {
- int i;
- for (i = 0; i < 4; i++)
- (void)fd_sense_int(fdc, &st0, &cyl);
- }
-
if(fd_cmd(fdcu,
2, NE7CMD_RECAL, fdu,
0)) /* Recalibrate Function */
@@ -1854,12 +1854,17 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
{
return(0); /* time's not up yet */
}
- /*
- * since the controller was off, it has lost its
- * idea about the current track it were; thus,
- * recalibrate the bastard
- */
- fdc->state = STARTRECAL;
+ if (fdc->flags & FDC_NEEDS_RESET) {
+ fdc->state = RESETCTLR;
+ fdc->flags &= ~FDC_NEEDS_RESET;
+ } else {
+ /*
+ * If all motors were off, then the controller was
+ * reset, so it has lost track of the current
+ * cylinder. Recalibrate to handle this case.
+ */
+ fdc->state = STARTRECAL;
+ }
return(1); /* will return immediatly */
default:
printf("fdc%d: Unexpected FD int->", fdcu);
@@ -1959,9 +1964,9 @@ retrier(fdcu)
fdc->fd->skip = 0;
biodone(bp);
fdc->state = FINDWORK;
+ fdc->flags |= FDC_NEEDS_RESET;
fdc->fd = (fd_p) 0;
fdc->fdu = -1;
- /* XXX abort current command, if any. */
return(1);
}
fdc->retry++;
diff --git a/sys/i386/isa/fdc.h b/sys/i386/isa/fdc.h
index 8524123..fdaf694 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: fdc.h,v 1.9 1997/09/17 20:16:16 tegge Exp $
+ * $Id: fdc.h,v 1.10 1998/07/11 06:35:39 bde Exp $
*
*/
@@ -55,6 +55,7 @@ struct fdc_data
#define FDC_TAPE_BUSY 0x04
#define FDC_STAT_VALID 0x08
#define FDC_HAS_FIFO 0x10
+#define FDC_NEEDS_RESET 0x20
struct fd_data *fd;
int fdu; /* the active drive */
int state;
diff --git a/sys/isa/fd.c b/sys/isa/fd.c
index f432e51..bf39b9b 100644
--- a/sys/isa/fd.c
+++ b/sys/isa/fd.c
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
@@ -44,7 +43,7 @@
* SUCH DAMAGE.
*
* from: @(#)fd.c 7.4 (Berkeley) 5/25/91
- * $Id: fd.c,v 1.118 1998/07/15 09:59:30 bde Exp $
+ * $Id: fd.c,v 1.119 1998/07/18 03:15:33 bde Exp $
*
*/
@@ -241,7 +240,7 @@ static void fd_turnon(fdu_t);
static void fdc_reset(fdc_p);
static int fd_in(fdcu_t, int *);
static void fdstart(fdcu_t);
-static timeout_t fd_timeout;
+static timeout_t fd_iotimeout;
static timeout_t fd_pseudointr;
static int fdstate(fdcu_t, fdc_p);
static int retrier(fdcu_t);
@@ -266,6 +265,7 @@ static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */
#define RECALWAIT 9
#define MOTORWAIT 10
#define IOTIMEDOUT 11
+#define RESETCOMPLETE 12
#ifdef FDC_DEBUG
static char const * const fdstates[] =
@@ -281,7 +281,8 @@ static char const * const fdstates[] =
"SEEKWAIT",
"RECALWAIT",
"MOTORWAIT",
-"IOTIMEDOUT"
+"IOTIMEDOUT",
+"RESETCOMPLETE",
};
/* CAUTION: fd_debug causes huge amounts of logging output */
@@ -1382,15 +1383,15 @@ fdstart(fdcu_t fdcu)
}
static void
-fd_timeout(void *arg1)
+fd_iotimeout(void *arg1)
{
- fdcu_t fdcu = (fdcu_t)arg1;
- fdu_t fdu = fdc_data[fdcu].fdu;
- int baseport = fdc_data[fdcu].baseport;
- struct buf *bp;
+ fdc_p fdc;
+ fdcu_t fdcu;
int s;
- bp = bufq_first(&fdc_data[fdcu].head);
+ fdcu = (fdcu_t)arg1;
+ fdc = fdc_data + fdcu;
+ TRACE1("fd%d[fd_iotimeout()]", fdc->fdu);
/*
* Due to IBM's brain-dead design, the FDC has a faked ready
@@ -1398,34 +1399,15 @@ fd_timeout(void *arg1)
* issued if there's no diskette in the drive will _never_
* complete, and must be aborted by resetting the FDC.
* Many thanks, Big Blue!
+ * The FDC must not be reset directly, since that would
+ * interfere with the state machine. Instead, pretend that
+ * the command completed but was invalid. The state machine
+ * will reset the FDC and retry once.
*/
-
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] = NE7_ST0_IC_RC;
- fdc_data[fdcu].state = IOTIMEDOUT;
- if( fdc_data[fdcu].retry < 6)
- fdc_data[fdcu].retry = 6;
- }
- else
- {
- fdc_data[fdcu].fd = (fd_p) 0;
- fdc_data[fdcu].fdu = -1;
- fdc_data[fdcu].state = DEVIDLE;
- }
+ fdc->status[0] = NE7_ST0_IC_IV;
+ fdc->flags &= ~FDC_STAT_VALID;
+ fdc->state = IOTIMEDOUT;
fdintr(fdcu);
splx(s);
}
@@ -1470,7 +1452,7 @@ static int
fdstate(fdcu_t fdcu, fdc_p fdc)
{
struct subdev *sd;
- int read, format, head, sec = 0, sectrac, st0, cyl, st3;
+ int read, format, head, i, sec = 0, sectrac, st0, cyl, st3;
unsigned blknum = 0, b_cylinder = 0;
fdu_t fdu = fdc->fdu;
fd_p fd;
@@ -1557,7 +1539,11 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
{
set_motor(fdcu, fd->fdsu, TURNON);
}
- fdc->state = DOSEEK;
+ if (fdc->flags & FDC_NEEDS_RESET) {
+ fdc->state = RESETCTLR;
+ fdc->flags &= ~FDC_NEEDS_RESET;
+ } else
+ fdc->state = DOSEEK;
break;
case DOSEEK:
if (b_cylinder == (unsigned)fd->track)
@@ -1669,6 +1655,9 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
if(fd_sense_drive_status(fdc, &st3) != 0)
{
/* stuck controller? */
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
+ format ? bp->b_bcount : fdblk,
+ fdc->dmachan);
fdc->retry = 6; /* reset the beast */
return(retrier(fdcu));
}
@@ -1706,6 +1695,9 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
0))
{
/* controller fell over */
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
+ format ? bp->b_bcount : fdblk,
+ fdc->dmachan);
fdc->retry = 6;
return(retrier(fdcu));
}
@@ -1725,18 +1717,24 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
0))
{
/* the beast is sleeping again */
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
+ format ? bp->b_bcount : fdblk,
+ fdc->dmachan);
fdc->retry = 6;
return(retrier(fdcu));
}
}
fdc->state = IOCOMPLETE;
- fd->tohandle = timeout(fd_timeout, (caddr_t)fdcu, hz);
+ fd->tohandle = timeout(fd_iotimeout, (caddr_t)fdcu, hz);
return(0); /* will return later */
case IOCOMPLETE: /* IO DONE, post-analyze */
- untimeout(fd_timeout, (caddr_t)fdcu, fd->tohandle);
+ untimeout(fd_iotimeout, (caddr_t)fdcu, fd->tohandle);
if (fd_read_status(fdc, fd->fdsu))
{
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
+ format ? bp->b_bcount : fdblk,
+ fdc->dmachan);
if (fdc->retry < 6)
fdc->retry = 6; /* force a reset */
return retrier(fdcu);
@@ -1747,7 +1745,7 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
/* FALLTHROUGH */
case IOTIMEDOUT:
- isa_dmadone(bp->b_flags, bp->b_data+fd->skip,
+ isa_dmadone(bp->b_flags, bp->b_data + fd->skip,
format ? bp->b_bcount : fdblk, fdc->dmachan);
if (fdc->status[0] & NE7_ST0_IC)
{
@@ -1793,16 +1791,18 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
case RESETCTLR:
fdc_reset(fdc);
fdc->retry++;
+ fdc->state = RESETCOMPLETE;
+ return (0);
+ case RESETCOMPLETE:
+ /*
+ * Discard all the results from the reset so that they
+ * can't cause an unexpected interrupt later.
+ */
+ for (i = 0; i < 4; i++)
+ (void)fd_sense_int(fdc, &st0, &cyl);
fdc->state = STARTRECAL;
- break;
+ /* Fall through. */
case STARTRECAL:
- /* XXX clear the fdc results from the last reset, if any. */
- {
- int i;
- for (i = 0; i < 4; i++)
- (void)fd_sense_int(fdc, &st0, &cyl);
- }
-
if(fd_cmd(fdcu,
2, NE7CMD_RECAL, fdu,
0)) /* Recalibrate Function */
@@ -1854,12 +1854,17 @@ fdstate(fdcu_t fdcu, fdc_p fdc)
{
return(0); /* time's not up yet */
}
- /*
- * since the controller was off, it has lost its
- * idea about the current track it were; thus,
- * recalibrate the bastard
- */
- fdc->state = STARTRECAL;
+ if (fdc->flags & FDC_NEEDS_RESET) {
+ fdc->state = RESETCTLR;
+ fdc->flags &= ~FDC_NEEDS_RESET;
+ } else {
+ /*
+ * If all motors were off, then the controller was
+ * reset, so it has lost track of the current
+ * cylinder. Recalibrate to handle this case.
+ */
+ fdc->state = STARTRECAL;
+ }
return(1); /* will return immediatly */
default:
printf("fdc%d: Unexpected FD int->", fdcu);
@@ -1959,9 +1964,9 @@ retrier(fdcu)
fdc->fd->skip = 0;
biodone(bp);
fdc->state = FINDWORK;
+ fdc->flags |= FDC_NEEDS_RESET;
fdc->fd = (fd_p) 0;
fdc->fdu = -1;
- /* XXX abort current command, if any. */
return(1);
}
fdc->retry++;
diff --git a/sys/isa/fdc.h b/sys/isa/fdc.h
index 8524123..fdaf694 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: fdc.h,v 1.9 1997/09/17 20:16:16 tegge Exp $
+ * $Id: fdc.h,v 1.10 1998/07/11 06:35:39 bde Exp $
*
*/
@@ -55,6 +55,7 @@ struct fdc_data
#define FDC_TAPE_BUSY 0x04
#define FDC_STAT_VALID 0x08
#define FDC_HAS_FIFO 0x10
+#define FDC_NEEDS_RESET 0x20
struct fd_data *fd;
int fdu; /* the active drive */
int state;
OpenPOWER on IntegriCloud