📄 wd.c
字号:
/*- * Copyright (c) 1990, 1993 * 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 8.1 (Berkeley) 6/11/93 *//* TODO:peel out buffer at low ipl, speed improvement, rewrite to clean code from garbage artifacts */#include "wd.h"#if NWD > 0#include <sys/param.h>#include <sys/dkbad.h>#include <sys/systm.h>#include <sys/conf.h>#include <sys/file.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <sys/disklabel.h>#include <sys/buf.h>#include <sys/uio.h>#include <sys/syslog.h>#include <i386/isa/isa_device.h>#include <i386/isa/icu.h>#include <i386/isa/wdreg.h>#include <vm/vm.h>#define RETRIES 5 /* number of retries before giving up */#define MAXTRANSFER 32 /* max size of transfer in page clusters */#define wdctlr(dev) ((minor(dev) & 0x80) >> 7)#define wdunit(dev) ((minor(dev) & 0x60) >> 5)#define wdpart(dev) ((minor(dev) & 0x1f))#define b_cylin b_resid /* cylinder number for doing IO to */ /* shares an entry in the buf struct *//* * Drive states. Used for open and format operations. * States < OPEN (> 0) are transient, during an open operation. * OPENRAW is used for unlabeled disks, and for floppies, to inhibit * bad-sector forwarding. */#define RAWDISK 8 /* raw disk operation, no translation*/#define ISRAWSTATE(s) (RAWDISK&(s)) /* are we in a raw state? */#define DISKSTATE(s) (~RAWDISK&(s)) /* are we in a given state regardless of raw or cooked mode? */#define CLOSED 0 /* disk is closed. */ /* "cooked" disk states */#define WANTOPEN 1 /* open requested, not started */#define RECAL 2 /* doing restore */#define RDLABEL 3 /* reading pack label */#define RDBADTBL 4 /* reading bad-sector table */#define OPEN 5 /* done with open */#define WANTOPENRAW (WANTOPEN|RAWDISK) /* raw WANTOPEN */#define RECALRAW (RECAL|RAWDISK) /* raw open, doing restore */#define OPENRAW (OPEN|RAWDISK) /* open, but unlabeled disk or floppy *//* * The structure of a disk drive. */struct disk { struct disklabel dk_dd; /* device configuration data */ long dk_bc; /* byte count left */ short dk_skip; /* blocks already transferred */ char dk_unit; /* physical unit number */ char dk_state; /* control state */ u_char dk_status; /* copy of status reg. */ u_char dk_error; /* copy of error reg. */ short dk_open; /* open/closed refcnt */ u_long dk_copenpart; /* character units open on this drive */ u_long dk_bopenpart; /* block units open on this drive */ u_long dk_openpart; /* all units open on this drive */ short dk_wlabel; /* label writable? */};/* * This label is used as a default when initializing a new or raw disk. * It really only lets us access the first track until we know more. */struct disklabel dflt_sizes = { DISKMAGIC, DTYPE_ST506, 0, "default", "", 512, /* sector size */ 17, /* # of sectors per track */ 8, /* # of tracks per cylinder */ 766, /* # of cylinders per unit */ 17*8, /* # of sectors per cylinder */ 766*8*17, /* # of sectors per unit */ 0, /* # of spare sectors per track */ 0, /* # of spare sectors per cylinder */ 0, /* # of alt. cylinders per unit */ 3600, /* rotational speed */ 1, /* hardware sector interleave */ 0, /* sector 0 skew, per track */ 0, /* sector 0 skew, per cylinder */ 0, /* head switch time, usec */ 0, /* track-to-track seek, usec */ 0, /* generic flags */ 0,0,0,0,0, 0,0,0,0,0, DISKMAGIC, 0, 8, 8192, 8192, {{21600, 0, 0,0,0,0}, /* A=root filesystem */ {21600, 40, 0,0,0,0}, {660890, 0, 0,0,0,0}, /* C=whole disk */ {216000, 80, 0,0,0,0}, {0, 0, 0,0,0,0}, {0, 0, 0,0,0,0}, {0, 0, 0,0,0,0}, {399600, 480, 0,0,0,0}}};static struct dkbad dkbad[NWD];struct disk wddrives[NWD] = {0}; /* table of units */struct buf wdtab = {0};struct buf wdutab[NWD] = {0}; /* head of queue per drive */struct buf rwdbuf[NWD] = {0}; /* buffers for raw IO */long wdxfer[NWD] = {0}; /* count of transfers */int writeprotected[NWD] = { 0 };int wdprobe(), wdattach(), wdintr();struct isa_driver wddriver = { wdprobe, wdattach, "wd",};static wdc;/* * Probe routine */wdprobe(dvp) struct isa_device *dvp;{wdc = dvp->id_iobase;#ifdef lint wdintr(0);#endif /* XXX sorry, needs to be better */ outb(wdc+wd_error, 0x5a) ; /* error register not writable */ outb(wdc+wd_cyl_lo, 0xa5) ; /* but all of cyllo are implemented */ if(inb(wdc+wd_error) != 0x5a && inb(wdc+wd_cyl_lo) == 0xa5) return(1) ; return (0);}/* * attach each drive if possible. */wdattach(dvp) struct isa_device *dvp;{ int unit = dvp->id_unit; outb(wdc+wd_ctlr,12); DELAY(1000); outb(wdc+wd_ctlr,8);}/* Read/write routine for a buffer. Finds the proper unit, range checks * arguments, and schedules the transfer. Does not wait for the transfer * to complete. Multi-page transfers are supported. All I/O requests must * be a multiple of a sector in length. */wdstrategy(bp) register struct buf *bp; /* IO operation to perform */{ register struct buf *dp; register struct disk *du; /* Disk unit to do the IO. */ register struct partition *p; long maxsz, sz; int unit = wdunit(bp->b_dev); int s; if ((unit >= NWD) || (bp->b_blkno < 0)) { printf("wdstrat: unit = %d, blkno = %d, bcount = %d\n", unit, bp->b_blkno, bp->b_bcount); pg("wd:error in wdstrategy"); bp->b_flags |= B_ERROR; goto bad; } if (writeprotected[unit] && (bp->b_flags & B_READ) == 0) { printf("wd%d: write protected\n", unit); goto bad; } du = &wddrives[unit]; if (DISKSTATE(du->dk_state) != OPEN) goto q;#ifdef old /* * Convert DEV_BSIZE "blocks" to sectors. * Note: doing the conversions this way limits the partition size * to about 8 million sectors (1-8 Gb). */ blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize; if (((u_long) bp->b_blkno * DEV_BSIZE % du->dk_dd.d_secsize != 0) || bp->b_bcount >= MAXTRANSFER * CLBYTES) { bp->b_flags |= B_ERROR; goto bad; } nblocks = du->dk_dd.d_partitions[part].p_size; cyloff = du->dk_dd.d_partitions[part].p_offset; if (blknum + (bp->b_bcount / du->dk_dd.d_secsize) > nblocks) { if (blknum == nblocks) bp->b_resid = bp->b_bcount; else bp->b_flags |= B_ERROR; goto bad; } bp->b_cylin = blknum / du->dk_dd.d_secpercyl + cyloff;#else /* * Determine the size of the transfer, and make sure it is * within the boundaries of the partition. */ p = &du->dk_dd.d_partitions[wdpart(bp->b_dev)]; maxsz = p->p_size; sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; if (bp->b_blkno + p->p_offset <= LABELSECTOR &&#if LABELSECTOR != 0 bp->b_blkno + p->p_offset + sz > LABELSECTOR &&#endif (bp->b_flags & B_READ) == 0 && du->dk_wlabel == 0) { bp->b_error = EROFS; goto bad; } if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) { /* if exactly at end of disk, return an EOF */ if (bp->b_blkno == maxsz) { bp->b_resid = bp->b_bcount; biodone(bp); return; } /* or truncate if part of it fits */ sz = maxsz - bp->b_blkno; if (sz <= 0) goto bad; bp->b_bcount = sz << DEV_BSHIFT; } bp->b_cylin = (bp->b_blkno + p->p_offset) / du->dk_dd.d_secpercyl;#endifq: dp = &wdutab[unit]; s = splhigh(); disksort(dp, bp); if (dp->b_active == 0) wdustart(du); /* start drive if idle */ if (wdtab.b_active == 0) wdstart(s); /* start IO if controller idle */ splx(s); return;bad: bp->b_error = EINVAL; biodone(bp);}/* Routine to queue a read or write command to the controller. The request is * linked into the active list for the controller. If the controller is idle, * the transfer is started. */wdustart(du) register struct disk *du;{ register struct buf *bp, *dp; dp = &wdutab[du->dk_unit]; if (dp->b_active) return; bp = dp->b_actf; if (bp == NULL) return; dp->b_forw = NULL; if (wdtab.b_actf == NULL) /* link unit into active list */ wdtab.b_actf = dp; else wdtab.b_actl->b_forw = dp; wdtab.b_actl = dp; dp->b_active = 1; /* mark the drive as busy */}/* * Controller startup routine. This does the calculation, and starts * a single-sector read or write operation. Called to start a transfer, * or from the interrupt routine to continue a multi-sector transfer. * RESTRICTIONS: * 1. The transfer length must be an exact multiple of the sector size. */static wd_sebyse;wdstart(){ register struct disk *du; /* disk unit for IO */ register struct buf *bp; struct buf *dp; register struct bt_bad *bt_ptr; long blknum, pagcnt, cylin, head, sector; long secpertrk, secpercyl, addr, i; int unit, s;loop: dp = wdtab.b_actf; if (dp == NULL) return; bp = dp->b_actf; if (bp == NULL) { wdtab.b_actf = dp->b_forw; goto loop; } unit = wdunit(bp->b_dev); du = &wddrives[unit]; if (DISKSTATE(du->dk_state) <= RDLABEL) { if (wdcontrol(bp)) { dp->b_actf = bp->av_forw; goto loop; /* done */ } return; } secpertrk = du->dk_dd.d_nsectors; secpercyl = du->dk_dd.d_secpercyl; /* * Convert DEV_BSIZE "blocks" to sectors. */ blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize + du->dk_skip;#ifdef WDDEBUG if (du->dk_skip == 0) { dprintf(DDSK,"\nwdstart %d: %s %d@%d; map ", unit, (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount, blknum); } else { dprintf(DDSK," %d)%x", du->dk_skip, inb(wdc+wd_altsts)); }#endif addr = (int) bp->b_un.b_addr; if(du->dk_skip==0) du->dk_bc = bp->b_bcount; cylin = blknum / secpercyl; head = (blknum % secpercyl) / secpertrk; sector = blknum % secpertrk; if (DISKSTATE(du->dk_state) == OPEN) cylin += du->dk_dd.d_partitions[wdpart(bp->b_dev)].p_offset / secpercyl; /* * See if the current block is in the bad block list. * (If we have one, and not formatting.) */ if (DISKSTATE(du->dk_state) == OPEN && wd_sebyse) 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -