386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Mon, 29 Jun 1992 04:25:24 +0000 (20:25 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Mon, 29 Jun 1992 04:25:24 +0000 (20:25 -0800)
Work on file usr/src/sys.386bsd/i386/isa/fd.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

usr/src/sys.386bsd/i386/isa/fd.c [new file with mode: 0644]

diff --git a/usr/src/sys.386bsd/i386/isa/fd.c b/usr/src/sys.386bsd/i386/isa/fd.c
new file mode 100644 (file)
index 0000000..1153907
--- /dev/null
@@ -0,0 +1,672 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Don Ahn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)fd.c        7.4 (Berkeley) 5/25/91
+ */
+
+#include "fd.h"
+#if NFD > 0
+
+#include "param.h"
+#include "dkbad.h"
+#include "systm.h"
+#include "conf.h"
+#include "file.h"
+#include "ioctl.h"
+#include "buf.h"
+#include "uio.h"
+#include "i386/isa/isa_device.h"
+#include "i386/isa/fdreg.h"
+#include "i386/isa/icu.h"
+#include "i386/isa/rtc.h"
+#undef NFD
+#define NFD 2
+
+#define        FDUNIT(s)       ((s>>3)&1)
+#define        FDTYPE(s)       ((s)&7)
+
+#define b_cylin b_resid
+#define b_step b_resid
+#define FDBLK 512
+#define NUMTYPES 4
+
+struct fd_type {
+       int     sectrac;                /* sectors per track         */
+       int     secsize;                /* size code for sectors     */
+       int     datalen;                /* data len when secsize = 0 */
+       int     gap;                    /* gap len between sectors   */
+       int     tracks;                 /* total num of tracks       */
+       int     size;                   /* size of disk in sectors   */
+       int     steptrac;               /* steps per cylinder        */
+       int     trans;                  /* transfer speed code       */
+};
+
+struct fd_type fd_types[NUMTYPES] = {
+       { 18,2,0xFF,0x1B,80,2880,1,0 }, /* 1.44 meg HD 3.5in floppy    */
+       { 15,2,0xFF,0x1B,80,2400,1,0 }, /* 1.2 meg HD floppy           */
+       { 9,2,0xFF,0x23,40,720,2,1 },   /* 360k floppy in 1.2meg drive */
+       { 9,2,0xFF,0x2A,40,720,1,1 },   /* 360k floppy in DD drive     */
+};
+
+struct fd_u {
+       int type;               /* Drive type (HD, DD     */
+       int active;             /* Drive activity boolean */
+       int motor;              /* Motor on flag          */
+       struct buf head;        /* Head of buf chain      */
+       struct buf rhead;       /* Raw head of buf chain  */
+       int reset;
+} fd_unit[NFD];
+
+
+struct buf fdtab, fdutab[NFD]; /* controller activity */
+extern int hz;
+
+/* state needed for current transfer */
+static fdc;    /* floppy disk controller io base register */
+int    fd_dmachan;
+static int fd_skip;
+static int fd_state;
+static int fd_retry;
+static int fd_drive;
+static int fd_hddrv;
+static int fd_track = -1;
+static int fd_status[7];
+
+/****************************************************************************/
+/*                      autoconfiguration stuff                             */
+/****************************************************************************/
+int fdprobe(), fdattach(), fd_turnoff();
+
+struct isa_driver fddriver = {
+       fdprobe, fdattach, "fd",
+};
+
+/*
+ * probe for existance of controller
+ */
+fdprobe(dev)
+struct isa_device *dev;
+{
+       fdc = dev->id_iobase;
+
+       /* see if it can handle a command */
+       if (out_fdc(NE7CMD_SPECIFY) < 0) {
+               fdc = 0;
+               return(0);
+       }
+       out_fdc(0xDF);
+       out_fdc(2);
+       return 1;
+}
+
+/*
+ * wire controller into system, look for floppy units
+ */
+fdattach(dev)
+struct isa_device *dev;
+{
+       int     i, hdr;
+       unsigned fdt,st0, cyl;
+
+       fd_dmachan = dev->id_drq;
+
+       fdt = rtcin(RTC_FDISKETTE);
+       hdr = 0;
+
+       /* check for each floppy drive */
+       for (i = 0; i < NFD; i++) {
+               /* is there a unit? */
+               if ((fdt & 0xf0) == RTCFDT_NONE)
+                       continue;
+
+#ifdef notyet
+               /* select it */
+               fd_turnon(i);
+               DELAY(10000);
+               out_fdc(NE7CMD_RECAL);  /* Recalibrate Function */
+               out_fdc(i);
+               DELAY(10000);
+
+               /* anything responding */
+               out_fdc(NE7CMD_SENSEI);
+               st0 = in_fdc();
+               cyl = in_fdc();
+               if (st0 & 0xd0)
+                       continue;
+
+#endif
+               /* yes, announce it */
+               if (!hdr)
+                       printf(" drives ");
+               else
+                       printf(", ");
+               printf("%d: ", i);
+
+               if ((fdt & 0xf0) == RTCFDT_12M) {
+                       printf("1.2M");
+                       fd_unit[i].type = 1;
+               }
+               if ((fdt & 0xf0) == RTCFDT_144M) {
+                       printf("1.44M");
+                       fd_unit[i].type = 0;
+               }
+
+               fdt <<= 4;
+               fd_turnoff(i);
+               hdr = 1;
+       }
+
+       /* Set transfer to 500kbps */
+       outb(fdc+fdctl,0);
+       fd_turnoff(0);
+}
+
+int
+fdsize(dev)
+dev_t  dev;
+{
+       return(0);
+}
+
+/****************************************************************************/
+/*                               fdstrategy                                 */
+/****************************************************************************/
+fdstrategy(bp)
+       register struct buf *bp;        /* IO operation to perform */
+{
+       register struct buf *dp,*dp0,*dp1;
+       long nblocks,blknum;
+       int     unit, type, s;
+
+       unit = FDUNIT(minor(bp->b_dev));
+       /*type = FDTYPE(minor(bp->b_dev));*/
+       type = fd_unit[unit].type;
+
+#ifdef FDTEST
+printf("fdstrat%d, blk = %d, bcount = %d, addr = %x|",
+       unit, bp->b_blkno, bp->b_bcount,bp->b_un.b_addr);
+#endif
+       if ((unit >= NFD) || (bp->b_blkno < 0)) {
+               printf("fdstrat: unit = %d, blkno = %d, bcount = %d\n",
+                       unit, 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_types[type].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_types[type].sectrac * 2);
+       dp = &fd_unit[unit].head;
+       dp->b_step = (fd_types[fd_unit[unit].type].steptrac);
+       s = splbio();
+       disksort(dp, bp);
+       if (dp->b_active == 0) {
+#ifdef FDDEBUG
+printf("T|");
+#endif
+               dp->b_active = 1;
+               fd_drive = unit;
+               fd_track = -1;  /* force seek on first xfer */
+               untimeout(fd_turnoff,unit);
+               fdstart(unit);          /* start drive if idle */
+       }
+       splx(s);
+       return;
+
+bad:
+       biodone(bp);
+}
+
+/****************************************************************************/
+/*                            motor control stuff                           */
+/****************************************************************************/
+set_motor(unit,reset)
+int unit,reset;
+{
+       int m0,m1;
+       m0 = fd_unit[0].motor;
+       m1 = fd_unit[1].motor;
+       outb(fdc+fdout, (unit&FDO_FDSEL)
+               | (reset ? 0 : (FDO_FRST|FDO_FDMAEN))
+               | (m0 ? FDO_MOEN0 : 0)
+               | (m1 ? FDO_MOEN1 : 0));
+}
+
+fd_turnoff(unit)
+int unit;
+{
+       fd_unit[unit].motor = 0;
+       if (unit) set_motor(0,0);
+       else set_motor(1,0);
+}
+
+fd_turnon(unit)
+int unit;
+{
+       fd_unit[unit].motor = 1;
+       set_motor(unit,0);
+}
+
+/****************************************************************************/
+/*                             fdc in/out                                   */
+/****************************************************************************/
+int
+in_fdc()
+{
+       int i, j = 100000;
+       while ((i = inb(fdc+fdsts) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0)
+               if (i == NE7_RQM) return -1;
+       if (j <= 0)
+               return(-1);
+       return inb(fdc+fddata);
+}
+
+out_fdc(x)
+int x;
+{
+       int i = 100000;
+
+       while ((inb(fdc+fdsts) & NE7_DIO) && i-- > 0);
+       while ((inb(fdc+fdsts) & NE7_RQM) == 0 && i-- > 0);
+       if (i <= 0) return (-1);
+       outb(fdc+fddata,x);
+       return (0);
+}
+
+static fdopenf;
+/****************************************************************************/
+/*                           fdopen/fdclose                                 */
+/****************************************************************************/
+Fdopen(dev, flags)
+       dev_t   dev;
+       int     flags;
+{
+       int unit = FDUNIT(minor(dev));
+       /*int type = FDTYPE(minor(dev));*/
+       int s;
+
+       fdopenf = 1;
+       /* check bounds */
+       if (unit >= NFD) return(ENXIO);
+       /*if (type >= NUMTYPES) return(ENXIO);*/
+
+       /* Set proper disk type, only allow one type */
+       return 0;
+}
+
+fdclose(dev, flags)
+       dev_t dev;
+{
+       return(0);
+}
+
+
+/****************************************************************************/
+/*                                 fdstart                                  */
+/****************************************************************************/
+fdstart(unit)
+int unit;
+{
+       register struct buf *dp,*bp;
+       int s;
+
+#ifdef FDTEST
+printf("st%d|",unit);
+#endif 
+       dp = &fd_unit[unit].head;
+       bp = dp->b_actf;
+       s = splbio();
+       if (!fd_unit[unit].motor) {
+               fd_turnon(unit);
+#ifdef notdef
+               if ((bp->b_flags & B_READ) == 0) {
+                       /* Wait for 1 sec */
+#endif
+                       timeout(fdstart,unit,hz);
+               /*}*/
+       } else
+                {
+               /* make sure drive is selected as well as on */
+
+               fd_retry = 0;
+               if (fd_unit[unit].reset) fd_state = 1;
+               else {
+                       /* DO a RESET */
+                       fd_unit[unit].reset = 1;
+                       fd_state = 5;
+               }
+               fd_skip = 0;
+#ifdef FDDEBUG
+printf("Seek %d %d\n", bp->b_cylin, dp->b_step);
+#endif
+               if (bp->b_cylin != fd_track) {
+               /* Seek necessary, never quite sure where head is at! */
+               out_fdc(NE7CMD_SEEK);   /* Seek function */
+               out_fdc(unit);  /* Drive number */
+               out_fdc(bp->b_cylin * dp->b_step);
+               fd_state = 6;
+               } else {
+               fd_state = 1;
+               fdintr(0xff);
+               }
+       }
+       splx(s);
+}
+
+fd_timeout(x)
+int x;
+{
+       int st0, st3, cyl;
+       struct buf *dp,*bp;
+
+       dp = &fd_unit[fd_drive].head;
+       bp = dp->b_actf;
+
+       out_fdc(NE7CMD_SENSED);
+       out_fdc(fd_hddrv);
+       st3 = in_fdc();
+
+       out_fdc(NE7CMD_SENSEI);
+       st0 = in_fdc();
+       cyl = in_fdc();
+printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n", fd_drive,
+st0, NE7_ST0BITS, cyl, st3, NE7_ST3BITS);
+
+       if (bp) {
+               fd_state = 4;
+               fdintr(fd_drive);
+       }
+}
+
+/****************************************************************************/
+/*                                 fdintr                                   */
+/****************************************************************************/
+fdintr(unit)
+{
+       register struct buf *dp,*bp;
+       struct buf *dpother;
+       int read,head,trac,sec,i,s,sectrac,cyl,st0;
+       unsigned long blknum;
+       struct fd_type *ft;
+
+#ifdef FDTEST
+       printf("state %d, unit %d, dr %d|",fd_state,unit,fd_drive);
+#endif
+
+       if (!fdopenf) return;
+       dp = &fd_unit[fd_drive].head;
+       bp = dp->b_actf;
+       read = bp->b_flags & B_READ;
+       /*ft = &fd_types[FDTYPE(bp->b_dev)];*/
+       ft = &fd_types[fd_unit[fd_drive].type];
+
+       switch (fd_state) {
+       case 1 : /* SEEK DONE, START DMA */
+               /* Make sure seek really happened*/
+               if (unit != 0xff) {
+                       int descyl = bp->b_cylin * dp->b_step;
+                       out_fdc(NE7CMD_SENSEI);
+                       i = in_fdc();
+                       cyl = in_fdc();
+                       if (cyl != descyl) {
+printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = %b)\n", fd_drive,
+descyl, cyl, i, NE7_ST0BITS);
+fd_state = 4;
+                               return;
+                       }
+               }
+
+               fd_track = bp->b_cylin;
+               isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd_skip,
+                       FDBLK, fd_dmachan);
+               blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
+                       + fd_skip/FDBLK;
+               sectrac = ft->sectrac;
+               sec = blknum %  (sectrac * 2);
+               head = sec / sectrac;
+               sec = sec % sectrac + 1;
+fd_hddrv = ((head&1)<<2)+fd_drive;
+
+               if (read)  out_fdc(NE7CMD_READ);        /* READ */
+               else out_fdc(NE7CMD_WRITE);             /* WRITE */
+               out_fdc(head << 2 | fd_drive);  /* head & unit */
+               out_fdc(fd_track);              /* track */
+               out_fdc(head);
+               out_fdc(sec);                   /* sector XXX +1? */
+               out_fdc(ft->secsize);           /* sector size */
+               out_fdc(sectrac);               /* sectors/track */
+               out_fdc(ft->gap);               /* gap size */
+               out_fdc(ft->datalen);           /* data length */
+               fd_state = 2;
+               break;
+       case 2 : /* IO DONE, post-analyze */
+               untimeout(fd_timeout,2);
+               for(i=0;i<7;i++) {
+                       fd_status[i] = in_fdc();
+               }
+               if (fd_status[0]&0xF8) {
+#ifdef FDOTHER
+printf("status0 err %d:",fd_status[0]);
+#endif
+                       goto retry;
+               }
+               /* All OK */
+               isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd_skip,
+                       FDBLK, fd_dmachan);
+               fd_skip += FDBLK;
+               if (fd_skip >= bp->b_bcount) {
+#ifdef FDTEST
+printf("DONE %d|", bp->b_blkno);
+#endif
+                       /* ALL DONE */
+                       fd_skip = 0;
+                       bp->b_resid = 0;
+                       dp->b_actf = bp->av_forw;
+                       biodone(bp);
+                       nextstate(dp);
+
+               } else {
+#ifdef FDDEBUG
+printf("next|");
+#endif
+                       /* set up next transfer */
+                       blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK
+                               + fd_skip/FDBLK;
+                       fd_state = 1;
+                       bp->b_cylin = (blknum / (ft->sectrac * 2));
+                       if (bp->b_cylin != fd_track) {
+#ifdef FDTEST
+printf("Seek|");
+#endif
+                               /* SEEK Necessary */
+                               out_fdc(NE7CMD_SEEK);   /* Seek function */
+                               out_fdc(fd_drive);/* Drive number */
+                               out_fdc(bp->b_cylin * dp->b_step);
+                       fd_state = 6;
+                               break;
+                       } else fdintr(0xff);
+               }
+               break;
+       case 3:
+               out_fdc(NE7CMD_SENSEI);
+               st0 = in_fdc();
+               cyl = in_fdc();
+               if (cyl != 0)
+                       printf("fd%d: recal failed ST0 %b cyl %d\n", fd_drive,
+                               st0, NE7_ST0BITS, cyl);
+
+               /* Seek necessary */
+               out_fdc(NE7CMD_SEEK);   /* Seek function */
+               out_fdc(fd_drive);/* Drive number */
+               out_fdc(bp->b_cylin * dp->b_step);
+               fd_state = 6;
+               break;
+       case 4:
+               out_fdc(NE7CMD_SPECIFY); /* specify command */
+               out_fdc(0xDF);
+               out_fdc(2);
+               out_fdc(NE7CMD_RECAL);  /* Recalibrate Function */
+               out_fdc(fd_drive);
+               fd_state = 7;
+               break;
+       case 5:
+#ifdef FDOTHER
+               printf("**RESET**\n");
+#endif
+               /* Try a reset, keep motor on */
+               set_motor(fd_drive,1);
+               DELAY(100);
+               set_motor(fd_drive,0);
+               outb(fdc+fdctl,ft->trans);
+               fd_retry++;
+               fd_state = 4;
+               break;
+       case 6:
+               /* allow heads to settle */
+               timeout(fdintr,fd_drive,hz/30);
+               fd_state = 1;
+               return;
+               break;
+               
+       case 7:
+               /* allow heads to settle */
+               timeout(fdintr,fd_drive,hz/3);
+               fd_state = 3;
+               return;
+               break;
+               
+       default:
+               printf("Unexpected FD int->");
+               out_fdc(NE7CMD_SENSEI);
+               st0 = in_fdc();
+               cyl = in_fdc();
+               printf("ST0 = %lx, PCN = %lx\n",i,sec);
+               out_fdc(0x4A); 
+               out_fdc(fd_drive);
+               for(i=0;i<7;i++) {
+                       fd_status[i] = in_fdc();
+               }
+       printf("intr status :%lx %lx %lx %lx %lx %lx %lx ",
+               fd_status[0], fd_status[1], fd_status[2], fd_status[3],
+               fd_status[4], fd_status[5], fd_status[6] );
+               break;
+       }
+       return;
+retry:
+       switch(fd_retry) {
+       case 0: case 1:
+       case 2:
+               break;
+       case 3:
+       case 4:
+       case 5:
+               fd_retry++;
+               fd_state = 4;
+               fdintr(0xff);
+               return;
+       case 6:
+               fd_retry++;
+               fd_state = 5;
+               fdintr(0xff);
+               return;
+       case 7:
+               break;
+       default:
+/*printf("fd%d: hard error (ST0 %b ST1 %b ST2 %b ST3 %b cyl %d hd %d sec %d)\n",
+               fd_drive, fd_status[0], NE7_ST0BITS, fd_status[1], NE7_ST1BITS,
+               fd_status[2], NE7_ST2BITS,  fd_status[3], NE7_ST3BITS, 
+               fd_status[4], fd_status[5], fd_status[6]);*/
+printf("fd%d: hard error (ST0 %b ", fd_drive, fd_status[0], NE7_ST0BITS);
+printf(" ST1 %b ", fd_status[1], NE7_ST1BITS);
+printf(" ST2 %b ", fd_status[2], NE7_ST2BITS);
+printf(" ST3 %b ", fd_status[3], NE7_ST3BITS);
+printf("cyl %d hd %d sec %d)\n", fd_status[4], fd_status[5], fd_status[6]);
+               badtrans(dp,bp);
+               return;
+       }
+       fd_state = 1;
+       fd_retry++;
+       fdintr(0xff);
+}
+
+badtrans(dp,bp)
+struct buf *dp,*bp;
+{
+
+       bp->b_flags |= B_ERROR;
+       bp->b_error = EIO;
+       bp->b_resid = bp->b_bcount - fd_skip;
+       dp->b_actf = bp->av_forw;
+       fd_skip = 0;
+       biodone(bp);
+       nextstate(dp);
+
+}
+
+/*
+       nextstate : After a transfer is done, continue processing
+       requests on the current drive queue.  If empty, go to
+       the other drives queue.  If that is empty too, timeout
+       to turn off the current drive in 5 seconds, and go
+       to state 0 (not expecting any interrupts).
+*/
+
+nextstate(dp)
+struct buf *dp;
+{
+       struct buf *dpother;
+       
+       if (dp->b_actf) fdstart(fd_drive);
+       else {
+               untimeout(fd_turnoff,fd_drive);
+               timeout(fd_turnoff,fd_drive,hz);
+               fd_state = 0;
+               dp->b_active = 0;
+       }
+}
+#endif