/* * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986,1987,1988 * LICENSED MATERIALS - PROPERTY OF IBM * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083 */ /* $Header: /usr/src/sys/rt/dev/RCS/fd.c,v 1.12 1994/06/04 15:46:28 md Exp $ */ /* $ACIS:fd.c 12.0$ */ /* $Source: /usr/src/sys/rt/dev/RCS/fd.c,v $ */ #if !defined(lint) && !defined(NO_RCS_HDRS) static char *rcsid = "$Header: /usr/src/sys/rt/dev/RCS/fd.c,v 1.12 1994/06/04 15:46:28 md Exp $"; #endif #include "fd.h" #if NFD > 0 #include #include #include #include #include #include #include #include #include #include #include "rt/include/ioccvar.h" #include "rt/rt/debug.h" #include #include #define spl_high() _spl1() caddr_t real_buf_addr(); #define REAL_BUF_ADDR(bp,bufaddr) (((bp->b_flags & B_PHYS) == 0) ? \ ((caddr_t)vtop(bufaddr)) : real_buf_addr(bp,bufaddr)) #include #include #include #include "rt/dev/dmavar.h" #include "rt/dev/fdreg.h" #include "rt/dev/fdvar.h" #include "rt/include/fdio.h" #include "rt/include/dkio.h" /* per-controller data */ struct fd_ctlr fd_ctlr[NFDC]; /* per-drive buffers */ struct buf rfdbuf[NFD]; /* buffers for raw I/O */ struct buf fdutab[NFD]; /* per drive buffers */ /* per-drive data */ struct fd_softc fd_softc[NFD]; /* End of per-drive data */ struct iocc_device *fddinfo[NFD]; struct iocc_ctlr *fdminfo[NFDC]; int fdprobe(), fdslave(), fdattach(), fddgo(), fdint(), fdwatch(), fdcallint(), fdverify(), fdstrategy(); /* dma_minphys */ unsigned (*dma_get_minphys())(); caddr_t fdstd[] = { (caddr_t)0xf00003f2, (caddr_t)0xf0000372, 0 }; /* because the floppy disk first register is write-only it appears to * not exist to autoconf. * we set idr_csr to 2 to find a read/write register */ struct iocc_driver fdcdriver = { fdprobe, fdslave, fdattach, fddgo, fdstd, "fd", fddinfo, "fdc", fdminfo, fdint, 2 }; int fdwstart; char fdresult(); /* * Drive description table. */ struct fdst fdst[] = { /*sec/track heads sec/cyl #cyl byte/disk gpl fgpl step xfer drive delay_factor */ /* 360K diskette in 1.2M drive */ { 9, 2, 2 * 9, 40,2*9*40*FDBPS,0x23,0x50, 0xdf,0x01, FD1200K,"fd360k", 2}, /* 1.2M diskette */ { 15, 2, 2 * 15, 80,2*15*80*FDBPS,0x1b,0x54,0xdf,0x00, FD1200K,"fd1200k", 2}, /* 360K diskette in 360k drive */ { 9, 2, 2 * 9, 40,2*9*40*FDBPS,0x2a,0x50, 0xdf,0x02, FD360K ,"fd360k", 1}, /* 720K in a 1.4M diskette (3.5 in.) UNTESTED */ { 9, 2, 2*9, 80, 2*9*80*FDBPS,0x1b,0x54,0xdf,0x00, FD1440K, "fd720k", 2}, /* 1.4M in a 1.4M diskette (3.5 in.) */ { 18, 2, 2*18, 80, 2*18*80*FDBPS,0x1b,0x54,0xdf,0x00, FD1440K, "fd1.4m", 2}, /* 720K in a 720K diskette (3.5 in.) UNTESTED */ { 9, 2, 2*9, 80, 2*9*80*FDBPS,0x2a,0x50,0xdf,0x02, FD720K, "fd720k", 1}, }; /* * This flag disables verify on writes. In the future it may be possible to * turn this on or off depending on the hardware installed. */ int nofdverify = 1; struct iocc_ctlr *fdicprobed; fdprobe(reg, ic) register struct fddevice *reg; register struct iocc_ctlr *ic; { DEBUGF(fddebug > 8,printf("fdprobe entered\n");); reg->fd_digout = FDMOTORON(0) | FDINTENABLE; delay(FDMOTORSTART/1000); reg->fd_digout = 0; PROBE_DELAY(FDMOTORSTART); fdicprobed = ic; return (PROBE_OK); } int fdfrc144 = 0; fdslave(iod, reg) register struct iocc_device *iod; register struct fddevice *reg; { register int unit = iod->iod_unit; struct fd_ctlr *fdc = &fd_ctlr[fdicprobed->ic_ctlr]; struct fd_softc *sc = &fd_softc[unit]; int cyl,count,status0,status3,i; DEBUGF(fddebug > 8,printf("fdslave entered; ctlr=%d, unit=%d\n", fdicprobed->ic_ctlr, iod->iod_unit);); /* motor on */ reg->fd_digout = FDMOTORON(unit) | FDINTENABLE | iod->iod_slave; delay(FDMOTORSTART/1000); fddinfo[unit] = iod; iod->iod_addr = (caddr_t) reg; /* find out if drive exists */ fdrecal(unit); delay(FDRECAL/1000); DEBUGF(fddebug,printf("status3=%b\n",sc->sc_sr3,FDSR3);); /* get result */ fdread_status(unit); /* * if the trak0 line is not on, then try once more for a * quick retry (it appears that on the 1.2 MB drive if * the head is in the last cylinder that a fdrecal doesn't * quite work the first time). */ if (((status3=sc->sc_sr3) & FDTRAK0) == 0) { fdrecal(unit); delay(FDRECAL/10000); /* get result */ fdread_status(unit); } /* * if the trak0 line is not on, the floppy drive is not there. */ if (((status3=sc->sc_sr3) & FDTRAK0) == 0) { /* ignore the interrupt */ fdc->fdc_state = FDS_SLAVE; /* turn off motor */ reg->fd_digout = 0; return(0); } /* * All the rest of this code is to determine which * type of drive is attached. First we do a seek to * 0,0 to clear the hardware. Then we seek to sector * 50. This will "peg" 360K drives to cylinder 40, but * leave 1.2Meg at cylinder 50). Then we start seeking * back toward 0. The 360K will get back to 0 first. */ fdc->fdc_state = FDS_SLAVE; sc->sc_dens = 1; /* don't double step seeks */ fdseek(unit,0,0); count = fdwaitint(fdc); /* get result */ fdcmd(unit,FDSENI); status0 = fdresult(unit,status0); cyl = fdresult(unit,cyl); DEBUGF(fddebug,printf("track = %d status=0x%b status3=%b count=%d\n", cyl,status0,FDSR0,status3,FDSR3,count);); /* seek to test sector */ fdc->fdc_state = FDS_SLAVE; fdseek(unit,50,0); count = fdwaitint(fdc); delay(FDRECAL/1000); /* get result */ fdcmd(unit,FDSENI); status0 = fdresult(unit,status0); cyl = fdresult(unit,cyl); DEBUGF(fddebug,printf("track = %d status=0x%b status3=%b count=%d\n", cyl,status0,FDSR0,status3,FDSR3,count);); for (i=10; i >=0; i--) { /* sense drive status */ fdread_status(unit); if ((status3=sc->sc_sr3) & FDTRAK0) { break; } /* seek to next track */ fdc->fdc_state = FDS_SLAVE; fdseek(unit,i,0); count = fdwaitint(fdc); fdcmd(unit,FDSENI); status0 = fdresult(unit,status0); cyl = fdresult(unit,cyl); DEBUGF(fddebug, printf("track = %d status=0x%b status3=%b count=%d\n", cyl,status0,FDSR0,status3,FDSR3,count);); } DEBUGF(fddebug,printf("count=%d\n",i);); if ( i > 0) { printf("fd%d: 360K drive\n",unit); sc->sc_drive = FD360K; /* } else if (fdfrc144) { */ /* } else if ((1<iod_flags) { */ } else if (iod->iod_flags) { printf("fd%d: 1.44M drive\n",unit); sc->sc_drive = FD1440K; } else { printf("fd%d: 1.2M drive\n",unit); sc->sc_drive = FD1200K; } /* turn off motor */ reg->fd_digout = 0; return(1); } fdwaitint(fdc) struct fd_ctlr *fdc; { int timeout=10000000; int count = 0; while ((fdc->fdc_state == FDS_SLAVE) && (timeout--)) { DELAY(1); count++; } DELAY(5); if (timeout == -1) printf("seek error on init\n"); return(count); } fdattach(iod) register struct iocc_device *iod; { int unit = iod->iod_unit; register struct buf *dp = &fdutab[unit]; register struct fd_softc *sc = &fd_softc[unit]; register struct fd_ctlr *fdc = &fd_ctlr[iod->iod_ctlr]; register struct iocc_ctlr *ic = fdminfo[iod->iod_ctlr]; /* initialize variables */ dp->b_actf = NULL; dp->b_actl = NULL; dp->b_active = 0; ic->ic_dmachannel = DMA_CHAN2; ic->ic_dmaflags = (DMA_SINGLE|DMA_INCREMENT|DMA_PAGE|DMA_PHYSICAL); sc->sc_dens=0; sc->sc_motor=0; sc->sc_open=0; sc->sc_bopen = 0; sc->sc_copen = 0; sc->sc_curcyl=0; sc->sc_flags=0; sc->sc_suppress=0; sc->sc_verbuf = NULL; fdc->fdc_lstdens=0; fdc->fdc_drives=0; fdc->fdc_state=FDS_IDLE; return (1); } /* * handle the openning and closing of the block/character device separately * to keep track whether or not the floppy is really open. */ fdbopen(dev,flags) /* block open */ dev_t dev; int flags; { fdkopen(dev,flags,1); } fdcopen(dev,flags) /* character open */ dev_t dev; int flags; { fdkopen(dev,flags,0); } fdkopen(dev, flags,block) dev_t dev; int flags; int block; { register int unit = FDUNIT(dev); register struct fd_softc *sc; register struct iocc_device *iod; register int error,s; struct fd_ctlr *fdc; DEBUGF(fddebug > 8,printf("fdopen entered\n");); trace(TR_F_OPE, pack(dev, block), flags); if (unit >= NFD || (iod = fddinfo[unit]) == 0 || iod->iod_alive == 0) return (ENXIO); sc = &fd_softc[unit]; fdc = &fd_ctlr[iod->iod_ctlr]; loop: s=FD_SPL(); if (sc->sc_open == 0) { /* don't try to autodensity while someone else is */ if (sc->sc_flags & FDF_LOCK) { splx(s); sleep((caddr_t) sc,FDPRI); goto loop; } else { sc->sc_flags |= FDF_LOCK; } /* * read device status to determine if * a floppy is present in the drive and * what density it is */ if (sc->sc_motor == 0) { error=fdautodensity(dev,flags); if (error) { sc->sc_flags &= ~FDF_LOCK; splx(s); wakeup((caddr_t) sc); return(error); } } if (fdwstart++ == 0) { fdc->fdc_tocnt = 0; timeout(fdwatch, (caddr_t)0, hz); /* start watchdog */ } DEBUGF(fddebug > 8,printf("fdopen: \n");); sc->sc_flags &= ~FDF_LOCK; fdc->fdc_drives++; /* number of open drives on ctlr*/ } else { if (sc->sc_flags & FDF_LOCK) { DEBUGF(fddebug > 8, printf("fdopen: EBUSY sc->sc_open=%d \n", sc->sc_open);); splx(s); return (EBUSY); } if ((sc->sc_sr3 & FDNOWRITE) && (flags & FWRITE)) { uprintf("fd%d: write protected.\n",unit); splx(s); return (EIO); } } if ((nofdverify == 0) && (flags & FWRITE) && (sc->sc_verbuf == NULL)) { sc->sc_verbuf = geteblk(fdst[sc->sc_dens].nsect*FDBPS); } sc->sc_open++; if (block) { sc->sc_bopen++; } else { sc->sc_copen++; } splx(s); wakeup((caddr_t) sc); DEBUGF(fddebug > 8,printf("fdopen exit\n");); return (0); } /* do the hardware initialization */ fdautodensity(dev,flags) dev_t dev; int flags; { register int unit = FDUNIT(dev); register struct fd_softc *sc = &fd_softc[unit]; register struct fd_ctlr *fdc = &fd_ctlr[fddinfo[unit]->iod_ctlr]; int fdretries = ((flags == FDFORMREQ) ? FDFORMRETRIES : FDRETRIES); DEBUGF(fddebug > 8,printf("fdautodensity entered\n");); sc->sc_errcnt=0; if ((FDTYPE(dev)) && (flags != FDINFOREQ)) { struct buf *bp = geteblk(FDBPS); DEBUGF(fddebug,printf("fixed density type device ");); switch(sc->sc_dens=(FDTYPE(dev)>>4)) { case FD1200K1200K: /* all these types are valid */ case FD360K360K: case FD720K1440K: case FD1440K1440K: case FD720K720K: break; default: /* invalid drive type, assume 1.2M */ sc->sc_dens = FD1200K1200K; } DEBUGF(fddebug,printf("density = %d, drive =%d\n",sc->sc_dens,sc->sc_drive);); sc->sc_flags = (unit & FDF_DEVTYPE) | FDF_LOCK; bp->b_dev = dev; bp->b_flags = B_SETUP|B_BUSY; bp->b_blkno = 0; bp->b_error = 0; bp->av_forw = 0; trace(TR_F_AUT, pack(dev, bp->b_bcount), bp->b_blkno); (void) fdstrategy(bp); trace(TR_F_WAI, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); iowait(bp); trace(TR_F_REL, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); brelse(bp); sc->sc_retries = -1; sc->sc_drive = fdst[sc->sc_dens].drive; } else { for (sc->sc_retries=0;(sc->sc_retries < fdretries) && (flags != FDINFOREQ);sc->sc_retries++) { struct buf *bp = geteblk(FDBPS); sc->sc_flags = (unit & FDF_DEVTYPE) | FDF_LOCK; bp->b_dev = dev; bp->b_flags = B_SETUP|B_BUSY; bp->b_blkno = 0; bp->b_error = 0; bp->av_forw = 0; trace(TR_F_AUT, pack(dev, bp->b_bcount), bp->b_blkno); (void) fdstrategy(bp); trace(TR_F_WAI, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); iowait(bp); /* this last piece of code tries to read sector 1 * track 0 * if it can then we are at the correct * density, otherwise * try different density */ DEBUGF(fddebug,printf("density = %d, retries= %d",sc->sc_dens,sc->sc_retries);); DEBUGF(fddebug,printf(" drive =%d\n",sc->sc_drive);); sc->sc_flags = (unit & FDF_DEVTYPE) | FDF_LOCK; bp->b_dev = dev; bp->b_flags = B_READ|B_BUSY; bp->b_error = 0; bp->b_blkno = fdst[sc->sc_dens].nspc; /* seek to cylinder 1 */ bp->av_forw = 0; bp->b_bcount = FDBPS; sc->sc_curcyl = 0; /* force a seek each round */ sc->sc_resid = 0; /* * read device status to determine if * a floppy is present in the drive and * what density it is */ (void) fdstrategy(bp); DEBUGF(fddebug,printf("strategy complete\n");); if (fdwstart++ == 0) { fdc = &fd_ctlr[fddinfo[unit]->iod_mi->ic_ctlr]; fdc->fdc_tocnt = 0; timeout(fdwatch, (caddr_t)0, hz); /* start watchdog */ } trace(TR_F_WAI, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); iowait(bp); DEBUGF(fddebug,printf("Floppy in drive\n");); /* check command status register after operation */ if (((sc->sc_sr0 & FDSR0MASK) != 0) || (sc->sc_curcyl != 1) || (sc->sc_hrdhd != 0) || (bp->b_flags & B_ERROR)) { if (++sc->sc_errcnt > 1) { /* force two errors before changing den */ sc->sc_errcnt = 0; switch (sc->sc_drive) { default: case FDUNKNOWN: if (++sc->sc_dens >FDMAXTYPE) sc->sc_dens=FD360K1200K; break; case FD360K: sc->sc_dens=FD360K360K; break; case FD1200K: sc->sc_dens = (sc->sc_dens == FD1200K1200K) ? FD360K1200K : FD1200K1200K; break; case FD1440K: sc->sc_dens = (sc->sc_dens == FD1440K1440K) ? FD720K1440K : FD1440K1440K; break; case FD720K: sc->sc_dens = FD720K720K; } } } else { trace(TR_F_REL, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); brelse(bp); sc->sc_retries = -1; sc->sc_drive = fdst[sc->sc_dens].drive; break; } trace(TR_F_REL, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); brelse(bp); if (sc->sc_flags & FDF_TIMEOUT) { fdmotoroff(unit); uprintf("fd%d: door open or hardware fault.\n",unit); return(ENXIO); /* return error if no disk */ } if ((sc->sc_sr3 & FDNOWRITE) && (flags & FWRITE)) { fdmotoroff(unit); uprintf("fd%d: write protected.\n",unit); return (EIO); } } /* end for */ } /* end if */ if (flags == FDINFOREQ) { sc->sc_flags |= FDF_INFO; } else { if ((sc->sc_sr3 & FDNOWRITE) && (flags & FWRITE)) { fdmotoroff(unit); uprintf("fd%d: write protected.\n",unit); return (EIO); } DEBUGF(fddebug,printf("Flags = %x ",flags);); if (sc->sc_retries > 0) { if (flags == FDFORMREQ) sc->sc_flags |= FDF_FORMAT; else { fdmotoroff(unit); uprintf("fd%d: bad or unformatted floppy.\n",unit); return (ENXIO); } } DEBUGF(fddebug > 0, { switch (sc->sc_dens) { case FD360K1200K: printf("fd: opening floppy drive for 360k in 1.2M\n"); break; case FD1200K1200K: printf("fd: opening floppy drive for 1.2M in 1.2M\n"); break; case FD360K360K: printf("fd: opening floppy drive for 360K in 360K\n"); break; case FD720K1440K: printf("fd: opening floppy drive for 720K in 1440K\n"); break; case FD1440K1440K: printf("fd: opening floppy drive for 1440K in 1440K\n"); break; case FD720K720K: printf("fd: opening floppy drive for 720K in 720K\n"); } }); } return (0); } fdread(dev, uio) dev_t dev; struct uio *uio; { int unit = FDUNIT(dev); struct fd_softc *sc = &fd_softc[unit]; DEBUGF(fddebug > 0x70,printf("fdread entered\n");); if (uio->uio_offset < 0) return (ENXIO); if (sc->sc_flags & (FDF_FORMAT | FDF_INFO)) return (ENXIO); return (physio(fdstrategy, &rfdbuf[unit], dev, B_READ, dma_get_minphys(&fd_ctlr[fddinfo[unit]->iod_ctlr]), uio)); } fdwrite(dev, uio) dev_t dev; struct uio *uio; { int unit = FDUNIT(dev); struct fd_softc *sc = &fd_softc[unit]; DEBUGF(fddebug > 0x70,printf("fdwrite entered\n");); if (uio->uio_offset < 0) return (ENXIO); if (sc->sc_flags & (FDF_FORMAT | FDF_INFO)) return (ENXIO); return (physio(fdstrategy, &rfdbuf[unit], dev, B_WRITE, dma_get_minphys(&fd_ctlr[fddinfo[unit]->iod_ctlr]), uio)); } /* * Control routine: * processes four kinds of requests: * * (1) Set density (i.e., format the diskette) according to * that specified data parameter * (2) Arrange for the next sector to be written with a deleted- * data mark. * (3) Report whether the last sector read had a deleted-data mark * (4) Report the density of the diskette in the indicated drive * (since the density it automatically determined by the driver, * this is the only way to let an application program know the * density) * * Requests relating to deleted-data marks can be handled right here. * A "set density" (format) request, however, must additionally be * processed through "fdstart", just like a read or write request. */ fdioctl(dev, cmd, data, flag) dev_t dev; caddr_t data; { int unit = FDUNIT(dev); struct fd_softc *sc; int s; DEBUGF(fddebug > 8,printf("fdioctl entered\n");); sc = &fd_softc[unit]; switch (cmd) { case FDIOC_FORMAT: if ((flag & FWRITE) == 0) return(EBADF); if (sc->sc_open > 1) return (EBUSY); switch (*(int *)data) { case 0: switch (sc->sc_drive) { case FDUNKNOWN: return(EIO); case FD360K: sc->sc_dens=FD360K360K; break; case FD720K: sc->sc_dens=FD360K360K; break; case FD1200K: sc->sc_dens=FD360K1200K; break; case FD1440K: sc->sc_dens=FD720K1440K; break; } break; case 1: switch (sc->sc_drive) { case FDUNKNOWN: case FD360K: case FD720K: return(EIO); case FD1200K: sc->sc_dens=FD1200K1200K; break; case FD1440K: sc->sc_dens=FD1440K1440K; break; } break; default: return(EIO); } return (fdformat(dev)); /* get density */ case FDIOC_GDENS: *(int *)data = sc->sc_dens; return (0); /* Get the type of drive */ case FDIOC_GTYPE: *(int *)data = sc->sc_drive; return(0); /* Set the type of drive */ case FDIOC_STYPE: switch (*(int *)data) { case FD1200K: sc->sc_dens=FD1200K1200K; break; case FD360K: sc->sc_dens=FD360K360K; break; case FD1440K: sc->sc_dens=FD1440K1440K; break; case FD720K: sc->sc_dens=FD720K720K; break; case FDUNKNOWN: break; default: return(EINVAL); } sc->sc_drive = *(int *)data; return(0); /* reset adapter */ case FDIOC_RESET: s=FD_SPL(); fdrecal(unit); delay(FDRECAL/1000); fdreset(fddinfo[unit]->iod_ctlr); splx(s); return (0); case DKIOCGPART: { register struct dkpart *dk = (struct dkpart *) data; register struct fdst *st = &fdst[sc->sc_dens]; dk->dk_size = st->nspc * st->ncyl; /* size */ dk->dk_start = 0; /* start */ dk->dk_blocksize = FDBPS; /* device blocksize */ dk->dk_ntrack = st->ntrak; /* tracks/cylinder */ dk->dk_nsector = st->nsect; /* sectors/track */ dk->dk_ncyl = st->ncyl; /* cylinders */ strcpy(dk->dk_name,st->name); /* copy name */ return(0); } case DIOCGDINFO: { register struct disklabel *dl = (struct disklabel *) data; register struct fdst *st = &fdst[sc->sc_dens]; dl->d_magic = DISKMAGIC; dl->d_type = DTYPE_FLOPPY; dl->d_subtype = 0; switch (sc->sc_drive) { case FD360K: strcpy(dl->d_typename, /*st->name*/"fd360k"); break; case FD720K: strcpy(dl->d_typename, /*st->name*/"fd720k"); break; case FD1200K: strcpy(dl->d_typename, /*st->name*/"fd1200k"); break; case FD1440K: strcpy(dl->d_typename, /*st->name*/"fd1440k"); break; } /* disk geometry: */ dl->d_secsize = 512; dl->d_nsectors = st->nsect; dl->d_ntracks = st->ntrak; dl->d_ncylinders = st->ncyl; dl->d_secpercyl = st->nspc; dl->d_secperunit = st->ncyl * st->ntrak * st->nsect; dl->d_sparespertrack = 0; dl->d_sparespercyl = 0; dl->d_acylinders = 0; dl->d_magic2 = DISKMAGIC; dl->d_rpm = 6; dl->d_interleave = 1; /* u_short d_checksum; /* xor of data incl. partitions */ /* filesystem and partition information: */ dl->d_npartitions = 1; dl->d_partitions[0].p_size = dl->d_secperunit; dl->d_partitions[0].p_offset = 0; dl->d_partitions[0].p_fsize = 512; dl->d_partitions[0].p_fstype = FS_BSDFFS; dl->d_partitions[0].p_frag = 8; dl->d_partitions[0].p_cpg = 16; dl->d_bbsize = BBSIZE; dl->d_sbsize = SBSIZE; return(0); } } return(ENOTTY); /* sigh */ } /* * use the two different close routines for block and char. */ fdbclose(dev, flag) /* block */ dev_t dev; int flag; { fdclose(dev,flag,1); } fdcclose(dev, flag) /* character */ dev_t dev; int flag; { fdclose(dev,flag,0); } /*ARGSUSED*/ fdclose(dev, flag,block) dev_t dev; int flag,block; { register struct fd_softc *sc = &fd_softc[FDUNIT(dev)]; register struct fd_ctlr *fdc = &fd_ctlr[fddinfo[FDUNIT(dev)]->iod_ctlr]; trace(TR_F_CLO, pack(dev, block), flag); if (block) { sc->sc_bopen = 0; } else { sc->sc_copen = 0; } if ((sc->sc_bopen == 0) && (sc->sc_copen == 0)) { sc->sc_open = 0; fdc->fdc_drives--; } DEBUGF(fddebug > 8,printf("fdclose: dev=0x%x, sc_open=%d\n", dev, sc->sc_open);); } /* * Strategy Routine: * Arguments: * Pointer to I/O buffer structure * R/W function flag * Function: * Start up the device */ fdstrategy(bp) register struct buf *bp; { struct iocc_device *iod; register struct buf *dp; int s, unit = FDUNIT(bp->b_dev); struct fd_softc *sc = &fd_softc[unit]; register struct fdst *st = &fdst[sc->sc_dens]; DEBUGF(fddebug > 8,printf("fdstrategy entered\n");); if (sc->sc_open == 0 && !(sc->sc_flags&FDF_LOCK)) { printf("fd not open! bp=%x b_flags=%x b_blkno=%x b_bcount=%x\n", bp, bp->b_flags, bp->b_blkno, bp->b_bcount); } trace(TR_F_STR, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); if (unit >= NFD) goto bad; iod = fddinfo[unit]; if (iod == 0 || iod->iod_alive == 0) goto bad; if (bp->b_blkno < 0) { DEBUGF(fddebug > 0,printf("fdstrategy: requested a sector out of range\n");); goto bad; } /* if total transfer execeeds disk capacity, truncate b_bcount */ sc->sc_trunc=0; if (((bp->b_blkno * BSIZE) + bp->b_bcount) > st->nbpd) { if (bp->b_blkno*BSIZE >= st->nbpd) { /* over end of disk? */ bp->b_resid = bp->b_bcount; iodone(bp); return; } sc->sc_trunc=bp->b_bcount; bp->b_bcount = st->nbpd - (bp->b_blkno * BSIZE); if (bp->b_bcount < 0) bp->b_bcount = 0; sc->sc_trunc-=bp->b_bcount; DEBUGF(fddebug > 0,printf("fdstrategy: transfer count too large, reduced to = %d (dec) \n", bp->b_bcount);); } if (bp->b_bcount <= 0) { bp->b_resid = sc->sc_trunc; goto done; } s = FD_SPL(); DEBUGF(fddebug > 1,printf("fdstrat: bp=0x%x, fl=0x%x, un=%d, bl=%d, cnt=%d\n", bp, bp->b_flags, unit, bp->b_blkno, bp->b_bcount);); bp->b_cylin = bp->b_blkno; /* don't care to calculate trackno */ dp = &fdutab[unit]; disksort(dp, bp); /* */ if (dp->b_active == 0) { fdustart(iod); bp = &iod->iod_mi->ic_tab; DEBUGF(fddebug > 8,printf("fdstrategy after fdustart bp=0x%x\n", bp);); if (bp->b_actf && bp->b_active == 0) { fdstart(iod->iod_mi); } else { trace(TR_F_STR, pack(bp->b_dev, 1), -1); } } else { trace(TR_F_STR, pack(bp->b_dev, 2), -1); } splx(s); DEBUGF(fddebug > 8,printf("fdstrategy exit\n");); return; bad: trace(TR_F_STR, pack(bp->b_dev, 3), -1); bp->b_error = ENXIO; bp->b_flags |= B_ERROR; done: if (bp->b_resid != 0) { trace(TR_F_STR, pack(bp->b_dev, 0), bp->b_resid); } else { trace(TR_F_STR, pack(bp->b_dev, 4), -1); } iodone(bp); return; } /* * Unit start routine. * Put this unit on the ready queue for the controller */ fdustart(iod) register struct iocc_device *iod; { struct buf *dp = &fdutab[iod->iod_unit]; struct iocc_ctlr *ic = iod->iod_mi; DEBUGF(fddebug > 8,printf("fdustart entered ic=0x%x\n", ic);); dp->b_forw = NULL; if (ic->ic_tab.b_actf == NULL) ic->ic_tab.b_actf = dp; else ic->ic_tab.b_actl->b_forw = dp; ic->ic_tab.b_actl = dp; dp->b_active++; } /* * Controller start routine. * Start a new transfer or continue a multisector * transfer. If this is a new transfer (dp->b_active == 1) * save the start address of the data buffer and the total * byte count in the soft control structure. These are * restored into the buffer structure when the transfer has * been completed, before calling 'iodone'. */ fdstart(ic) register struct iocc_ctlr *ic; { register struct fdst *st; register struct fd_ctlr *fdc; register struct fd_softc *sc; struct buf *dp, *bp; int unit; register int sector; register char cylinder, head; DEBUGF(fddebug > 8,printf("fdstart entered\n");); if (ic->ic_tab.b_active) { trace(TR_F_STA, -1, ic->ic_tab.b_active); return; } loop: if ((dp = ic->ic_tab.b_actf) == NULL) { trace(TR_F_STA, -1, -1); return; } if ((bp = dp->b_actf) == NULL) { ic->ic_tab.b_actf = dp->b_forw; goto loop; } trace(TR_F_STA, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); ic->ic_tab.b_active++; unit = FDUNIT(bp->b_dev); sc = &fd_softc[unit]; fdc = &fd_ctlr[ic->ic_ctlr]; if (fdc->fdc_lstdens == sc->sc_dens) fdctrl(unit,fdc->fdc_ctrl); /* select drive */ else fdmotoron(unit); /* set proper xfer rate */ st = &fdst[sc->sc_dens]; /* save multi-sector info */ if (dp->b_active == 1) { sc->sc_resid = bp->b_bcount; /* */ sc->sc_bcnt = bp->b_bcount; sc->sc_uaddr = bp->b_un.b_addr; sc->sc_reduce = 0; dp->b_active++; } bp->b_bcount = sc->sc_resid; if (bp->b_flags & B_SETUP) { fdc->fdc_state= FDS_SETUP; (void) fdint(ic->ic_ctlr); return; } DEBUGF(fddebug > 8,printf("fdstart: ");); /* for either read or write, we must SEEK */ fdc->fdc_tocnt = 0; /* enable interupts */ fdctrl(unit,fdc->fdc_ctrl | FDINTENABLE); sector = ((bp->b_blkno * BSIZE) + (sc->sc_bcnt - sc->sc_resid)) /FDBPS; cylinder = sector / (unsigned)(st->nspc); sector %= st->nspc; head = (sector / st->nsect) & 1; sector %= (st->nsect); sc->sc_cyl = cylinder; sc->sc_sec = (char) sector; sc->sc_head = head; /* if we're having problems try transfering only one sector */ if (((++ic->ic_tab.b_errcnt >= FD_REDUCE_XFER) && ((sc->sc_flags & FDF_FORMAT) == 0)) || (sc->sc_reduce)) { sc->sc_maxbyt = FDBPS; sc->sc_reduce = 1; } else { sc->sc_maxbyt = (st->nsect - sector) * FDBPS; } if (bp->b_bcount > sc->sc_maxbyt) bp->b_bcount = sc->sc_maxbyt; if ((sc->sc_curcyl != cylinder) || (sc->sc_hrdhd != head)) { fdc->fdc_state = FDS_SEEK; /* send seek commands to adapter */ fdseek(unit,cylinder, head); } else { DEBUGF(fddebug > 8,printf("fdstart: no seek needed...sc->sc_curcyl=0x%x req. cyl=0x%x\n", sc->sc_curcyl, cylinder);); ic->ic_dmabuf = bp; if (dma_setup(ic)) { ic->ic_tab.b_active = 0; bp->b_error = EIO; bp->b_flags |= B_ERROR; /* disable interupts */ fdctrl(unit,fdc->fdc_ctrl & ~FDINTENABLE); trace(TR_F_STA, bp->b_dev<<16 | bp->b_flags, -1); iodone(bp); } DEBUGF(fddebug > 8,printf("fdstart: after dma_setup\n");); } DEBUGF(fddebug > 8,printf("fdstart: exiting");); } /* ARGSUSED */ fddgo(ic,length,addr,bp) struct iocc_ctlr *ic; int length,addr; /* note: addr is a dma address, NOT a pointer */ struct buf *bp; { register int unit = FDUNIT(bp->b_dev); struct fd_softc *sc = &fd_softc[unit]; struct fd_ctlr *fdc = &fd_ctlr[ic->ic_ctlr]; register struct fdst *st = &fdst[sc->sc_dens]; register char sector = sc->sc_sec; register char cylinder = sc->sc_cyl; register char head = sc->sc_head; DEBUGF(fddebug > 8,printf("fddgo: entered\n");); sc->sc_error = 0; DEBUGF(fddebug > 8,printf("\rfd: cyl %x sec %x count %x ", cylinder, sector, bp->b_bcount);); /* */ /* enable the channel */ dma_go(ic->ic_dmachannel); trace(TR_F_DGO, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); /* determine if READ or WRITE or FORMAT */ if (bp->b_flags & B_CTRL) { /* FORMAT */ fdc->fdc_state = FDS_FORMAT; fdcmd(unit,FDFORMAT); /* start format (1 track) */ fdcmd(unit,(head << FDBIT2)+(char) fddinfo[unit]->iod_slave); fdcmd(unit,FDSECSIZE); /* byte per sector (2= 512)*/ fdcmd(unit,st->nsect); /* sectors per cylinder */ fdcmd(unit,st->fgpl); /* Gap length */ fdcmd(unit,FDPATTERN); /* pattern */ DEBUGF(fddebug > 8,printf("fddgo: exiting\n");); return; } if (bp->b_flags & B_READ) { /* READ */ if (bp->b_flags & B_VERIFY) { fdc->fdc_state = FDS_VERIFY; } else { fdc->fdc_state = FDS_READ; } fdcmd(unit,FDREAD); } else { /* WRITE */ fdc->fdc_state = FDS_WRITE; fdcmd(unit,FDWRITE); } /* end read/write if */ /* common disk setup code for read and write */ fdcmd(unit,(head << FDBIT2) + (char) fddinfo[unit]->iod_slave); fdcmd(unit,cylinder); /* cylinder */ fdcmd(unit,head); /* put head number at bit 1 */ fdcmd(unit,(sector + 1)); /* sector number (1-n) */ fdcmd(unit,FDSECSIZE); /* sector size */ fdcmd(unit,st->nsect); /* last sector on track */ fdcmd(unit,st->gpl); /* gap length */ fdcmd(unit,FDSECBASE); /* sector size */ DEBUGF(fddebug > 8,printf("fddgo: exiting\n");); } fdint(ctlr) int ctlr; { int unit; struct iocc_ctlr *ic = fdminfo[ctlr]; register struct buf *bp, *dp; register struct fd_softc *sc; register struct fdst *st; struct iocc_device *iod; struct fd_ctlr *fdc = &fd_ctlr[ctlr]; DEBUGF(fddebug,printf("fdint: entered; ctlr=%d ",ctlr);); /* special state used by fdslave only */ if (fdc->fdc_state == FDS_SLAVE) { fdc->fdc_state = FDS_IDLE; DELAY(5); trace(TR_F_INT, -1, 1); return; } if (!ic->ic_tab.b_active) { DEBUGF(fddebug > 1,printf("fdint: ic not active\n");); trace(TR_F_INT, -1, 2); return (1); } dp = ic->ic_tab.b_actf; if (!dp->b_active) { DEBUGF(fddebug > 1,printf("fdint: dp->b_active not active\n");); trace(TR_F_INT, -1, 3); return(1); } bp = dp->b_actf; trace(TR_F_INT, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); unit = FDUNIT(bp->b_dev); sc = &fd_softc[unit]; st = &fdst[sc->sc_dens]; iod = fddinfo[unit]; fdc->fdc_tocnt = 0; DEBUGF(fddebug>0,printf(" fdintstate= %s ",statestring[fdc->fdc_state].value);); fdctrl(unit,fdc->fdc_ctrl & ~FDINTENABLE); if (sc->sc_flags & (FDF_TIMEOUT)) { if ((fdc->fdc_state == FDS_READ) || (fdc->fdc_state == FDS_FORMAT) || (fdc->fdc_state == FDS_WRITE) || (fdc->fdc_state == FDS_VERIFY)) { dma_done(ic->ic_dmachannel); fdunload_results(unit); } goto giveup; } switch (fdc->fdc_state) { /* * Incomplete commands. Perform next step * and return. Note that b_active is set on * entrance and, therefore, also on exit. */ case FDS_SEEK: fdc->fdc_state = FDS_SKDON; timeout(fdcallint, ctlr, st->delay*FDHDSETTLE); DEBUGF(fddebug,printf("\n");); return(0); case FDS_SKDON: /* done with seek */ fdcmd(unit,FDSENI); /* get result */ sc->sc_sr0 = fdresult(unit,sc->sc_sr0); sc->sc_curcyl = fdresult(unit,sc->sc_curcyl); sc->sc_hrdcyl=sc->sc_curcyl; sc->sc_curcyl = (sc->sc_dens == 0) ? (sc->sc_curcyl / 2) : sc->sc_curcyl; (void) fdwait(unit,FDSTATDAB | FDSTATDBB, 0); /* wait for READY */ DEBUGF(fddebug,printf("Seek ret = 0x%b", (int) sc->sc_sr0,FDSR0);); /* if error */ if ((sc->sc_sr0 & FDSR0MASK) != 0) goto error; fdctrl(unit,fdc->fdc_ctrl | FDINTENABLE); ic->ic_dmabuf = bp; if (dma_setup(ic)) { ic->ic_tab.b_errcnt = 0; ic->ic_tab.b_active = 0; bp->b_error = EIO; bp->b_flags |= B_ERROR; goto cleanup; } DEBUGF(fddebug,printf("\n");); return (0); case FDS_READ: case FDS_FORMAT: dma_done(ic->ic_dmachannel); DEBUGF(fddebug > 8, { if (bp->b_flags & B_READ) printf("fdint: B_READ; "); else printf("fdint: B_CTRL: "); }); fdunload_results(unit); /* if error */ if ((sc->sc_sr0 & FDSR0MASK) != 0) goto error; fdc->fdc_state = FDS_IDLE; /* end read/write/format */ goto done; case FDS_WRITE: dma_done(ic->ic_dmachannel); DEBUGF(fddebug > 8, { printf("fdint: B_WRITE; "); }); fdunload_results(unit); /* if error */ if ((sc->sc_sr0 & FDSR0MASK) != 0) goto error; fdc->fdc_state = FDS_IDLE; /* end read/write/format */ /* * if no verify is set then just continue */ if (nofdverify) goto done; /* * verify what we just wrote */ ic->ic_tab.b_active = 0; sc->sc_write_retry = ic->ic_tab.b_errcnt; ic->ic_tab.b_errcnt = 0; sc->sc_verify = bp; dp->b_actf = sc->sc_verbuf; dp->b_actf->b_flags = B_VERIFY | B_READ | B_BUSY; dp->b_actf->b_blkno = bp->b_blkno; dp->b_actf->b_dev = bp->b_dev; dp->b_actf->b_error = bp->b_error; dp->b_actf->av_forw = bp->av_forw; dp->b_actf->b_bcount = bp->b_bcount; fdstart(ic); return(0); case FDS_VERIFY: dma_done(ic->ic_dmachannel); DEBUGF(fddebug > 8, { printf("fdint: B_VERIFY; "); }); fdunload_results(unit); /* if error */ if ((sc->sc_sr0 & FDSR0MASK) != 0) /* retry read */ goto error; /* complete the task off-level */ timeout(fdverify,ctlr,0); return(0); /* * verify complete */ case FDS_VER_DONE: /* continue with the write */ sc->sc_verify->b_error = bp->b_error; bp = sc->sc_verify; dp->b_actf = bp; fdc->fdc_state = FDS_IDLE; /* end read/write/format */ goto done; /* * error detected, retry the write. */ case FDS_VER_ERROR: sc->sc_verify->b_error = bp->b_error; ic->ic_tab.b_errcnt = sc->sc_write_retry; bp = sc->sc_verify; dp->b_actf = bp; fdc->fdc_state = FDS_WRITE; goto error; case FDS_SETUP: /* don't reset the controller if the other unit is open */ if (fdc->fdc_drives == 0) fdctrl(unit,fdc->fdc_ctrl & FDRST); sc->sc_error = 0; fdmotoron(unit); fdcmd(unit,FDSPEC); fdcmd(unit,st->step); fdcmd(unit,FDLOAD); fdrecal(unit); DEBUGF(fddebug,printf ("Fdrecall completed\n");); if (sc->sc_error) { bp->b_error=EIO; bp->b_flags=B_ERROR; } fdc->fdc_state = FDS_DONE; timeout(fdcallint,ctlr,st->delay*FDRECALDELAY); DEBUGF(fddebug,printf("\n");); return(0); case FDS_RECAL: fdc->fdc_state = FDS_IDLE; ic->ic_tab.b_active = 0; fdstart(ic); DEBUGF(fddebug,printf("\n");); return(0); case FDS_DONE: fdc->fdc_state = FDS_IDLE; ic->ic_tab.b_errcnt = 0; ic->ic_tab.b_active = 0; goto cleanup; default: printf("fd%d: state %s (reset)\n", unit, statestring[fdc->fdc_state].value); fdreset(ctlr); return (0); /* */ } error: /* * In case of an error: * (a) In case of TIMEOUT giveup. * (b) Otherwise try operation FD_ERROR_RETRY more times * (c) Make auto density do it's own retrying */ if (++ic->ic_tab.b_errcnt >= (sc->sc_open ? FD_ERROR_RETRY : 3)) { goto giveup; } fdrecal(unit); fdc->fdc_state=FDS_RECAL; timeout(fdcallint, ctlr, st->delay*FDRECALDELAY); DEBUGF(fddebug,printf("\n");); return (0); giveup: if (sc->sc_open == 0) goto done; /* * Hard I/O error -- * Current request is aborted, next request is started. */ sc->sc_trunc += sc->sc_resid; sc->sc_resid = 0; /* make sure the transfer is terminated */ ic->ic_tab.b_errcnt = 0; ic->ic_tab.b_active = 0; fdread_status(unit); if (!(sc->sc_suppress)) { printf("fd%d: hard error trk = %d sec =%d ",unit, (int) sc->sc_cyl, (int)sc->sc_sec); printf("Sr0 = 0x%b ",(int)sc->sc_sr0,FDSR0); printf("Sr1 = 0x%b ",(int)sc->sc_sr1,FDSR1); printf("Sr2 = 0x%b ",(int)sc->sc_sr2,FDSR2); printf("Sr3 = 0x%b ",(int)sc->sc_sr3,FDSR3); printf("Cyl= %d Sec=%d Head=%d St=%d ",sc->sc_hrdcyl, sc->sc_hrdsc,sc->sc_hrdhd,sc->sc_size); printf("state = %s.\n",statestring[fdc->fdc_state].value); } fdrecal(unit); bp->b_flags |= B_ERROR; fdc->fdc_state = FDS_IDLE; ic->ic_tab.b_active = 0; ic->ic_tab.b_errcnt = 0; goto cleanup; done: DEBUGF(fddebug > 8,printf("fdint: at done; ");); ic->ic_tab.b_active = 0; ic->ic_tab.b_errcnt = 0; /* multi-sector tricks */ if ((sc->sc_resid -= sc->sc_maxbyt) > 0) { bp->b_un.b_addr += sc->sc_maxbyt; fdstart(ic); DEBUGF(fddebug,printf("\n");); return (0); } cleanup: bp->b_resid = sc->sc_trunc; bp->b_un.b_addr = sc->sc_uaddr; bp->b_bcount = sc->sc_bcnt; dp->b_actf = bp->av_forw; if (bp->b_resid != 0) { trace(TR_F_INT, pack(bp->b_dev, -1), bp->b_resid); } else { trace(TR_F_INT, bp->b_dev<<16 | bp->b_flags, -1); } iodone(bp); fdc->fdc_state = FDS_IDLE; ic->ic_tab.b_actf = dp->b_forw; dp->b_active = 0; dp->b_errcnt = 0; DEBUGF(fddebug > 8,printf(".. bp=%x, new=%x\n", bp, dp->b_actf);); /* * If this unit has more work to do, * start it up right away */ if (dp->b_actf) fdustart(iod); fdstart(ic); DEBUGF(fddebug > 8,printf("fdint: exit; ");); DEBUGF(fddebug,printf("\n");); return (0); } fdcallint(ctlr) { int s = FD_SPL(); DEBUGF(fddebug>0,printf("fdcallint: ctlr=%d\n",ctlr);); (void) fdint(ctlr); splx(s); return; } #define MIN3(x,y,z) ( ((x)<(y)) ? (((z)<(x))?(z):(x)) : (((z)<(y))?(z):(y))) /* these shouln't be here XXX */ #define PAGE_SIZE 2048 #define PAGE_MASK (PAGE_SIZE-1) /* return bytes to the end of the page */ #define PAGE_END(x) (PAGE_SIZE - ((unsigned)(x) & PAGE_MASK)) /* * Handle the verify function off level */ fdverify(ctlr) int ctlr; { int unit,x; struct iocc_ctlr *ic = fdminfo[ctlr]; register struct buf *bp, *dp; register struct fd_softc *sc; struct fd_ctlr *fdc = &fd_ctlr[ctlr]; caddr_t new_buf,old_buf,real_old_buf,real_new_buf; dp = ic->ic_tab.b_actf; bp = dp->b_actf; unit = FDUNIT(bp->b_dev); sc = &fd_softc[unit]; fdc->fdc_tocnt = 0; old_buf = sc->sc_verify->b_un.b_addr; new_buf = bp->b_un.b_addr; real_new_buf = real_buf_addr(bp,new_buf); real_old_buf = REAL_BUF_ADDR(sc->sc_verify,old_buf); /* * verify that we read back the proper data * NOTE it is possible that the buffers do not * have the same page alignment. */ while (bp->b_bcount) { /* * compare the minimum of: * 1) the bytes left on the original buffer's page * 2) the bytes left on the new buffer's page * 3) the remaining bytes of the transfer that haven't * been compared */ int max_cmp = MIN3(PAGE_END(real_new_buf),bp->b_bcount, PAGE_END(real_old_buf)); #ifdef DEBUG /* make sure their are no infinite loops */ if ((max_cmp == 0) || (bp->b_bcount < 0)) { panic("fdverify bad count"); } #endif DEBUG GET_VR0(x); if (bcmp(real_new_buf,real_old_buf,max_cmp)) { SET_VR(x); /* * retry write */ fdc->fdc_state = FDS_VER_ERROR; /* * Keep the post transfer cleanup/retry code together */ fdcallint(ctlr); return; } SET_VR(x); /* * If bp->b_bcount is not 0, then we must have crossed a page * boundary (e.i. bp->b_bcount was not the minimum value in * the MIN3 case). Get the address of the new buffers for * comparing. */ if (bp->b_bcount -= max_cmp) { new_buf += max_cmp; old_buf += max_cmp; real_new_buf=real_buf_addr(bp,new_buf); real_old_buf=REAL_BUF_ADDR(sc->sc_verify,old_buf); } } fdc->fdc_state = FDS_VER_DONE; /* * Keep the post transfer cleanup/retry code together */ fdcallint(ctlr); return; } fdwatch() { register struct iocc_device *iod; register struct iocc_ctlr *ic; register struct fd_softc *sc; struct fd_ctlr *fdc; int i, dopen = 0; int s=FD_SPL(); for (i = 0; i < NFD; i++) { iod = fddinfo[i]; if (iod == 0 || iod->iod_alive == 0) continue; sc = &fd_softc[i]; if ((sc->sc_open == 0) && (fdutab[i].b_active == 0)) { /* * It's now safe to release sc_verbuf */ if (!nofdverify && sc->sc_verbuf) { register struct buf *bp = sc->sc_verbuf; bp->b_bcount = fdst[sc->sc_dens].nsect*FDBPS; trace(TR_F_REL, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); brelse(bp); sc->sc_verbuf = NULL; } if (sc->sc_motor >= 1) { if (--sc->sc_motor < 1) { /* turn off motor */ fdmotoroff(i); } else { dopen++; } } continue; } dopen++; } /* check the controller loop */ for (i = 0; i < NFDC; i++) { ic = fdminfo[i]; if (ic == 0 || ic->ic_alive == 0) continue; fdc = &fd_ctlr[ic->ic_ctlr]; if (++fdc->fdc_tocnt >= FD_MAXTIMEOUT) { if (ic->ic_tab.b_active) { int unit; unit = FDUNIT(ic->ic_tab.b_actf->b_actf->b_dev); if (fdc->fdc_tocnt == FD_MAXTIMEOUT) { sc->sc_flags|=FDF_TIMEOUT; /* * If an operation was started this will * generate and interupt */ fdctrl(0,fdc->fdc_ctrl & ~FDINTENABLE); } else { /* Something is really wrong call fdint by hand */ fdc->fdc_tocnt = 0; printf("fd%d: timeout\n", unit); (void) fdint(ic->ic_ctlr); } } else { fdc->fdc_tocnt = 0; } } } if (dopen) { timeout(fdwatch, (caddr_t)0, hz); } else { fdwstart = 0; } splx(s); } fdreset(ctlr) int ctlr; { register struct iocc_ctlr *dm; DEBUGF(fddebug > 8,printf("fdreset entered\n");); if ((dm = fdminfo[ctlr]) != 0 && dm->ic_alive != 0) { fd_ctlr[ctlr].fdc_state = FDS_IDLE; fdstart(dm); } } /* * Initiate a format command. */ fdformat(dev) dev_t dev; { register int unit = FDUNIT(dev); register struct fd_softc *sc = &fd_softc[unit]; struct buf *bp = geteblk(FDBPS); register struct fdst *st = &fdst[sc->sc_dens]; register int error=0,cyl,head,sect; register struct fdformdata *fdformdata = (struct fdformdata *) bp->b_un.b_addr; /* Set up */ bp->b_dev = dev; bp->b_error = 0; bp->b_flags |= B_SETUP; bp->b_blkno = 0; bp->av_forw = 0; bp->b_bcount = FDBPS; trace(TR_F_FOR, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); (void) fdstrategy(bp); trace(TR_F_WAI, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); iowait(bp); if (bp->b_flags & B_ERROR) { error = bp->b_error; goto out; } sc->sc_flags = FDF_FORMAT | FDF_LOCK; for (cyl = 0; cyl < st->ncyl ; cyl++) { for (head = 0; head < st->ntrak; head++) { int error_cnt = 0; format_retry: /* load the sector information */ for (sect = 0; sect < st->nsect; sect++) { fdformdata[sect].fdtrack = (char) cyl; fdformdata[sect].fdhead = (char) head; fdformdata[sect].fdsect = (char) sect + 1; fdformdata[sect].fdsize = FDSECSIZE; } DEBUGF(fddebug,{ register caddr_t tmpaddr = bp->b_un.b_addr; register int count; printf("\n"); for (count=0; count < st->nsect; count++) { char xcyl = *tmpaddr++; char xhd = *tmpaddr++; char xsec = *tmpaddr++; char xsize = *tmpaddr++; printf("<>cyl=%d head=%d sect=%d size=%d\n", xcyl,xhd,xsec,xsize); } printf("head = %d\n",head); }); /* Format */ bp->b_dev = dev; bp->b_flags = B_CTRL|B_BUSY; bp->b_error = 0; bp->b_blkno = (cyl * st->nspc) + (head * st->nsect); bp->b_bcount = st->nsect * FDBPS; bp->av_forw = 0; (void) fdstrategy(bp); trace(TR_F_WAI, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); iowait(bp); if (bp->b_flags & B_ERROR) { if (++error_cnt > FD_ERROR_RETRY) { error = EIO; break; } goto format_retry; } /* * if the no verify is on don't */ if (nofdverify) continue; /* verify the format */ for (sect = 0 ; sect < st->nsect ; sect++) { sc->sc_suppress = 1; bp->b_dev = dev; bp->b_flags = B_READ|B_BUSY; bp->b_error = 0; bp->b_blkno = (cyl * st->nspc) + (head * st->nsect) + sect; bp->b_bcount = FDBPS; bp->av_forw = 0; DEBUGF(fddebug,printf("vread block = %d\n", bp->b_blkno);); (void) fdstrategy(bp); trace(TR_F_WAI, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); iowait(bp); sc->sc_suppress = 0; /* no read errors, sector must exist */ if (bp->b_flags & B_ERROR) { if (++error_cnt > FD_ERROR_RETRY) { error = EIO; break; } goto format_retry; } } if (error) break; } if(error) break; } out: sc->sc_flags &= ~FDF_LOCK & ~FDF_FORMAT; trace(TR_F_REL, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); brelse(bp); return (error); } /* read extended status */ fdread_status(unit) int unit; { register struct fd_softc *sc = &fd_softc[unit]; fdcmd(unit,FDREADSTAT); /* read status */ fdcmd(unit,(char) fddinfo[unit]->iod_slave); /* drive head */ sc->sc_sr3 = fdresult(unit,FDWAITREAD); DEBUGF(fddebug,printf("fdread_status: 0x%b\n", (int) sc->sc_sr3, FDSR3);); } /* recalibrate floppy drive (reset head to track 0)*/ fdrecal(unit) int unit; { register struct fd_softc *sc = &fd_softc[unit]; fdcmd(unit,FDREST); fdcmd(unit, (char) fddinfo[unit]->iod_slave); fdread_status(unit); /* if ((sc->sc_sr3 & FDTRAK0)) sc->sc_error++; else */ sc->sc_curcyl = 0; } /* start support routines for fd.c (these aren't "classic" driver routines) */ fdmotoron(unit) { register struct iocc_device *iod = fddinfo[unit]; register struct fddevice *fdadapter = (struct fddevice *) iod->iod_addr; register struct fd_softc *sc = &fd_softc[unit]; register struct fd_ctlr *fdc = &fd_ctlr[iod->iod_ctlr]; fdctrl(unit,fdc->fdc_ctrl | FDMOTORON(iod->iod_slave)); /* set drive transfer rate here too */ fdadapter->fd_digin = fdst[sc->sc_dens].xfer; fdctrl(unit,fdc->fdc_ctrl | FDMOTORON(iod->iod_slave)); sc->sc_motor = FDMOTORWAIT; fdc->fdc_lstdens = sc->sc_dens; } /* turn off motor if other unit is open turn it on */ fdmotoroff(unit) { register struct iocc_device *iod = fddinfo[unit]; register struct iocc_device *iod2; register struct fd_softc *sc = &fd_softc[unit]; register struct fd_ctlr *fdc = &fd_ctlr[iod->iod_ctlr]; register int unit2; if (((unit2 = FDOTHERUNIT(unit)) < NFD) && (iod2 = fddinfo[unit2]) && (iod2->iod_alive)) { fdctrl(unit2,fdc->fdc_ctrl & FDMOTOROFF(iod->iod_slave)); } else { fdctrl(unit,fdc->fdc_ctrl & FDMOTOROFF(iod->iod_slave)); } sc->sc_motor = 0; } fdctrl(unit,cmd) unsigned char cmd; { register struct iocc_device *iod = fddinfo[unit]; register struct fddevice *fdadapter = (struct fddevice *) iod->iod_addr; register struct fd_ctlr *fdc = &fd_ctlr[iod->iod_ctlr]; fdc->fdc_ctrl = cmd & FDCTRLMASK; fdadapter->fd_digout = cmd | iod->iod_slave; } /* send command to floppy disk controller */ fdcmd(unit,cmd) char cmd; { register struct fddevice *fdadapter = (struct fddevice *) fddinfo[unit]->iod_addr; register struct fd_softc *sc = &fd_softc[unit]; if (fdwait(unit,FDSTATRQM | FDSTATDIO, FDSTATRQM) == 0) /* wait for READY */ sc->sc_error++; fdadapter->fd_data = cmd; /* */ } /* Wait for disk I/O to complete */ fdwait(unit,mask, compare) unsigned char mask; unsigned char compare; { register struct fddevice *fdadapter = (struct fddevice *) fddinfo[unit]->iod_addr; int timeout = FDWAITTIME; u_char status; while ((((status = fdadapter->fd_status) & mask) != compare) && (--timeout)) DELAY( (int) 2); if (timeout) return(1); else { DEBUGF(fddebug,{ printf("fdwait: mask 0x%b,",mask,FDSTAT); printf(" compare 0x%b, ",compare,FDSTAT); printf("status 0x%b,\n",status,FDSTAT); }); return(0); } } /* read result byte from the NEC diskette controller */ char fdresult(unit,value) char value; { register struct fddevice *fdadapter = (struct fddevice *) fddinfo[unit]->iod_addr; register struct fd_softc *sc = &fd_softc[unit]; char result,temp; if (fdwait(unit,FDSTATRQM | FDSTATDIO, FDSTATRQM | FDSTATDIO)) /* wait for READY */ result = fdadapter->fd_data; /* */ else { temp = fdadapter->fd_data; if (value = FDWAITREAD) result = temp; else result = value; sc->sc_error++; } return (result); } fdseek(unit,cylinder, head) unsigned char cylinder, head; { register struct fd_softc *sc = &fd_softc[unit]; DEBUGF(fddebug > 8,printf("fdseek: entered cyl=%d head=%d; sc->sc_dens=%d", cylinder, head, sc->sc_dens);); fdcmd(unit,FDSEEK); fdcmd(unit,(head << FDBIT2) + (char) fddinfo[unit]->iod_slave); /* double step on a 360k diskette in 1.2M drive */ if (sc->sc_dens == 0) fdcmd(unit,cylinder * FDDBLSTEP); /* cylinder */ else fdcmd(unit,cylinder); /* cylinder */ DEBUGF(fddebug > 8,printf("fdseek: exiting; ");); } /* unload command results */ fdunload_results(unit) { register struct fd_softc *sc = &fd_softc[unit]; sc->sc_sr0 = fdresult(unit,FDWAITREAD); sc->sc_sr1 = fdresult(unit,FDWAITREAD); sc->sc_sr2 = fdresult(unit,FDWAITREAD); sc->sc_hrdcyl = fdresult(unit,sc->sc_hrdcyl); /* next sector is at this cylinder */ sc->sc_hrdhd = fdresult(unit,sc->sc_hrdhd); sc->sc_hrdsc = fdresult(unit,sc->sc_hrdsc); sc->sc_size = fdresult(unit,sc->sc_size); DEBUGF(fddebug > 0, { printf("FD: unload Sr0 = 0x%b ",(int) sc->sc_sr0,FDSR0); /* */ printf(" Dsr1 = 0x%b ",(int) sc->sc_sr1,FDSR1); /* */ printf(" DSr2 = 0x%b ",(int) sc->sc_sr2,FDSR2); /* */ printf(" cyl = %x ",sc->sc_hrdcyl);/* */ printf(" hd = %x ", sc->sc_hrdhd); /* */ printf(" sec = %x ", sc->sc_hrdsc); /* */ printf(" n = %x\n ", sc->sc_size); /* */ }); } #endif /* end if for NFD */