home *** CD-ROM | disk | FTP | other *** search
- /*-
- * 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.
- *
- * 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.
- *
- * @(#)wd.c 7.3 (Berkeley) 5/4/91
- */
-
- /* device driver for winchester disk */
-
- #include "param.h"
- #include "dkbad.h"
- #include "disklabel.h"
- #include "i386/isa/isa.h"
- #include "i386/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, wdquiet;
- /*#define WDDEBUG*/
-
- #ifdef SMALL
- extern struct disklabel disklabel;
- #else
- struct disklabel wdsizes[NWD];
- #endif
-
- extern cyloffset ; /* bootstrap's idea of cylinder for disklabel */
-
- /*
- * 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];
- static wdcport;
-
- wdopen(io)
- register struct iob *io;
- {
- register struct disklabel *dd;
-
- #ifdef WDDEBUG
- printf("wdopen ");
- #endif
- #ifdef SMALL
- dd = &disklabel;
- #else
- dd = &wdsizes[io->i_unit];
- if (io->i_part > 8)
- _stop("Invalid partition number");
- if(io->i_ctlr > 1)
- _stop("Invalid controller number");
- #endif
- if (wdinit(io))
- _stop("wd initialization error");
- io->i_boff = dd->d_partitions[io->i_part].p_offset ;
- return(0);
- }
-
- 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 = io->i_unit;
- partition = io->i_part;
- #ifdef WDDEBUG
- printf("wdstrat %d %d ", unit, partition);
- #endif
- #ifdef SMALL
- dd = &disklabel;
- #else
- dd = &wdsizes[unit];
- #endif
- iosize = io->i_cc / dd->d_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->d_secsize;
- nblocks = dd->d_partitions[partition].p_size;
- #ifndef SMALL
- 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->d_secsize) {
- printf("wdstrategy - transfer starts in midsector\n");
- return(-1);
- }
- if (io->i_cc % dd->d_secsize) {
- printf("wd: transfer of partial sector\n");
- return(-1);
- }
- #endif
-
- address = io->i_ma;
- while (iosize > 0) {
- if (wdio(func, unit, sector, address))
- return(-1);
- iosize--;
- sector++;
- address += dd->d_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;
- register wdc = wdcport;
- struct bt_bad *bt_ptr;
- int i;
- int retries = 0;
- long cylin, head, sector;
- u_char opcode, erro;
-
- #ifdef SMALL
- dd = &disklabel;
- #else
- dd = &wdsizes[unit];
- #endif
- if (func == F_WRITE)
- opcode = WDCC_WRITE;
- else
- opcode = WDCC_READ;
-
- /* Calculate data for output. */
- cylin = blknm / dd->d_secpercyl;
- head = (blknm % dd->d_secpercyl) / dd->d_nsectors;
- sector = blknm % dd->d_nsectors;
-
- /*
- * See if the current block is in the bad block list.
- */
- if (blknm > BBSIZE/DEV_BSIZE) /* 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
- printf("--- badblock code -> Old = %d; ",
- blknm);
- #endif
- blknm = dd->d_secperunit - dd->d_nsectors
- - (bt_ptr - dkbad[unit].bt_bad) - 1;
- cylin = blknm / dd->d_secpercyl;
- head = (blknm % dd->d_secpercyl) / dd->d_nsectors;
- sector = blknm % dd->d_nsectors;
- #ifdef WDDEBUG
- printf("new = %d\n", blknm);
- #endif
- break;
- }
- }
-
- sector += 1;
- retry:
- #ifdef WDDEBUG
- printf("sec %d sdh %x cylin %d ", sector,
- WDSD_IBM | (unit<<4) | (head & 0xf), cylin);
- #endif
- 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_status) & WDCS_READY) == 0) ;
-
- outb(wdc+wd_command, opcode);
- while (opcode == WDCC_READ && (inb(wdc+wd_status) & WDCS_BUSY))
- ;
- /* Did we get an error? */
- if (opcode == WDCC_READ && (inb(wdc+wd_status) & WDCS_ERR))
- goto error;
-
- /* Ready to remove data? */
- while ((inb(wdc+wd_status) & 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_status) & WDCS_DRQ) goto error;
-
- while (opcode == WDCC_WRITE && (inb(wdc+wd_status) & WDCS_BUSY)) ;
-
- if (inb(wdc+wd_status) & WDCS_ERR) goto error;
-
- #ifdef WDDEBUG
- printf("+");
- #endif
- return (0);
- error:
- erro = inb(wdc+wd_error);
- if (++retries < RETRIES)
- goto retry;
- if (!wdquiet)
- #ifdef SMALL
- printf("wd%d: hard error: sector %d status %x error %x\n", unit,
- blknm, inb(wdc+wd_status), erro);
- #else
- 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, erro, WDERR_BITS);
- #endif
- return (-1);
- }
-
- wdinit(io)
- struct iob *io;
- {
- register wdc;
- struct disklabel *dd;
- unsigned int unit;
- struct dkbad *db;
- int i, errcnt = 0;
- char buf[512];
- static open[NWD];
-
- unit = io->i_unit;
- if (open[unit]) return(0);
-
- wdcport = io->i_ctlr ? IO_WD2 : IO_WD1;
- wdc = wdcport;
-
- #ifdef SMALL
- dd = &disklabel;
- #else
- /* reset controller */
- outb(wdc+wd_ctlr, 12); wait(10); outb(wdc+wd_ctlr, 8);
- wdwait();
-
- dd = &wdsizes[unit];
-
- tryagainrecal:
- /* set SDH, step rate, do restore to recalibrate drive */
- 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) {
- /*#ifdef SMALL
- printf("wd%d: recal status %x error %x\n",
- unit, i, inb(wdc+wd_error));
- #else*/
- printf("wd%d: recal status %b error %b\n",
- unit, i, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
- /*#endif*/
- if (++errcnt < 10)
- goto tryagainrecal;
- return(-1);
- }
-
- /*
- * Some controllers require this (after a recal they
- * revert to a logical translation mode to compensate for
- * dos limitation on 10-bit cylinders -- *shudder* -wfj)
- * note: cylinders *must* be fewer than or equal to 8 to
- * compensate for some IDE drives that latch this for all time.
- */
- outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) + 8 -1);
- outb(wdc+wd_seccnt, 35 );
- outb(wdc+wd_cyl_lo, 1224);
- outb(wdc+wd_cyl_hi, 1224/256);
- outb(wdc+wd_command, 0x91);
- while (inb(wdc+wd_status) & WDCS_BUSY) ;
-
- errcnt = 0;
- retry:
- /*
- * Read in LABELSECTOR 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, LABELSECTOR + 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) {
- int err;
-
- err = inb(wdc+wd_error);
- if (++errcnt < RETRIES)
- goto retry;
- if (!wdquiet)
- /*#ifdef SMALL
- printf("wd%d: reading label, status %x error %x\n",
- unit, i, err);
- #else*/
- printf("wd%d: reading label, status %b error %b\n",
- unit, i, WDCS_BITS, err, WDERR_BITS);
- /*#endif*/
- return(-1);
- }
-
- /* Ready to remove data? */
- while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
-
- i = insw(wdc+wd_data, buf, 256);
-
- #ifdef WDDEBUG
- printf("magic %x,insw %x, %x\n",
- ((struct disklabel *) (buf + LABELOFFSET))->d_magic, i, buf);
- #endif
- if (((struct disklabel *) (buf + LABELOFFSET))->d_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->d_secpercyl = 1999999 ; dd->d_nsectors = 17 ;
- dd->d_secsize = 512;
- outb(wdc+wd_precomp, 0xff); /* force head 3 bit off */
- return (0) ;
- }
- #endif SMALL
- #ifdef SMALL
- #ifdef WDDEBUG
- printf("magic %x sect %d\n", dd->d_magic, dd->d_nsectors);
- #endif
- #endif SMALL
-
-
- /* now that we know the disk geometry, tell the controller */
- outb(wdc+wd_cyl_lo, dd->d_ncylinders);
- outb(wdc+wd_cyl_hi, (dd->d_ncylinders)>>8);
- outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) + dd->d_ntracks-1);
- outb(wdc+wd_seccnt, dd->d_nsectors);
- outb(wdc+wd_command, 0x91);
- while (inb(wdc+wd_status) & WDCS_BUSY) ;
-
- dkbad[unit].bt_bad[0].bt_cyl = -1;
- outb(wdc+wd_precomp, dd->d_precompcyl / 4);
-
- /*
- * Read bad sector table into memory.
- */
- i = 0;
- do {
- int blknm = dd->d_secperunit - dd->d_nsectors + i;
- errcnt = wdio(F_READ, unit, blknm, buf);
- } while (errcnt && (i += 2) < 10 && i < dd->d_nsectors);
- db = (struct dkbad *)(buf);
- #define DKBAD_MAGIC 0x4321
- 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;
- }
- return(0);
- }
-
- wdwait()
- {
- register wdc = wdcport;
- register i = 0;
-
- while (inb(wdc+wd_status) & WDCS_BUSY)
- ;
- while ((inb(wdc+wd_status) & WDCS_READY) == 0)
- if (i++ > 100000)
- return(-1);
- return(0);
- }
-