date and time created 90/04/24 19:57:26 by william
authorWilliam Nesheim <william@ucbvax.Berkeley.EDU>
Wed, 25 Apr 1990 10:57:26 +0000 (02:57 -0800)
committerWilliam Nesheim <william@ucbvax.Berkeley.EDU>
Wed, 25 Apr 1990 10:57:26 +0000 (02:57 -0800)
SCCS-vsn: sys/i386/stand/wd.c 7.1

usr/src/sys/i386/stand/wd.c [new file with mode: 0644]

diff --git a/usr/src/sys/i386/stand/wd.c b/usr/src/sys/i386/stand/wd.c
new file mode 100644 (file)
index 0000000..792d24f
--- /dev/null
@@ -0,0 +1,394 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * %sccs.include.noredist.c%
+ *
+ *     @(#)wd.c        7.1 (Berkeley) %G%
+ */
+
+/*  device driver for winchester disk  */
+
+#include "../h/param.h"
+#include "../h/inode.h"
+#include "../h/fs.h"
+#include "../h/dkbad.h"
+#include "../h/disk.h"
+#include "../isa/atio.h"
+#include "../isa/wdreg.h"
+#include "saio.h"
+
+#define        NWD             2       /* number of hard disk units supported, max 2 */
+#define        RETRIES         5       /* number of retries before giving up */
+
+int noretries = 0;
+int wdquiet = 0;
+#ifdef WDDEBUG
+int wdinfoflag = 0;
+#endif
+
+#ifdef SMALL
+extern struct disklabel disklabel;
+#else
+struct disklabel wdsizes[NWD];
+extern struct disklabel *dlp;
+#endif
+int cyloffset;
+
+/*
+ * Record for the bad block forwarding code.
+ * This is initialized to be empty until the bad-sector table
+ * is read from the disk.
+ */
+#define TRKSEC(trk,sec)        ((trk << 8) + sec)
+
+struct dkbad   dkbad[NWD];
+
+wdopen(io)
+       register struct iob *io;
+{
+        register struct disklabel *dd;
+       int unit, partition;
+
+       unit = minor_unit(minor(io->i_ino.i_dev));
+       partition = minor_partition(minor(io->i_ino.i_dev));
+#ifdef SMALL
+        dd = &disklabel;
+#else
+        dd = &wdsizes[unit];
+       if (partition >= 8)
+                _stop("Invalid partition number");
+#endif
+        if (wdinit(io))
+                _stop("wd initialization error");
+}
+
+wdstrategy(io,func)
+       register struct iob *io;
+{
+       register int iosize;    /* number of sectors to do IO for this loop */
+       register daddr_t sector;
+       int nblocks, cyloff;
+       int unit, partition;
+       char *address;
+       register struct disklabel *dd;
+
+       unit = minor_unit(minor(io->i_ino.i_dev));
+       partition = minor_partition(minor(io->i_ino.i_dev));
+       if ((unsigned)unit >= NWD) {
+               printf("wd: unit %d\n", unit);
+               return(-1);
+       }
+#ifdef SMALL
+       dd = &disklabel;
+#else
+       dd = &wdsizes[unit];
+#endif
+        iosize = io->i_cc / dd->dk_secsize;
+       /*
+        * Convert PGSIZE "blocks" to sectors.
+        * Note: doing the conversions this way limits the partition size
+        * to about 8 million sectors (1-8 Gb).
+        */
+       sector = (unsigned long) io->i_bn * DEV_BSIZE / dd->dk_secsize;
+       nblocks = dd->dk_partition[partition].nblocks;
+       cyloff = dd->dk_partition[partition].cyloff;
+        if (iosize < 0 || sector + iosize > nblocks || sector < 0) {
+#ifdef WDDEBUG
+               printf("bn = %d; sectors = %d; partition = %d; fssize = %d\n",
+                       io->i_bn, iosize, partition, nblocks);
+#endif
+                printf("wdstrategy - I/O out of filesystem boundaries\n");
+               return(-1);
+       }
+       if (io->i_bn * DEV_BSIZE % dd->dk_secsize) {
+               printf("wdstrategy - transfer starts in midsector\n");
+               return(-1);
+       }
+        if (io->i_cc % dd->dk_secsize) {
+               printf("wd: transfer of partial sector\n");
+               return(-1);
+       }
+
+       address = io->i_ma;
+       sector += cyloff * dd->dk_secpercyl;
+        while (iosize > 0) {
+                if (wdio(func, unit, sector, address))
+                        return(-1);
+               iosize--;
+               sector++;
+                address += dd->dk_secsize;
+        }
+        return(io->i_cc);
+}
+
+/* 
+ * Routine to do a one-sector I/O operation, and wait for it
+ * to complete.
+ */
+wdio(func, unit, blknm, addr)
+        short *addr;
+{
+       struct disklabel *dd;
+       int wdc = IO_WD0;
+       struct bt_bad *bt_ptr;
+        int    i;
+       int retries = 0;
+        long    cylin, head, sector;
+        u_char opcode;
+
+#ifdef SMALL
+       dd = &disklabel;
+#else
+       dd = &wdsizes[unit];
+#endif
+        if (func == WRITE)
+                opcode = WDCC_WRITE;
+        else
+                opcode = WDCC_READ;
+
+        /* Calculate data for output.           */
+        cylin = blknm / dd->dk_secpercyl;
+        head = (blknm % dd->dk_secpercyl) / dd->dk_nsectors;
+        sector = blknm % dd->dk_nsectors + 1;
+
+#ifdef notyet
+       /* 
+        * See if the current block is in the bad block list.
+        */
+       if (blknm > 7)  /* should be BBSIZE */
+           for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
+               if (bt_ptr->bt_cyl > cylin)
+                       /* Sorted list, and we passed our cylinder. quit. */
+                       break;
+               if (bt_ptr->bt_cyl == cylin &&
+                       bt_ptr->bt_trksec == (head << 8) + sector) {
+                       /*
+                        * Found bad block.  Calculate new block addr.
+                        * This starts at the end of the disk (skip the
+                        * last track which is used for the bad block list),
+                        * and works backwards to the front of the disk.
+                        */
+#ifdef WDDEBUG
+                       if (wdinfoflag)
+                           printf("--- badblock code -> Old = %d; ",
+                               blknm);
+#endif
+                       blknm = dd->dk_secperunit - dd->dk_nsectors
+                               - (bt_ptr - dkbad[unit].bt_bad) - 1;
+                       cylin = blknm / dd->dk_secpercyl;
+                       head = (blknm % dd->dk_secpercyl) / dd->dk_nsectors;
+                       sector = blknm % dd->dk_nsectors;
+#ifdef WDDEBUG
+                       if (wdinfoflag)
+                           printf("new = %d\n", blknm);
+#endif
+                       break;
+               }
+       }
+
+#endif
+retry:
+printf("sec %d sdh %x cylin %d\n", sector
+       , WDSD_IBM | (unit<<4) | (head & 0xf), cylin);
+       outb(wdc+wd_precomp, 0xff);
+       outb(wdc+wd_seccnt, 1);
+       outb(wdc+wd_sector, sector);
+       outb(wdc+wd_cyl_lo, cylin);
+       outb(wdc+wd_cyl_hi, cylin >> 8);
+
+       /* Set up the SDH register (select drive).     */
+       outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
+       while ((inb(wdc+wd_altsts) & WDCS_READY) == 0) ;
+
+       outb(wdc+wd_command, opcode);
+       while (opcode == WDCC_READ && (inb(wdc+wd_altsts) & WDCS_BUSY))
+               ;
+       /* Did we get an error?         */
+       if (opcode == WDCC_READ && (inb(wdc+wd_altsts) & WDCS_ERR))
+               goto error;
+
+       /* Ready to remove data?        */
+       while ((inb(wdc+wd_altsts) & WDCS_DRQ) == 0) ;
+
+       if (opcode == WDCC_READ)
+               insw(wdc+wd_data,addr,256);
+       else    outsw(wdc+wd_data,addr,256);
+
+       /* Check data request (should be done).         */
+       if (inb(wdc+wd_altsts) & WDCS_DRQ) goto error;
+
+       while (opcode == WDCC_WRITE && (inb(wdc+wd_altsts) & WDCS_BUSY)) ;
+
+       if (inb(wdc+wd_altsts) & WDCS_ERR) goto error;
+
+/*for (i=0; i < 256 ; i++){
+       if((i%20) == 0) printf("\n");
+       printf("%x ", addr[i]);
+}
+i=getchar();*/
+
+        return (0);
+error:
+       wddumpregs(wdc);
+       if (++retries < RETRIES)
+               goto retry;
+       if (!wdquiet)
+           printf("wd%d: hard %s error: sector %d, status %b error %b\n", unit,
+               opcode == WDCC_READ? "read" : "write", blknm, 
+               inb(wdc+wd_status), WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
+       return (-1);
+}
+
+wdinit(io)
+       struct iob *io;
+{
+       int wdc = IO_WD0;
+       struct disklabel *dd;
+        unsigned int   unit;
+       struct dkbad *db;
+       int i, errcnt = 0;
+       char buf[512];
+       static open[NWD];
+
+       /* reset controller */
+       outb(wdc+wd_ctlr, 4); wait(10); outb(wdc+wd_ctlr, 0);
+       wdwait();
+
+       unit = minor_unit(minor(io->i_ino.i_dev));
+#ifdef SMALL
+       dd = &disklabel;
+       outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
+       wdwait();
+#else
+       dd = &wdsizes[unit];
+       if (open[unit]) return(0);
+/*
+code to tell disk controller geometry of disk
+not currently used
+
+       outb(wdc+wd_sdh, 0xa7);
+wdwait();
+       outb(wdc+wd_seccnt, 17);
+       outb(wdc+wd_cyl_lo, 0);
+       outb(wdc+wd_command, 0x91);
+       wdwait();*/
+
+tryagainrecal:
+       /* set SDH, step rate, do restore */
+       outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
+       wdwait();
+       outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
+       wdwait();
+       if ((i = inb(wdc+wd_status)) & WDCS_ERR) {
+               printf("wd%d: recal status %b error %b\n",
+                       unit, i, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
+               wddumpregs(wdc);
+               if (++errcnt < 10)
+                       goto tryagainrecal;
+               return(-1);
+       }
+#endif SMALL
+#ifndef        SMALL
+       errcnt = 0;
+retry:
+cyloffset = 290;
+       /*
+        * Read in sector 0 to get the pack label and geometry.
+        */
+       outb(wdc+wd_precomp, 0xff);     /* sometimes this is head bit 3 */
+       outb(wdc+wd_seccnt, 1);
+       outb(wdc+wd_sector, 1);
+       outb(wdc+wd_cyl_lo, (cyloffset & 0xff));
+       outb(wdc+wd_cyl_hi, (cyloffset >> 8));
+       outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
+       wdwait();
+       outb(wdc+wd_command, WDCC_READ);
+       wdwait();
+       if ((i = inb(wdc+wd_status)) & WDCS_ERR) {
+               wddumpregs(wdc);
+               if (++errcnt < RETRIES)
+                       goto retry;
+               if (!wdquiet)
+                   printf("wd%d: reading label, status %b error %b\n",
+                       unit, i, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
+               return(-1);
+       }
+
+       /* Ready to remove data?        */
+       while ((inb(wdc+wd_altsts) & WDCS_DRQ) == 0) ;
+
+       i = insw(wdc+wd_data, buf, 256);
+
+       /*printf("magic %x,insw %x, %x\n",
+       ((struct disklabel *) (buf + LABELOFFSET))->dk_magic, i, buf);*/
+       if (((struct disklabel *) (buf + LABELOFFSET))->dk_magic == DISKMAGIC) {
+               *dd = * (struct disklabel *) (buf + LABELOFFSET);
+               open[unit] = 1;
+       } else {
+               if (!wdquiet)
+                       printf("wd%d: bad disk label\n", unit);
+               if (io->i_flgs & F_FILE) return(-1);
+               dkbad[unit].bt_bad[0].bt_cyl = -1;
+               dd->dk_secpercyl = 1999999 ; dd->dk_nsectors = 17 ;
+               dd->dk_secsize = 512;
+               outb(wdc+wd_precomp, 0xff);     /* force head 3 bit off */
+/**dd = *dlp;
+open[unit] = 1;*/
+               return (0) ;
+       }
+       dkbad[unit].bt_bad[0].bt_cyl = -1;
+       outb(wdc+wd_precomp, dd->dk_precompcyl / 4);
+#endif SMALL
+
+#ifdef notyet
+       /*
+        * Read bad sector table into memory.
+        */
+       i = 0;
+       do {
+               int blknm = dd->dk_secperunit - dd->dk_nsectors + i;
+               errcnt = wdio(READ, unit, blknm, buf);
+       } while (errcnt && (i += 2) < 10 && i < dd->dk_nsectors);
+       db = (struct dkbad *)(buf);
+       if (errcnt == 0 && db->bt_mbz == 0 && db->bt_flag == DKBAD_MAGIC)
+               dkbad[unit] = *db;
+       else {
+               if (!wdquiet)
+                       printf("wd%d: error in bad-sector file\n", unit);
+               dkbad[unit].bt_bad[0].bt_cyl = -1;
+       }
+#endif
+       return(0);
+}
+
+wdwait()
+{
+       register wdc = IO_WD0;
+       register i = 0;
+       
+       while (inb(wdc+wd_altsts) & WDCS_BUSY)
+               ;
+       while ((inb(wdc+wd_altsts) & WDCS_READY) == 0)
+               if (i++ > 100000)
+                       return(-1);
+       return(0);
+}
+
+
+wddumpregs(wdc){
+
+printf("err %x ", inb(wdc+wd_error));
+printf("seccnt %d ", inb(wdc+wd_seccnt));
+printf("sector %d ", inb(wdc+wd_sector));
+printf("cyl %d:", inb(wdc+wd_cyl_lo));
+printf("%d ", inb(wdc+wd_cyl_hi));
+printf("sdh %x ", inb(wdc+wd_sdh));
+printf("sts %x ", inb(wdc+wd_status));
+printf("alt %x ", inb(wdc+wd_altsts));
+printf("dig %x\n", inb(wdc+wd_digin));
+
+}