diff options
Diffstat (limited to 'sys/i386/isa/fd.c')
-rw-r--r-- | sys/i386/isa/fd.c | 778 |
1 files changed, 486 insertions, 292 deletions
diff --git a/sys/i386/isa/fd.c b/sys/i386/isa/fd.c index 8f3e1dc9cc946..f4632b9f2cb1e 100644 --- a/sys/i386/isa/fd.c +++ b/sys/i386/isa/fd.c @@ -6,6 +6,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 +41,7 @@ * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fd.c,v 1.21.2.1 1994/03/07 02:08:43 rgrimes Exp $ + * $Id: fd.c,v 1.26 1994/05/22 12:30:32 joerg Exp $ * */ @@ -59,28 +65,29 @@ #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 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 @@ -132,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]; /***********************************************************************\ @@ -153,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 */ int ftu); +int ftclose(/* dev_t */ int, int); +void ftstrategy(struct buf *); +int ftioctl(/* dev_t */ int, int, caddr_t, int, struct proc *); +int ftdump(/* dev_t */ int); +int ftsize(/* dev_t */ int); +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 */ int); +void fdintr(fdcu_t); +int Fdopen(/* dev_t */int, int); +int fdclose(/* dev_t */int, int); +void fdstrategy(struct buf *); +int fdioctl(/* dev_t */ int, 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 void fd_turnoff(caddr_t arg1, int arg2); +static void fd_motor_on(caddr_t arg1, int arg2); +static void fd_turnon(fdu_t); +static void fdc_reset(fdc_p); +static void fdstart(fdcu_t); +static void fd_timeout(caddr_t, int); +static void fd_pseudointr(caddr_t, int); +static int fdstate(fdcu_t, fdc_p); static int retrier(fdcu_t); +static int fdformat(/* dev_t */ int, struct fd_formb *, struct proc *); + #define DEVIDLE 0 #define FINDWORK 1 @@ -188,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 void fd_turnoff(caddr_t, int); - /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ -static int fdprobe(struct isa_device *); -static int fdattach(struct isa_device *); - struct isa_driver fdcdriver = { fdprobe, fdattach, "fdc", }; @@ -214,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 */ @@ -303,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) { @@ -346,15 +392,10 @@ fdattach(dev) fd->type = NO_TYPE; break; } - - fd_turnoff((caddr_t)fdu, 0); - hdr = 1; } printf("\n"); - /* Set transfer to 500kbps */ - outb(fdc->baseport+fdctl,0); /*XXX*/ - return 1; + return (1); } int @@ -365,102 +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); - 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(caddr_t arg1, int arg2) { @@ -470,11 +456,12 @@ fd_turnoff(caddr_t arg1, int arg2) 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(caddr_t arg1, int arg2) { fdu_t fdu = (fdu_t)arg1; @@ -490,27 +477,39 @@ fd_motor_on(caddr_t arg1, int arg2) 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)); } /****************************************************************************/ @@ -522,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 } @@ -546,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); } @@ -647,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 * @@ -671,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) @@ -683,38 +760,43 @@ fdstart(fdcu) splx(s); } +/* ARGSUSED */ static void fd_timeout(caddr_t arg1, int arg2) { 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; @@ -730,11 +812,13 @@ fd_timeout(caddr_t arg1, int arg2) } /* just ensure it has the right spl */ +/* ARGSUSED */ static void fd_pseudointr(caddr_t arg1, int arg2) { fdcu_t fdcu = (fdcu_t)arg1; int s; + s = splbio(); fdintr(fdcu); splx(s); @@ -756,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; @@ -787,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"); @@ -805,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) @@ -818,6 +904,8 @@ fdstate(fdcu, fdc) fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; + 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 * @@ -838,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; @@ -848,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)); } } @@ -884,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); @@ -931,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 @@ -970,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 */ @@ -1042,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: @@ -1061,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)|3); + bp->b_dev = makedev(major(bp->b_dev), + (FDUNIT(minor(bp->b_dev))<<3)|3); 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); @@ -1101,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); @@ -1119,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; @@ -1138,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)); @@ -1182,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; @@ -1221,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: @@ -1243,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: + */ |