Subject: Re: wd
To: None <current-users@sun-lamp.cs.berkeley.edu, mycroft@sun-lamp.cs.berkeley.edu,>
From: Charles Hannum <mycroft>
List: current-users
Date: 03/04/1994 15:48:41
   I got an endless loop of timeout errors on wdc0, [...]

This version should work on your machine.


# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	wd.c
#	wdreg.h
#
echo x - wd.c
sed 's/^X//' >wd.c << 'END-of-wd.c'
X/*
X * Copyright (c) 1994 Charles Hannum.
X * Copyright (c) 1990 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * William Jolitz.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by the University of
X *	California, Berkeley and its contributors.
X * 4. Neither the name of the University nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X *	from: @(#)wd.c	7.2 (Berkeley) 5/9/91
X *	$Id: wd.c,v 1.59 1994/03/04 23:43:14 mycroft Exp $
X */
X
X#define	QUIETWORKS	/* define this to make wdopen() set DKFL_QUIET */
X#define	INSTRUMENT	/* instrumentation stuff by Brad Parker */
X
X#include "wd.h"
X#if NWDC > 0
X
X#include <sys/param.h>
X#include <sys/dkbad.h>
X#include <sys/systm.h>
X#include <sys/kernel.h>
X#include <sys/conf.h>
X#include <sys/file.h>
X#include <sys/stat.h>
X#include <sys/ioctl.h>
X#include <sys/disklabel.h>
X#include <sys/buf.h>
X#include <sys/uio.h>
X#include <sys/malloc.h>
X#include <sys/syslog.h>
X#ifdef INSTRUMENT
X#include <sys/dkstat.h>
X#endif
X
X#include <vm/vm.h>
X
X#include <machine/cpu.h>
X#include <machine/cpufunc.h>
X#include <machine/pio.h>
X
X#include <i386/isa/isa.h>
X#include <i386/isa/isa_device.h>
X#include <i386/isa/icu.h>
X#include <i386/isa/wdreg.h>
X
X#define WDCNDELAY	100000	/* delay = 100us; so 10s for a controller state change */
X#define WDCDELAY	100
X
X#if 0
X/* if you enable this, it will report any delays more than 100us * N long */
X#define WDCNDELAY_DEBUG	100
X#endif
X
X#define	WDIORETRIES	5	/* number of retries before giving up */
X
X#define WDUNIT(dev)	((minor(dev) & 0xf8) >> 3)
X#define WDPART(dev)	((minor(dev) & 0x07)     )
X#define makewddev(maj, unit, part)	(makedev(maj, ((unit << 3) + part)))
X#define WDRAW	3		/* 'd' partition isn't a partition! */
X
X#define b_cylin	b_resid		/* cylinder number for doing IO to */
X				/* shares an entry in the buf struct */
X
X/*
X * Drive states.  Used to initialize drive.
X */
X#define	CLOSED		0		/* disk is closed. */
X#define	WANTOPEN	1		/* open requested, not started */
X#define	RECAL		2		/* doing restore */
X#define	OPEN		3		/* done with open */
X
X/*
X * Drive status.
X */
Xstruct	disk {
X	long	dk_bc;		/* byte count left */
X	long	dk_bct;		/* total byte count left */
X	short	dk_skip;	/* blocks already transferred */
X	short	dk_skipm;	/* blocks already transferred for multi */
X	char	dk_ctrlr;	/* physical controller number */
X	char	dk_unit;	/* physical unit number */
X	char	dk_lunit;	/* logical unit number */
X	char	dk_state;	/* control state */
X	int	dk_timeout;	/* timeout counter */
X	u_char	dk_status;	/* copy of status reg. */
X	u_char	dk_error;	/* copy of error reg. */
X	short	dk_port;	/* i/o port base */
X	
X	u_long  dk_copenpart;   /* character units open on this drive */
X	u_long  dk_bopenpart;   /* block units open on this drive */
X	u_long  dk_openpart;    /* all units open on this drive */
X	short	dk_wlabel;	/* label writable? */
X	short	dk_flags;	/* drive characteistics found */
X#define	DKFL_QUIET	0x00002	 /* report errors back, but don't complain */
X#define	DKFL_SINGLE	0x00004	 /* sector at a time mode */
X#define	DKFL_ERROR	0x00008	 /* processing a disk error */
X#define	DKFL_BSDLABEL	0x00010	 /* has a BSD disk label */
X#define	DKFL_BADSECT	0x00020	 /* has a bad144 badsector table */
X#define	DKFL_WRITEPROT	0x00040	 /* manual unit write protect */
X	struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */
X	struct disklabel dk_dd;	/* device configuration data */
X	struct cpu_disklabel dk_cpd;
X	long	dk_badsect[127];	/* 126 plus trailing -1 marker */
X};
X
Xstruct board {
X	short dkc_port;
X};
X
Xstruct	board	wdcontroller[NWDC];
Xstruct	disk	*wddrives[NWD];		/* table of units */
Xstruct	buf	wdtab[NWDC];		/* various per-controller info */
Xstruct	buf	wdutab[NWD];		/* head of queue per drive */
Xstruct	buf	rwdbuf[NWD];		/* buffers for raw IO */
Xlong	wdxfer[NWD];			/* count of transfers */
X
Xint wdprobe(), wdattach();
X
Xstruct	isa_driver wdcdriver = {
X	wdprobe, wdattach, "wdc",
X};
X
Xstatic void wdustart __P((struct disk *));
Xstatic void wdstart __P((int));
Xstatic int wdcommand __P((struct disk *, int));
Xstatic int wdcontrol __P((struct buf *));
Xstatic int wdsetctlr __P((struct disk *));
Xstatic int wdgetctlr __P((struct disk *));
Xstatic void bad144intern __P((struct disk *));
Xstatic int wdreset __P((struct disk *, int));
Xstatic int wdtimeout __P((caddr_t));
Xvoid wddisksort __P((struct buf *dp, struct buf *bp));
Xint wdc_wait __P((struct disk *, int));
X#define	wait_for_drq(d)		wdc_wait(d, WDCS_DRQ)
X#define	wait_for_ready(d)	wdc_wait(d, WDCS_READY | WDCS_SEEKCMPLT)
X#define	wait_for_unbusy(d)	wdc_wait(d, 0)
X
X/*
X * Probe for controller.
X */
Xint
Xwdprobe(isa_dev)
X	struct isa_device *isa_dev;
X{
X	struct disk *du;
X	int wdc;
X
X	if (isa_dev->id_unit >= NWDC)
X		return 0;
X
X	du = (struct disk *)malloc(sizeof(struct disk), M_TEMP, M_NOWAIT);
X	bzero(du, sizeof(struct disk));
X
X	du->dk_ctrlr = isa_dev->id_unit;
X	du->dk_unit = 0;
X	du->dk_lunit = 0;
X	wdcontroller[isa_dev->id_unit].dkc_port = isa_dev->id_iobase;
X
X	wdc = du->dk_port = isa_dev->id_iobase;
X
X	/* check if we have registers that work */
X	outb(wdc+wd_error, 0x5a);	/* error register not writable */
X	outb(wdc+wd_cyl_lo, 0xa5);	/* but all of cyllo are implemented */
X	if (inb(wdc+wd_error) == 0x5a || inb(wdc+wd_cyl_lo) != 0xa5)
X		goto nodevice;
X
X	wdreset(du, 0);
X
X	/* execute a controller only command */
X	if (wdcommand(du, WDCC_DIAGNOSE) < 0)
X		goto nodevice;
X
X	free(du, M_TEMP);
X	return 8;
X
Xnodevice:
X	free(du, M_TEMP);
X	return 0;
X}
X
X/*
X * Called for the controller too
X * Attach each drive if possible.
X */
Xint
Xwdattach(isa_dev)
X	struct isa_device *isa_dev;
X{
X	int unit, lunit;
X	struct disk *du;
X	int i, blank;
X
X	if (isa_dev->id_masunit == -1)
X		return 0;
X	if (isa_dev->id_masunit >= NWDC)
X		return 0;
X    
X	lunit = isa_dev->id_unit;
X	if (lunit == -1) {
X		printf("wdc%d: cannot support unit ?\n", isa_dev->id_masunit);
X		return 0;
X	}
X	if (lunit >= NWD)
X		return 0;
X	unit = isa_dev->id_physid;
X	
X	du = wddrives[lunit] =
X	    (struct disk *)malloc(sizeof(struct disk), M_TEMP, M_NOWAIT);
X	bzero(du, sizeof(struct disk));
X	bzero(&wdutab[lunit], sizeof(struct buf));
X	bzero(&rwdbuf[lunit], sizeof(struct buf));
X	wdxfer[lunit] = 0;
X	du->dk_ctrlr = isa_dev->id_masunit;
X	du->dk_unit = unit;
X	du->dk_lunit = lunit;
X	du->dk_port = wdcontroller[isa_dev->id_masunit].dkc_port;
X
X	if (wdgetctlr(du) != 0) {
X		/*printf("wd%d at wdc%d slave %d -- error\n", lunit,
X		    isa_dev->id_masunit, unit);*/
X		wddrives[lunit] = 0;
X		free(du, M_TEMP);
X		return 0;
X	}
X
X	printf("wd%d at wdc%d targ %d: ", isa_dev->id_unit,
X	    isa_dev->id_masunit, isa_dev->id_physid);
X	if (du->dk_params.wdp_heads == 0)
X		printf("(unknown size) <");
X	else
X		printf("%dMB %d cyl, %d head, %d sec <",
X		    du->dk_dd.d_ncylinders * du->dk_dd.d_secpercyl /
X		    (1048576 / DEV_BSIZE),
X		    du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks,
X		    du->dk_dd.d_nsectors);
X	for (i = blank = 0; i < sizeof(du->dk_params.wdp_model); i++) {
X		char c = du->dk_params.wdp_model[i];
X		if (c == '\0')
X			break;
X		if (c != ' ') {
X			if (blank)
X				printf(" %c", c);
X			else
X				printf("%c", c);
X			blank = 0;
X		} else
X			blank = 1;
X	}
X	printf(">\n");
X
X	wdtimeout((caddr_t)du);
X
X	return 1;
X}
X
X/* Read/write routine for a buffer.  Finds the proper unit, range checks
X * arguments, and schedules the transfer.  Does not wait for the transfer
X * to complete.  Multi-page transfers are supported.  All I/O requests must
X * be a multiple of a sector in length.
X */
Xint
Xwdstrategy(bp)
X	struct buf *bp;
X{
X	struct buf *dp;
X	struct disk *du;	/* Disk unit to do the IO.	*/
X	int lunit = WDUNIT(bp->b_dev);
X	int s;
X    
X	/* valid unit, controller, and request?  */
X	if (lunit >= NWD || bp->b_blkno < 0 ||
X	    howmany(bp->b_bcount, DEV_BSIZE) >= (1 << NBBY) ||
X	    (du = wddrives[lunit]) == 0) {
X		bp->b_error = EINVAL;
X		bp->b_flags |= B_ERROR;
X		goto done;
X	}
X    
X	/* "soft" write protect check */
X	if ((du->dk_flags & DKFL_WRITEPROT) && (bp->b_flags & B_READ) == 0) {
X		bp->b_error = EROFS;
X		bp->b_flags |= B_ERROR;
X		goto done;
X	}
X    
X	/* have partitions and want to use them? */
X	if ((du->dk_flags & DKFL_BSDLABEL) != 0 && WDPART(bp->b_dev) != WDRAW) {
X		/*
X		 * do bounds checking, adjust transfer. if error, process.
X		 * if end of partition, just return
X		 */
X		if (bounds_check_with_label(bp, &du->dk_dd, du->dk_wlabel) <= 0)
X			goto done;
X		/* otherwise, process transfer request */
X	}
X    
X	/* don't bother */
X	bp->b_cylin = 0;
X
X	/* queue transfer on drive, activate drive and controller if idle */
X	dp = &wdutab[lunit];
X	s = splbio();
X	wddisksort(dp, bp);
X	if (dp->b_active == 0)
X		wdustart(du);		/* start drive */
X	if (wdtab[du->dk_ctrlr].b_active == 0)
X		wdstart(du->dk_ctrlr);		/* start controller */
X	splx(s);
X	return 0;
X    
Xdone:
X	/* toss transfer, we're done early */
X	biodone(bp);
X}
X
X/*
X * Need to skip over multitransfer bufs.
X */
Xvoid
Xwddisksort(dp, bp)
X	struct buf *dp, *bp;
X{
X	struct buf *ap;
X
X	while ((ap = dp->b_actf) && ap->b_flags & B_XXX)
X		dp = ap;
X	disksort(dp, bp);
X}
X
X/*
X * Routine to queue a command to the controller.  The unit's
X * request is linked into the active list for the controller.
X * If the controller is idle, the transfer is started.
X */
Xstatic void
Xwdustart(du)
X	struct disk *du;
X{
X	struct buf *dp = &wdutab[du->dk_lunit];
X	int ctrlr = du->dk_ctrlr;
X    
X	/* unit already active? */
X	if (dp->b_active)
X		return;
X    
X	/* anything to start? */
X	if (dp->b_actf == NULL)
X		return;	
X    
X	/* link onto controller queue */
X	dp->b_forw = NULL;
X	if (wdtab[ctrlr].b_forw == NULL)
X		wdtab[ctrlr].b_forw = dp;
X	else
X		wdtab[ctrlr].b_actl->b_forw = dp;
X	wdtab[ctrlr].b_actl = dp;
X    
X	/* mark the drive unit as busy */
X	dp->b_active = 1;
X}
X
X/*
X * Controller startup routine.  This does the calculation, and starts
X * a single-sector read or write operation.  Called to start a transfer,
X * or from the interrupt routine to continue a multi-sector transfer.
X * RESTRICTIONS:
X * 1.	The transfer length must be an exact multiple of the sector size.
X */
Xstatic void
Xwdstart(ctrlr)
X	int ctrlr;
X{
X	register struct disk *du;	/* disk unit for IO */
X	register struct buf *bp;
X	struct disklabel *lp;
X	struct buf *dp;
X	long blknum, cylin, head, sector;
X	long secpertrk, secpercyl, addr;
X	int lunit, wdc;
X	int xfrblknum;
X    
Xloop:
X	/* is there a drive for the controller to do a transfer with? */
X	dp = wdtab[ctrlr].b_forw;
X	if (dp == NULL) {
X		wdtab[ctrlr].b_active = 0;
X		return;
X	}
X    
X	/* is there a transfer to this drive ? if so, link it on
X	   the controller's queue */
X	bp = dp->b_actf;
X	if (bp == NULL) {
X		dp->b_active = 0;
X		wdtab[ctrlr].b_forw = dp->b_forw;
X		goto loop;
X	}
X    
X	/* obtain controller and drive information */
X	lunit = WDUNIT(bp->b_dev);
X	du = wddrives[lunit];
X
X	/* clear any pending timeout, just in case */
X	du->dk_timeout = 0;
X    
X	/* if not really a transfer, do control operations specially */
X	if (du->dk_state < OPEN) {
X		(void) wdcontrol(bp);
X		return;
X	}
X    
X	/* calculate transfer details */
X	blknum = bp->b_blkno + du->dk_skip;
X#ifdef WDDEBUG
X	if (du->dk_skip == 0)
X		printf("\nwdstart %d: %s %d@%d; map ", lunit,
X		    (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount,
X		    blknum);
X	else
X		printf(" %d)%x", du->dk_skip, inb(du->dk_port+wd_altsts));
X#endif
X	addr = (int)bp->b_un.b_addr;
X	if (du->dk_skip == 0)
X		du->dk_bc = bp->b_bcount;
X	if (du->dk_skipm == 0) {
X		struct buf *oldbp, *nextbp;
X		oldbp = bp;
X		nextbp = bp->b_actf;
X		du->dk_bct = du->dk_bc;
X		oldbp->b_flags |= B_XXX;
X		while (nextbp && (oldbp->b_flags & DKFL_SINGLE) == 0 &&
X		    oldbp->b_dev == nextbp->b_dev && nextbp->b_blkno ==
X		    (oldbp->b_blkno + (oldbp->b_bcount / DEV_BSIZE)) &&
X		    (oldbp->b_flags & B_READ) == (nextbp->b_flags & B_READ)) {
X			if ((du->dk_bct + nextbp->b_bcount) / DEV_BSIZE >= 240)
X				break;
X			du->dk_bct += nextbp->b_bcount; 
X			nextbp->b_flags |= B_XXX;
X			oldbp = nextbp;
X			nextbp = nextbp->b_actf;
X		}
X	}
X    
X	lp = &du->dk_dd;
X	secpertrk = lp->d_nsectors;
X	secpercyl = lp->d_secpercyl;
X	if ((du->dk_flags & DKFL_BSDLABEL) != 0 && WDPART(bp->b_dev) != WDRAW)
X		blknum += lp->d_partitions[WDPART(bp->b_dev)].p_offset;
X	cylin = blknum / secpercyl;
X	head = (blknum % secpercyl) / secpertrk;
X	sector = blknum % secpertrk;
X    
X	/* Check for bad sectors if we have them, and not formatting */
X	/* Only do this in single-sector mode, or when starting a */
X	/* multiple-sector transfer. */
X	if ((du->dk_flags & DKFL_BADSECT) &&
X#ifdef B_FORMAT
X	    (bp->b_flags & B_FORMAT) == 0 &&
X#endif
X	    (du->dk_skipm == 0 || (du->dk_flags & DKFL_SINGLE))) {
X
X		long blkchk, blkend, blknew;
X		int i;
X
X		blkend = blknum + howmany(du->dk_bct, DEV_BSIZE) - 1;
X		for (i = 0; (blkchk = du->dk_badsect[i]) != -1; i++) {
X			if (blkchk > blkend)
X				break;	/* transfer is completely OK; done */
X			if (blkchk == blknum) {
X				blknew =
X				    lp->d_secperunit - lp->d_nsectors - i - 1;
X				cylin = blknew / secpercyl;
X				head = (blknew % secpercyl) / secpertrk;
X				sector = blknew % secpertrk;
X				du->dk_flags |= DKFL_SINGLE;
X				/* found and replaced first blk of transfer; done */
X				break;
X			} else if (blkchk > blknum) {
X				du->dk_flags |= DKFL_SINGLE;
X				break;	/* bad block inside transfer; done */
X			}
X		}
X	}
X	if (du->dk_flags & DKFL_SINGLE) {
X		du->dk_bct = du->dk_bc;
X		du->dk_skipm = du->dk_skip;
X	}
X
X#ifdef WDDEBUG
X	printf("c%d h%d s%d ", cylin, head, sector);
X#endif
X
X	sector++;	/* sectors begin with 1, not 0 */
X    
X	wdtab[ctrlr].b_active = 1;		/* mark controller active */
X	wdc = du->dk_port;
X    
X#ifdef INSTRUMENT
X	/* instrumentation */
X	if (du->dk_unit >= 0 && du->dk_skip == 0) {
X		dk_busy |= 1 << du->dk_lunit;
X		dk_wds[du->dk_lunit] += bp->b_bcount >> 6;
X	}
X	if (du->dk_unit >= 0 && du->dk_skipm == 0) {
X		++dk_seek[du->dk_lunit];
X		++dk_xfer[du->dk_lunit];
X	}
X#endif
X
Xretry:
X	/* if starting a multisector transfer, or doing single transfers */
X	if (du->dk_skipm == 0 || (du->dk_flags & DKFL_SINGLE)) {
X		int command;
X
X		if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0) {
X			du->dk_bc += DEV_BSIZE;
X			du->dk_bct += DEV_BSIZE;
X		}
X
X		/* controller idle? */
X		if (wait_for_unbusy(du) < 0) {
X			wdreset(du, 1);
X			goto retry;
X		}
X	
X		/* stuff the task file */
X		outb(wdc+wd_precomp, lp->d_precompcyl / 4);
X#ifdef B_FORMAT
X		if (bp->b_flags & B_FORMAT) {
X			outb(wdc+wd_sector, lp->d_gap3);
X			outb(wdc+wd_seccnt, lp->d_nsectors);
X		} else {
X			if (du->dk_flags & DKFL_SINGLE)
X				outb(wdc+wd_seccnt, 1);
X			else
X				outb(wdc+wd_seccnt,
X				    howmany(du->dk_bct, DEV_BSIZE));
X			outb(wdc+wd_sector, sector);
X		}
X#else
X		if (du->dk_flags & DKFL_SINGLE)
X			outb(wdc+wd_seccnt, 1);
X		else
X			outb(wdc+wd_seccnt, howmany(du->dk_bct, DEV_BSIZE));
X		outb(wdc+wd_sector, sector);
X#endif
X		outb(wdc+wd_cyl_lo, cylin);
X		outb(wdc+wd_cyl_hi, cylin >> 8);
X	
X		/* set up the SDH register (select drive) */
X		outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit<<4) | (head & 0xf));
X		/* wait for drive to become ready */
X		if (wait_for_ready(du) < 0) {
X			wdreset(du, 1);
X			goto retry;
X		}
X	
X		/* initiate command! */
X#ifdef B_FORMAT
X		if (bp->b_flags & B_FORMAT)
X			command = WDCC_FORMAT;
X		else
X			command =
X			    (bp->b_flags & B_READ) ? WDCC_READ : WDCC_WRITE;
X#else
X		command = (bp->b_flags & B_READ) ? WDCC_READ : WDCC_WRITE;
X#endif
X		if (wdcommand(du, command) < 0) {
X			wdreset(du, 1);
X			goto retry;
X		}
X#ifdef WDDEBUG
X		printf("sector %d cylin %d head %d addr %x sts %x\n", sector,
X		    cylin, head, addr, inb(wdc+wd_altsts));
X#endif
X	}
X    
X	/* if this is a read operation, just go away until it's done.	*/
X	if (bp->b_flags & B_READ) {
X		du->dk_timeout = 2;
X		return;
X	}
X    
X	if (wait_for_drq(du) < 0) {
X		wdreset(du, 1);
X		goto retry;
X	}
X	/* then send it! */
X	outsw(wdc+wd_data, addr + du->dk_skip * DEV_BSIZE,
X	    DEV_BSIZE / sizeof(short));
X
X	du->dk_bc -= DEV_BSIZE;
X	du->dk_bct -= DEV_BSIZE;
X
X	du->dk_timeout = 2;
X}
X
X/* Interrupt routine for the controller.  Acknowledge the interrupt, check for
X * errors on the current operation, mark it done if necessary, and start
X * the next request.  Also check for a partially done transfer, and
X * continue with the next chunk if so.
X */
Xvoid
Xwdintr(ctrlr)
X	int ctrlr;
X{
X	register struct	disk *du;
X	register struct buf *bp, *dp;
X	int stat, wdc;
X
X	/* clear the pending interrupt */
X	(void) inb(wdcontroller[ctrlr].dkc_port+wd_status);
X
X	if (!wdtab[ctrlr].b_active) {
X		printf("wdc%d: extra interrupt\n", ctrlr);
X		return;
X	}
X    
X	dp = wdtab[ctrlr].b_forw;
X	bp = dp->b_actf;
X	du = wddrives[WDUNIT(bp->b_dev)];
X	wdc = du->dk_port;
X	du->dk_timeout = 0;
X    
X#ifdef WDDEBUG
X	printf("I%d ", ctrlr);
X#endif
X
X	stat = wait_for_unbusy(du);
X	if (stat < 0) {
X		printf("wdc%d: timeout waiting for unbusy\n", ctrlr);
X		goto lose;
X	}
X    
X	/* is it not a transfer, but a control operation? */
X	if (du->dk_state < OPEN) {
X		wdtab[ctrlr].b_active = 0;
X		if (wdcontrol(bp))
X			wdstart(ctrlr);
X		return;
X	}
X    
X	/* have we an error? */
X	if (stat & (WDCS_ERR | WDCS_ECCCOR)) {
X	lose:
X		du->dk_status = stat;
X		du->dk_error = inb(wdc+wd_error);
X#ifdef WDDEBUG
X		printf("stat %x error %x\n", stat, du->dk_error);
X#endif
X		if ((du->dk_flags & DKFL_SINGLE) == 0) {
X			du->dk_flags |= DKFL_ERROR;
X			goto outt;
X		}
X#ifdef B_FORMAT
X		if (bp->b_flags & B_FORMAT) {
X			bp->b_error = EIO;
X			bp->b_flags |= B_ERROR;
X			goto done;
X		}
X#endif
X	
X		/* error or error correction? */
X		if (stat & WDCS_ERR) {
X			if (++wdtab[ctrlr].b_errcnt < WDIORETRIES)
X				wdtab[ctrlr].b_active = 0;
X			else {
X				if ((du->dk_flags & DKFL_QUIET) == 0) {
X					diskerr(bp, "wd", "hard error",
X					    LOG_PRINTF, du->dk_skip,
X					    &du->dk_dd);
X#ifdef WDDEBUG
X					printf("stat %b error %b\n", stat,
X					    WDCS_BITS, inb(wdc+wd_error),
X					    WDERR_BITS);
X#endif
X				}
X				bp->b_error = EIO;
X				bp->b_flags |= B_ERROR;	/* flag the error */
X			}
X		} else if ((du->dk_flags & DKFL_QUIET) == 0)
X			diskerr(bp, "wd", "soft ecc", 0, du->dk_skip,
X			    &du->dk_dd);
X	}
X    
X	/*
X	 * If this was a successful read operation, fetch the data.
X	 */
X	if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) &&
X	    wdtab[ctrlr].b_active) {
X		int chk;
X	
X		chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short));
X	
X		/* ready to receive data? */
X		if (wait_for_drq(du) < 0) {
X			printf("wdc%d: timeout waiting for drq\n", ctrlr);
X			goto lose;
X		}
X
X		/* suck in data */
X		insw(wdc+wd_data,
X		    (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE, chk);
X		du->dk_bc -= chk * sizeof(short);
X		du->dk_bct -= chk * sizeof(short);
X	
X		/* for obselete fractional sector reads */
X		while (chk++ < (DEV_BSIZE / sizeof(short)))
X			(void) inw(wdc+wd_data);
X	}
X    
X	wdxfer[du->dk_lunit]++;
Xoutt:
X	if (wdtab[ctrlr].b_active) {
X#ifdef INSTRUMENT
X		if (du->dk_unit >= 0)
X			dk_busy &= ~(1 << du->dk_unit);
X#endif
X		if ((bp->b_flags & B_ERROR) == 0) {
X			du->dk_skip++;	/* Add to succ. sect */
X			du->dk_skipm++;	/* Add to succ. sect for multitransfer */
X			if (wdtab[ctrlr].b_errcnt &&
X			    (du->dk_flags & DKFL_QUIET) == 0)
X				diskerr(bp, "wd", "soft error", 0, du->dk_skip,
X				    &du->dk_dd);
X			wdtab[ctrlr].b_errcnt = 0;
X	    
X			/* see if more to transfer */
X			if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
X				wdtab[ctrlr].b_active = 0;
X				wdstart(ctrlr);
X				return;	/* next chunk is started */
X			} else if ((du->dk_flags & (DKFL_SINGLE | DKFL_ERROR)) == DKFL_ERROR) {
X				du->dk_skip = 0;
X				du->dk_skipm = 0;
X				du->dk_flags &= ~DKFL_ERROR;
X				du->dk_flags |=  DKFL_SINGLE;
X				wdtab[ctrlr].b_active = 0;
X				wdstart(ctrlr);
X				return;	/* redo xfer sector by sector */
X			}
X		}
X
Xdone:
X		/* done with this transfer, with or without error */
X		du->dk_flags &= ~DKFL_SINGLE;
X		wdtab[ctrlr].b_errcnt = 0;
X		if (du->dk_bct == 0) {
X			wdtab[ctrlr].b_forw = dp->b_forw;
X			du->dk_skipm = 0;
X			dp->b_active = 0;
X		}
X		bp->b_resid = bp->b_bcount - du->dk_skip * DEV_BSIZE;
X		bp->b_flags &= ~B_XXX;
X		du->dk_skip = 0;
X		dp->b_actf = bp->b_actf;
X		dp->b_errcnt = 0;
X		biodone(bp);
X	}
X
X	/* controller now idle */
X	wdtab[ctrlr].b_active = 0;
X
X	/* anything more on drive queue? */
X	if (dp->b_actf && du->dk_bct == 0)
X		wdustart(du);
X
X	/* anything more for controller to do? */
X	if (wdtab[ctrlr].b_forw)
X		wdstart(ctrlr);
X}
X
X/*
X * Initialize a drive.
X */
Xint
Xwdopen(dev, flag, fmt, p)
X	dev_t dev;
X	int flag;
X	int fmt;
X	struct proc *p;
X{
X	int lunit;
X	struct disk *du;
X	int part = WDPART(dev), mask = 1 << part;
X	struct partition *pp;
X	char *msg;
X    
X	lunit = WDUNIT(dev);
X	if (lunit >= NWD)
X		return ENXIO;
X	du = wddrives[lunit];
X	if (du == 0)
X		return ENXIO;
X    
X#ifdef QUIETWORKS
X	if (part == WDRAW)
X		du->dk_flags |= DKFL_QUIET;
X	else
X		du->dk_flags &= ~DKFL_QUIET;
X#else
X	du->dk_flags &= ~DKFL_QUIET;
X#endif
X	
X	if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
X		du->dk_flags |= DKFL_WRITEPROT;
X		wdutab[lunit].b_actf = NULL;
X
X		/*
X		 * Use the default sizes until we've read the label,
X		 * or longer if there isn't one there.
X		 */
X		bzero(&du->dk_dd, sizeof(du->dk_dd));
X		du->dk_dd.d_type = DTYPE_ST506;
X		du->dk_dd.d_ncylinders = 1024;
X		du->dk_dd.d_secsize = DEV_BSIZE;
X		du->dk_dd.d_ntracks = 8;
X		du->dk_dd.d_nsectors = 17;
X		du->dk_dd.d_secpercyl = 17*8;
X		du->dk_dd.d_secperunit = 17*8*1024;
X		du->dk_state = WANTOPEN;
X
X		/* read label using "raw" partition */
X#ifdef notdef
X		/* wdsetctlr(du); */	/* Maybe do this TIH */
X		msg = readdisklabel(makewddev(major(dev), WDUNIT(dev), WDRAW),
X		    wdstrategy, &du->dk_dd, &du->dk_cpd);
X		wdsetctlr(du);
X#endif
X		if (msg = readdisklabel(makewddev(major(dev), WDUNIT(dev),
X		    WDRAW), wdstrategy, &du->dk_dd, &du->dk_cpd)) {
X			if ((du->dk_flags & DKFL_QUIET) == 0) {
X				log(LOG_WARNING,
X				    "wd%d: cannot find label (%s)\n",
X				    lunit, msg);
X				return EINVAL;	/* XXX needs translation */
X			}
X		} else {
X			wdsetctlr(du);
X			du->dk_flags |= DKFL_BSDLABEL;
X			du->dk_flags &= ~DKFL_WRITEPROT;
X			if (du->dk_dd.d_flags & D_BADSECT)
X				du->dk_flags |= DKFL_BADSECT;
X		}
X	}
X
X	if (du->dk_flags & DKFL_BADSECT)
X		bad144intern(du);
X
X	/*
X	 * Warn if a partion is opened
X	 * that overlaps another partition which is open
X	 * unless one is the "raw" partition (whole disk).
X	 */
X	if ((du->dk_openpart & mask) == 0 && part != WDRAW) {
X		int start, end;
X	
X		pp = &du->dk_dd.d_partitions[part];
X		start = pp->p_offset;
X		end = pp->p_offset + pp->p_size;
X		for (pp = du->dk_dd.d_partitions;
X		    pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
X		    pp++) {
X			if (pp->p_offset + pp->p_size <= start ||
X			    pp->p_offset >= end)
X				continue;
X			if (pp - du->dk_dd.d_partitions == WDRAW)
X				continue;
X			if (du->dk_openpart & (1 << (pp - du->dk_dd.d_partitions)))
X				log(LOG_WARNING,
X				    "wd%d%c: overlaps open partition (%c)\n",
X				    lunit, part + 'a',
X				    pp - du->dk_dd.d_partitions + 'a');
X		}
X	}
X	if (part >= du->dk_dd.d_npartitions && part != WDRAW)
X		return ENXIO;
X    
X	/* insure only one open at a time */
X	du->dk_openpart |= mask;
X	switch (fmt) {
X	case S_IFCHR:
X		du->dk_copenpart |= mask;
X		break;
X	case S_IFBLK:
X		du->dk_bopenpart |= mask;
X		break;
X	}
X	return 0;
X}
X
X/*
X * Implement operations other than read/write.
X * Called from wdstart or wdintr during opens and formats.
X * Uses finite-state-machine to track progress of operation in progress.
X * Returns 0 if operation still in progress, 1 if completed.
X */
Xstatic int
Xwdcontrol(bp)
X	struct buf *bp;
X{
X	struct disk *du;
X	int unit, lunit;
X	int stat;
X	int s, ctrlr;
X	int wdc;
X    
X	du = wddrives[WDUNIT(bp->b_dev)];
X	ctrlr = du->dk_ctrlr;
X	unit = du->dk_unit;
X	lunit = du->dk_lunit;
X	wdc = du->dk_port;
X    
X	switch (du->dk_state) {
X	case WANTOPEN:			/* set SDH, step rate, do restore */
X	tryagainrecal:
X#ifdef WDDEBUG
X		printf("wd%d: recal ", lunit);
X#endif
X		s = splbio();		/* not called from intr level ... */
X		wdgetctlr(du);
X
X		if (wait_for_unbusy(du) < 0) {
X		lose:
X			wdreset(du, 1);
X			splx(s);
X			goto tryagainrecal;
X		}
X		outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
X		if (wait_for_ready(du) < 0)
X			goto lose;
X		wdtab[ctrlr].b_active = 1;
X		if (wdcommand(du, WDCC_RESTORE | WD_STEP) < 0)
X			goto lose;
X		du->dk_state = RECAL;
X		splx(s);
X		return 0;
X
X	case RECAL:
X		if ((stat = inb(wdc+wd_altsts)) & WDCS_ERR) {
X			if ((du->dk_flags & DKFL_QUIET) == 0) {
X				printf("wd%d: recal", du->dk_lunit);
X				printf(": status %b error %b\n", stat,
X				    WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
X			}
X			if (++wdtab[ctrlr].b_errcnt < WDIORETRIES)
X				goto tryagainrecal;
X			bp->b_error = ENXIO;	/* XXX needs translation */
X			goto badopen;
X		}
X
X		/* some controllers require this ... */
X		wdsetctlr(du);
X	
X		wdtab[ctrlr].b_errcnt = 0;
X		du->dk_state = OPEN;
X		/*
X		 * The rest of the initialization can be done
X		 * by normal means.
X		 */
X		return 1;
X
X	default:
X		panic("wdcontrol");
X	}
X	/* NOTREACHED */
X
Xbadopen:
X	if ((du->dk_flags & DKFL_QUIET) == 0) 
X		printf(": status %b error %b\n", stat, WDCS_BITS,
X		    inb(wdc+wd_error), WDERR_BITS);
X	bp->b_flags |= B_ERROR;
X	return 1;
X}
X
X/*
X * send a command and wait uninterruptibly until controller is finished.
X * return -1 if controller busy for too long, otherwise
X * return status. intended for brief controller commands at critical points.
X * assumes interrupts are blocked.
X */
Xstatic int
Xwdcommand(du, cmd)
X	struct disk *du;
X	int cmd;
X{
X	int stat, wdc;
X    
X	wdc = du->dk_port;
X
X	/* controller ready for command? */
X	if (wait_for_unbusy(du) < 0)
X		return -1;
X    
X	/* send command, await results */
X	outb(wdc+wd_command, cmd);
X
X	switch (cmd) {
X	default:
X#if 0
X	case WDCC_FORMAT:
X	case WDCC_RESTORE | WD_STEP:
X#endif
X		return wait_for_unbusy(du);
X	case WDCC_READ:
X	case WDCC_WRITE:
X		return 0;
X	case WDCC_IDC:
X	case WDCC_DIAGNOSE:
X		return wdc_wait(du, WDCS_READY);
X	case WDCC_READP:
X		return wait_for_drq(du);
X	}
X}
X
X/*
X * issue IDC to drive to tell it just what geometry it is to be.
X */
Xstatic int
Xwdsetctlr(du)
X	struct disk *du;
X{
X	int stat, s, wdc;
X    
X#ifdef WDDEBUG
X	printf("wd(%d,%d) C%dH%dS%d\n", du->dk_ctrlr, du->dk_unit,
X	    du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors);
X#endif
X    
X	wdc = du->dk_port;
X
X	s = splbio();
X	if (wait_for_unbusy(du) < 0) {
X	lose:
X		splx(s);
X		return -1;
X	}
X	outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders);	/* TIH: was ...ders+1 */
X	outb(wdc+wd_cyl_hi, du->dk_dd.d_ncylinders>>8);	/* TIH: was ...ders+1 */
X	outb(wdc+wd_sdh,
X	    WDSD_IBM | (du->dk_unit << 4) + du->dk_dd.d_ntracks - 1);
X	outb(wdc+wd_seccnt, du->dk_dd.d_nsectors);
X	if (wait_for_ready(du) < 0)
X		goto lose;
X	stat = wdcommand(du, WDCC_IDC);
X	if (stat < 0 || stat & WDCS_ERR)
X		printf("wdsetctlr: stat %b error %b\n", stat, WDCS_BITS,
X		    inb(wdc+wd_error), WDERR_BITS);
X	splx(s);
X	return stat;
X}
X
X/*
X * issue READP to drive to ask it what it is.
X */
Xstatic int
Xwdgetctlr(du)
X	struct disk *du;
X{
X	int stat, s, i, wdc;
X	char tb[DEV_BSIZE];
X	struct wdparams *wp;
X    
X	s = splbio();		/* not called from intr level ... */
X	wdc = du->dk_port;
X	if (wait_for_unbusy(du) < 0) {
X	lose:
X		splx(s);
X		return -1;
X	}
X	outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4));
X	if (wait_for_ready(du) < 0)
X		goto lose;
X	stat = wdcommand(du, WDCC_READP);
X	if (stat < 0)
X		goto lose;
X
X	if ((stat & WDCS_ERR) == 0) {
X		/* obtain parameters */
X		wp = &du->dk_params;
X		insw(wdc+wd_data, tb, sizeof(tb)/sizeof(short));
X		bcopy(tb, wp, sizeof(struct wdparams));
X
X		/* shuffle string byte order */
X		for (i = 0; i < sizeof(wp->wdp_model); i += 2) {
X			u_short *p;
X			p = (u_short *)(wp->wdp_model + i);
X			*p = ntohs(*p);
X		}
X
X		strncpy(du->dk_dd.d_typename, "ESDI/IDE",
X		    sizeof du->dk_dd.d_typename);
X		du->dk_dd.d_type = DTYPE_ESDI;
X		bcopy(wp->wdp_model+20, du->dk_dd.d_packname, 14-1);
X
X		/* update disklabel given drive information */
X		du->dk_dd.d_ncylinders =
X		    wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/;
X		du->dk_dd.d_ntracks = wp->wdp_heads;
X		du->dk_dd.d_nsectors = wp->wdp_sectors;
X		du->dk_dd.d_secpercyl =
X		    du->dk_dd.d_ntracks * du->dk_dd.d_nsectors;
X		du->dk_dd.d_partitions[1].p_size =
X		    du->dk_dd.d_secpercyl * wp->wdp_sectors;
X		du->dk_dd.d_partitions[1].p_offset = 0;
X	} else {
X		/*
X		 * If WDCC_READP fails then we might have an old drive
X		 * so we try a seek to 0; if that passes then the
X		 * drive is there but it's OLD AND KRUSTY.
X		 */
X		stat = wdcommand(du, WDCC_RESTORE | WD_STEP);
X		if (stat < 0 || stat & WDCS_ERR)
X			goto lose;
X
X		strncpy(du->dk_dd.d_typename, "ST506",
X		    sizeof du->dk_dd.d_typename);
X		strncpy(du->dk_params.wdp_model, "Unknown Type",
X		    sizeof du->dk_params.wdp_model);
X		du->dk_dd.d_type = DTYPE_ST506;
X	}
X
X#if 0
X	printf("gc %x cyl %d trk %d sec %d type %d sz %d model %s\n",
X	    wp->wdp_config, wp->wdp_fixedcyl + wp->wdp_removcyl, wp->wdp_heads,
X	    wp->wdp_sectors, wp->wdp_cntype, wp->wdp_cnsbsz, wp->wdp_model);
X#endif
X    
X	/* better ... */
X	du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
X    
X	/* XXX sometimes possibly needed */
X	(void) inb(wdc+wd_status);
X	splx(s);
X	return 0;
X}
X
Xint
Xwdclose(dev, flag, fmt)
X	dev_t dev;
X	int flag;
X	int fmt;
X{
X	struct disk *du;
X	int part = WDPART(dev), mask = 1 << part;
X    
X	du = wddrives[WDUNIT(dev)];
X    
X	/* insure only one open at a time */
X	du->dk_openpart &= ~mask;
X	switch (fmt) {
X	case S_IFCHR:
X		du->dk_copenpart &= ~mask;
X		break;
X	case S_IFBLK:
X		du->dk_bopenpart &= ~mask;
X		break;
X	}
X	return 0;
X}
X
Xint
Xwdioctl(dev, cmd, addr, flag, p)
X	dev_t dev;
X	int cmd;
X	caddr_t addr;
X	int flag;
X	struct proc *p;
X{
X	int lunit = WDUNIT(dev);
X	struct disk *du = wddrives[lunit];
X	int error;
X    
X	switch (cmd) {
X	case DIOCSBAD:
X		if ((flag & FWRITE) == 0)
X			return EBADF;
X		du->dk_cpd.bad = *(struct dkbad *)addr;
X		bad144intern(du);
X		return 0;
X
X	case DIOCGDINFO:
X		*(struct disklabel *)addr = du->dk_dd;
X		return 0;
X	
X	case DIOCGPART:
X		((struct partinfo *)addr)->disklab = &du->dk_dd;
X		((struct partinfo *)addr)->part =
X		    &du->dk_dd.d_partitions[WDPART(dev)];
X		return 0;
X	
X	case DIOCSDINFO:
X		if ((flag & FWRITE) == 0)
X			return EBADF;
X		error = setdisklabel(&du->dk_dd,
X		    (struct disklabel *)addr,
X		    /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart : */0,
X		    &du->dk_cpd);
X		if (error == 0) {
X			du->dk_flags |= DKFL_BSDLABEL;
X			wdsetctlr(du);
X		}
X		return error;
X	
X	case DIOCWLABEL:
X		du->dk_flags &= ~DKFL_WRITEPROT;
X		if ((flag & FWRITE) == 0)
X			return EBADF;
X		du->dk_wlabel = *(int *)addr;
X		return 0;
X	
X	case DIOCWDINFO:
X		du->dk_flags &= ~DKFL_WRITEPROT;
X		if ((flag & FWRITE) == 0)
X			return EBADF;
X		error = setdisklabel(&du->dk_dd,
X		    (struct disklabel *)addr,
X		    /*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart :*/0,
X		    &du->dk_cpd);
X		if (error == 0) {
X			int wlab;
X	    
X			du->dk_flags |= DKFL_BSDLABEL;
X			wdsetctlr(du);
X	    
X			/* simulate opening partition 0 so write succeeds */
X			du->dk_openpart |= (1 << 0);	    /* XXX */
X			wlab = du->dk_wlabel;
X			du->dk_wlabel = 1;
X			error = writedisklabel(dev, wdstrategy, &du->dk_dd,				    &du->dk_cpd);
X			du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
X			du->dk_wlabel = wlab;
X		}
X		return error;
X	
X#ifdef notyet
X	case DIOCGDINFOP:
X		*(struct disklabel **)addr = &du->dk_dd;
X		return 0;
X	
X	case DIOCWFORMAT:
X		if ((flag & FWRITE) == 0)
X			return EBADF;
X	{
X		register struct format_op *fop;
X		struct iovec aiov;
X		struct uio auio;
X	    
X		fop = (struct format_op *)addr;
X		aiov.iov_base = fop->df_buf;
X		aiov.iov_len = fop->df_count;
X		auio.uio_iov = &aiov;
X		auio.uio_iovcnt = 1;
X		auio.uio_resid = fop->df_count;
X		auio.uio_segflg = 0;
X		auio.uio_offset =
X		    fop->df_startblk * du->dk_dd.d_secsize;
X		error = physio(wdformat, &rwdbuf[lunit], dev, B_WRITE,
X		    minphys, &auio);
X		fop->df_count -= auio.uio_resid;
X		fop->df_reg[0] = du->dk_status;
X		fop->df_reg[1] = du->dk_error;
X		return error;
X	}
X#endif
X	
X	default:
X		return ENOTTY;
X	}
X
X#ifdef DIAGNOSTIC
X	panic("wdioctl: impossible");
X#endif
X}
X
X#ifdef B_FORMAT
Xint
Xwdformat(struct buf *bp)
X{
X
X	bp->b_flags |= B_FORMAT;
X	return wdstrategy(bp);
X}
X#endif
X
Xint
Xwdsize(dev)
X	dev_t dev;
X{
X	int lunit = WDUNIT(dev), part = WDPART(dev);
X	struct disk *du;
X    
X	if (lunit >= NWD)
X		return -1;
X	du = wddrives[lunit];
X	if (du == 0)
X		return -1;
X    
X	if (du->dk_state < OPEN || (du->dk_flags & DKFL_BSDLABEL) == 0) {
X		int val;
X		val = wdopen(makewddev(major(dev), lunit, WDRAW), FREAD,
X		    S_IFBLK, 0);
X		/* XXX Clear the open flag? */
X		if (val != 0)
X			return -1;
X	}
X    
X	if ((du->dk_flags & (DKFL_WRITEPROT | DKFL_BSDLABEL)) != DKFL_BSDLABEL)
X		return -1;
X
X	return (int)du->dk_dd.d_partitions[part].p_size;
X}
X
X/* dump core after a system crash */
Xint
Xwddump(dev)
X	dev_t dev;
X{
X	register struct disk *du;	/* disk unit to do the IO */
X	long num;			/* number of sectors to write */
X	int ctrlr, lunit, part, wdc;
X	long blkoff, blknum;
X	long cylin, head, sector, stat;
X	long secpertrk, secpercyl, nblocks, i;
X	char *addr;
X	static wddoingadump = 0;
X	extern caddr_t CADDR1;
X	extern struct pte *CMAP1;
X	
X	addr = (char *)0;		/* starting address */
X    
X#if DO_NOT_KNOW_HOW
X	/* toss any characters present prior to dump, ie. non-blocking getc */
X	while (cngetc())
X		;
X#endif
X    
X	/* size of memory to dump */
X	lunit = WDUNIT(dev);
X	part = WDPART(dev);	/* file system */
X	/* check for acceptable drive number */
X	if (lunit >= NWD)
X		return ENXIO;
X	du = wddrives[lunit];
X	/* was it ever initialized ? */
X	if (du == 0 || du->dk_state < OPEN || du->dk_flags & DKFL_WRITEPROT)
X		return ENXIO;
X
X	wdc = du->dk_port;
X	ctrlr = du->dk_ctrlr;
X    
X	/* Convert to disk sectors */
X	num = ctob(physmem) / du->dk_dd.d_secsize;
X    
X	/* check if controller active */
X	/*if (wdtab[ctrlr].b_active)
X		return EFAULT;*/
X	if (wddoingadump)
X		return EFAULT;
X    
X	secpertrk = du->dk_dd.d_nsectors;
X	secpercyl = du->dk_dd.d_secpercyl;
X	nblocks = du->dk_dd.d_partitions[part].p_size;
X	blkoff = du->dk_dd.d_partitions[part].p_offset;
X    
X	/*printf("xunit %x, nblocks %d, dumplo %d num %d\n", part, nblocks,
X	    dumplo, num);*/
X	/* check transfer bounds against partition size */
X	if (dumplo < 0 || dumplo + num > nblocks)
X		return EINVAL;
X    
X	/* mark controller active for if we panic during the dump */
X	/*wdtab[ctrlr].b_active = 1;*/
X	wddoingadump = 1;
X	i = 200000000;
X	if (wait_for_unbusy(du) < 0)
X		return EIO;
X	outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4));
X	if (wait_for_ready(du) < 0)
X		return EIO;
X	if (wdcommand(du, WDCC_RESTORE | WD_STEP) < 0)
X		return EIO;
X    
X	/* some compaq controllers require this ... */
X	wdsetctlr(du);
X    
X	blknum = dumplo + blkoff;
X	while (num > 0) {
X		/* compute disk address */
X		cylin = blknum / secpercyl;
X		head = (blknum % secpercyl) / secpertrk;
X		sector = blknum % secpertrk;
X	
X		if (du->dk_flags & DKFL_BADSECT) {
X			long newblk;
X			int i;
X
X			for (i = 0; du->dk_badsect[i] != -1; i++) {
X				if (blknum < du->dk_badsect[i]) {
X					/* sorted list, passed our block by */
X					break;
X				} else if (blknum == du->dk_badsect[i]) {
X					newblk = du->dk_dd.d_secperunit -
X						du->dk_dd.d_nsectors - i - 1;
X					cylin = newblk / secpercyl;
X					head = (newblk % secpercyl) / secpertrk;
X					sector = newblk % secpertrk;
X					/* found and replaced; done */
X					break;
X				}
X			}
X		}
X		sector++;		/* origin 1 */
X	    
X		if (wait_for_unbusy(du) < 0)
X			return EIO;
X		/* select drive.     */
X		outb(wdc+wd_sdh, WDSD_IBM | (du->dk_unit << 4) | (head & 0xf));
X		if (wait_for_ready(du) < 0)
X			return EIO;
X
X		/* transfer some blocks */
X		outb(wdc+wd_sector, sector);
X		outb(wdc+wd_seccnt, 1);
X		outb(wdc+wd_cyl_lo, cylin);
X		outb(wdc+wd_cyl_hi, cylin >> 8);
X#ifdef notdef
X		/* lets just talk about this first...*/
X		printf("sdh 0%o sector %d cyl %d addr 0x%x", inb(wdc+wd_sdh),
X		    inb(wdc+wd_sector),
X		    (inb(wdc+wd_cyl_hi) << 8) + inb(wdc+wd_cyl_lo), addr);
X#endif
X		stat = wdcommand(du, WDCC_WRITE);
X		if (stat < 0 || stat & WDCS_ERR)
X			return EIO;
X	
X#ifdef notdef	/* cannot use this since this address was mapped differently */
X		pmap_enter(kernel_pmap, CADDR1, trunc_page(addr), VM_PROT_READ, TRUE);
X#else
X		*(int *)CMAP1 = PG_V | PG_KW | ctob((long)addr);
X		tlbflush();
X#endif
X	
X		outsw(wdc+wd_data, CADDR1 + ((int)addr & PGOFSET),
X		    DEV_BSIZE / sizeof(short));
X	
X		/* Check data request (should be done). */
X		if (inb(wdc+wd_status) & (WDCS_ERR | WDCS_DRQ))
X			return EIO;
X	
X		if (wait_for_unbusy(du) < 0)
X			return EIO;
X
X		/* error check the xfer */
X		if (inb(wdc+wd_status) & WDCS_ERR)
X			return EIO;
X	
X		if ((unsigned)addr % 1048576 == 0)
X			printf("%d ", num / (1048576 / DEV_BSIZE));
X
X		/* update block count */
X		num--;
X		blknum++;
X		(int)addr += DEV_BSIZE;
X	
X#if DO_NOT_KNOW_HOW
X		/* operator aborting dump? non-blocking getc() */
X		if (cngetc())
X			return EINTR;
X#endif
X	}
X	return 0;
X}
X
X/*
X * Internalize the bad sector table.
X */
Xvoid
Xbad144intern(du)
X	struct disk *du;
X{
X	int i;
X
X	if ((du->dk_flags & DKFL_BADSECT) == 0)
X		return;
X
X	for (i = 0; i < 127; i++)
X		du->dk_badsect[i] = -1;
X	for (i = 0; i < 126; i++) {
X		if (du->dk_cpd.bad.bt_bad[i].bt_cyl == 0xffff)
X			break;
X		du->dk_badsect[i] =
X		    du->dk_cpd.bad.bt_bad[i].bt_cyl * du->dk_dd.d_secpercyl +
X		    (du->dk_cpd.bad.bt_bad[i].bt_trksec >> 8) *
X			du->dk_dd.d_nsectors +
X		    (du->dk_cpd.bad.bt_bad[i].bt_trksec & 0x00ff);
X	}
X}
X
Xstatic int
Xwdreset(du, err)
X	struct disk *du;
X	int err;
X{
X	int wdc = du->dk_port;
X	int stat;
X
X	if (err)
X		printf("wdc%d: busy too long, resetting\n", du->dk_ctrlr);
X
X	/* reset the device  */
X	outb(wdc+wd_ctlr, WDCTL_RST | WDCTL_IDS);
X	DELAY(1000);
X	outb(wdc+wd_ctlr, WDCTL_IDS);
X	DELAY(1000);
X	(void) inb(wdc+wd_error);
X	outb(wdc+wd_ctlr, WDCTL_4BIT);
X
X	if (wait_for_unbusy(du) < 0)
X		printf("wdc%d: failed to reset controller\n", du->dk_ctrlr);
X}
X
Xint
Xwdc_wait(du, mask)
X	struct disk *du;
X	int mask;
X{
X	int wdc = du->dk_port;
X	int timeout = 0;
X	int stat;
X
X	for (;;) {
X		stat = inb(wdc+wd_altsts);
X		if ((stat & WDCS_BUSY) == 0 && (stat & mask) == mask)
X			break;
X		if (++timeout > WDCNDELAY)
X			return -1;
X		DELAY(WDCDELAY);
X	}
X#ifdef WDCNDELAY_DEBUG
X	if (timeout > WDCNDELAY_DEBUG)
X		printf("wdc%d: timeout took %dus\n", du->dk_ctrlr,
X		    WDCDELAY * timeout);
X#endif
X	return stat;
X}
X
Xstatic int
Xwdtimeout(arg)
X	caddr_t arg;
X{
X	int s = splbio();
X	struct disk *du = (void *)arg;
X
X	if (du->dk_timeout && --du->dk_timeout == 0) {
X		int wdc = du->dk_port;
X
X		printf("wd%d: lost interrupt - status %x, error %x\n",
X		    du->dk_lunit, inb(wdc+wd_status), inb(wdc+wd_error));
X		wdreset(du, 0);
X		du->dk_skip = 0;
X		du->dk_flags |= DKFL_SINGLE;
X		wdstart(du->dk_ctrlr);		/* start controller */
X	}
X	timeout((timeout_t)wdtimeout, arg, hz);
X	splx(s);
X	return 0;
X}
X
X#endif /* NWDC > 0 */
END-of-wd.c
echo x - wdreg.h
sed 's/^X//' >wdreg.h << 'END-of-wdreg.h'
X/*-
X * Copyright (c) 1991 The Regents of the University of California.
X * All rights reserved.
X *
X * This code is derived from software contributed to Berkeley by
X * William Jolitz.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by the University of
X *	California, Berkeley and its contributors.
X * 4. Neither the name of the University nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X *	from: @(#)wdreg.h	7.1 (Berkeley) 5/9/91
X *	$Id: wdreg.h,v 1.4 1994/03/02 21:43:42 mycroft Exp $
X */
X
X/*
X * Disk Controller register definitions.
X */
X#define	wd_data		0x0		/* data register (R/W - 16 bits) */
X#define wd_error	0x1		/* error register (R) */
X#define	wd_precomp	wd_error	/* write precompensation (W) */
X#define	wd_seccnt	0x2		/* sector count (R/W) */
X#define	wd_sector	0x3		/* first sector number (R/W) */
X#define	wd_cyl_lo	0x4		/* cylinder address, low byte (R/W) */
X#define	wd_cyl_hi	0x5		/* cylinder address, high byte (R/W)*/
X#define	wd_sdh		0x6		/* sector size/drive/head (R/W)*/
X#define	wd_command	0x7		/* command register (W)	 */
X#define	wd_status wd_command		/* immediate status (R)	 */
X
X#define	wd_altsts	0x206	 /*alternate fixed disk status(via 1015) (R)*/
X#define	wd_ctlr		0x206	 /*fixed disk controller control(via 1015) (W)*/
X#define  WDCTL_4BIT	 0x8	/* use four head bits (wd1003) */
X#define  WDCTL_RST	 0x4	/* reset the controller */
X#define  WDCTL_IDS	 0x2	/* disable controller interrupts */
X#define	wd_digin	0x207	 /* disk controller input(via 1015) (R)*/
X
X/*
X * Status Bits.
X */
X#define	WDCS_BUSY	0x80		/* Controller busy bit. */
X#define	WDCS_READY	0x40		/* Selected drive is ready */
X#define	WDCS_WRTFLT	0x20		/* Write fault */
X#define	WDCS_SEEKCMPLT	0x10		/* Seek complete */
X#define	WDCS_DRQ	0x08		/* Data request bit. */
X#define	WDCS_ECCCOR	0x04		/* ECC correction made in data */
X#define	WDCS_INDEX	0x02		/* Index pulse from selected drive */
X#define	WDCS_ERR	0x01		/* Error detect bit. */
X
X#define WDCS_BITS	"\020\010busy\006rdy\006wrtflt\005seekdone\004drq\003ecc_cor\002index\001err"
X
X#define WDERR_BITS	"\020\010badblk\007uncorr\006id_crc\005no_id\003abort\002tr000\001no_dam"
X
X/*
X * Commands for Disk Controller.
X */
X#define	WDCC_RESTORE	0x10		/* disk restore code -- resets cntlr */
X
X#define	WDCC_READ	0x20		/* disk read code */
X#define	WDCC_WRITE	0x30		/* disk write code */
X#define	 WDCC__LONG	 0x02		 /* modifier -- access ecc bytes */
X#define	 WDCC__NORETRY	 0x01		 /* modifier -- no retrys */
X
X#define	WDCC_FORMAT	0x50		/* disk format code */
X#define	WDCC_DIAGNOSE	0x90		/* controller diagnostic */
X#define	WDCC_IDC	0x91		/* initialize drive command */
X
X#define	WDCC_EXTDCMD	0xE0		/* send extended command */
X#define	WDCC_READP	0xEC		/* read parameters from controller */
X#define	WDCC_CACHEC	0xEF		/* cache control */
X
X#define	WD_STEP		0		/* winchester- default 35us step */
X
X#define	WDSD_IBM	0xa0		/* forced to 512 byte sector, ecc */
X
X
X#ifdef KERNEL
X/*
X * read parameters command returns this:
X */
Xstruct wdparams {
X	/* drive info */
X	short	wdp_config;		/* general configuration */
X	short	wdp_fixedcyl;		/* number of non-removable cylinders */
X	short	wdp_removcyl;		/* number of removable cylinders */
X	short	wdp_heads;		/* number of heads */
X	short	wdp_unfbytespertrk;	/* number of unformatted bytes/track */
X	short	wdp_unfbytes;		/* number of unformatted bytes/sector */
X	short	wdp_sectors;		/* number of sectors */
X	short	wdp_minisg;		/* minimum bytes in inter-sector gap*/
X	short	wdp_minplo;		/* minimum bytes in postamble */
X	short	wdp_vendstat;		/* number of words of vendor status */
X	/* controller info */
X	char	wdp_cnsn[20];		/* controller serial number */
X	short	wdp_cntype;		/* controller type */
X#define	WDTYPE_SINGLEPORTSECTOR	1	 /* single port, single sector buffer */
X#define	WDTYPE_DUALPORTMULTI	2	 /* dual port, multiple sector buffer */
X#define	WDTYPE_DUALPORTMULTICACHE 3	 /* above plus track cache */
X	short	wdp_cnsbsz;		/* sector buffer size, in sectors */
X	short	wdp_necc;		/* ecc bytes appended */
X	char	wdp_rev[8];		/* firmware revision */
X	char	wdp_model[40];		/* model name */
X	short	wdp_nsecperint;		/* sectors per interrupt */
X	short	wdp_usedmovsd;		/* can use double word read/write? */
X};
X
X/*
X * wd driver entry points
X */
Xint wdprobe(struct isa_device *);
Xint wdattach(struct isa_device *);
Xint wdstrategy(struct buf *);
Xvoid wdintr(int);
Xint wdopen(dev_t, int, int, struct proc *);
Xint wdclose(dev_t, int, int);
Xint wdioctl(dev_t, int, caddr_t, int, struct proc *);
X/* int wdformat(struct buf *bp); */
Xint wdsize(dev_t);
Xint wddump(dev_t);
X
X#endif KERNEL
END-of-wdreg.h
exit


------------------------------------------------------------------------------