NetBSD/sys/dev/ata/wd.c
1998-01-15 01:26:13 +00:00

1002 lines
23 KiB
C

/* $NetBSD: wd.c,v 1.169 1998/01/15 01:26:13 cgd Exp $ */
/*
* Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved.
*
* DMA and multi-sector PIO handling are derived from code contributed by
* Onno van der Linden.
*
* Bus_space-ified by Christopher G. Demetriou.
*
* 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 Charles M. Hannum.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*/
#include "rnd.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/buf.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/disk.h>
#include <sys/syslog.h>
#include <sys/proc.h>
#if NRND > 0
#include <sys/rnd.h>
#endif
#include <vm/vm.h>
#include <machine/intr.h>
#include <machine/bus.h>
#include <dev/ic/wdcreg.h> /* XXX */
#include <dev/ic/wdcvar.h> /* XXX */
#include <dev/ata/wdlink.h>
#include "locators.h"
#define WAITTIME (4 * hz) /* time to wait for a completion */
#define WDIORETRIES 5 /* number of retries before giving up */
#define WDUNIT(dev) DISKUNIT(dev)
#define WDPART(dev) DISKPART(dev)
#define MAKEWDDEV(maj, unit, part) MAKEDISKDEV(maj, unit, part)
#define WDLABELDEV(dev) (MAKEWDDEV(major(dev), WDUNIT(dev), RAW_PART))
#ifdef WDDEBUG
#define WDDEBUG_PRINT(args) printf args
#else
#define WDDEBUG_PRINT(args)
#endif
struct wd_softc {
struct device sc_dev;
struct disk sc_dk;
struct wd_link *d_link;
struct buf sc_q;
#if NRND > 0
rndsource_element_t rnd_source;
#endif
};
#ifdef __BROKEN_INDIRECT_CONFIG
int wdprobe __P((struct device *, void *, void *));
#else
int wdprobe __P((struct device *, struct cfdata *, void *));
#endif
void wdattach __P((struct device *, struct device *, void *));
int wdprint __P((void *, char *));
struct cfattach wd_ca = {
sizeof(struct wd_softc), wdprobe, wdattach
};
extern struct cfdriver wd_cd;
void wdgetdefaultlabel __P((struct wd_softc *, struct disklabel *));
void wdgetdisklabel __P((struct wd_softc *));
int wd_get_parms __P((struct wd_softc *));
void wdstrategy __P((struct buf *));
void wdstart __P((void *));
struct dkdriver wddkdriver = { wdstrategy };
/* XXX: these should go elsewhere */
cdev_decl(wd);
bdev_decl(wd);
void wdfinish __P((struct wd_softc *, struct buf *));
int wdsetctlr __P((struct wd_link *));
static void bad144intern __P((struct wd_softc *));
int wdlock __P((struct wd_link *));
void wdunlock __P((struct wd_link *));
int
#ifdef __BROKEN_INDIRECT_CONFIG
wdprobe(parent, matchv, aux)
#else
wdprobe(parent, match, aux)
#endif
struct device *parent;
#ifdef __BROKEN_INDIRECT_CONFIG
void *matchv;
#else
struct cfdata *match;
#endif
void *aux;
{
#ifdef __BROKEN_INDIRECT_CONFIG
struct cfdata *match = matchv;
#endif
struct wd_link *d_link = aux;
int drive;
if (d_link == NULL)
return 0;
if (d_link->type != ATA)
return 0;
drive = d_link->drive;
if (match->cf_loc[ATACF_DRIVE] != ATACF_DRIVE_DEFAULT &&
match->cf_loc[ATACF_DRIVE] != drive)
return 0;
return 1;
}
void
wdattach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
struct wd_softc *wd = (void *)self;
struct wd_link *d_link= aux;
int i, blank;
char buf[41], c, *p, *q;
wd->d_link = d_link;
d_link->openings = 1;
d_link->wd_softc = (caddr_t)wd;
/*
* Initialize and attach the disk structure.
*/
wd->sc_dk.dk_driver = &wddkdriver;
wd->sc_dk.dk_name = wd->sc_dev.dv_xname;
disk_attach(&wd->sc_dk);
d_link->sc_lp = wd->sc_dk.dk_label;
wdc_get_parms((struct wdc_softc *)d_link->wdc_softc, d_link);
for (blank = 0, p = d_link->sc_params.wdp_model, q = buf, i = 0;
i < sizeof(d_link->sc_params.wdp_model); i++) {
c = *p++;
if (c == '\0')
break;
if (c != ' ') {
if (blank) {
*q++ = ' ';
blank = 0;
}
*q++ = c;
} else
blank = 1;
}
*q++ = '\0';
printf(": <%s>\n%s: %dMB, %d cyl, %d head, %d sec, %d bytes/sec\n",
buf, self->dv_xname,
d_link->sc_params.wdp_cylinders *
(d_link->sc_params.wdp_heads * d_link->sc_params.wdp_sectors) /
(1048576 / DEV_BSIZE),
d_link->sc_params.wdp_cylinders,
d_link->sc_params.wdp_heads,
d_link->sc_params.wdp_sectors,
DEV_BSIZE);
if ((d_link->sc_params.wdp_capabilities & WD_CAP_DMA) != 0 &&
d_link->sc_mode == WDM_DMA) {
d_link->sc_mode = WDM_DMA;
} else if (d_link->sc_params.wdp_maxmulti > 1) {
d_link->sc_mode = WDM_PIOMULTI;
d_link->sc_multiple = min(d_link->sc_params.wdp_maxmulti, 16);
} else {
d_link->sc_mode = WDM_PIOSINGLE;
d_link->sc_multiple = 1;
}
printf("%s: using", wd->sc_dev.dv_xname);
if (d_link->sc_mode == WDM_DMA)
printf(" dma transfers,");
else
printf(" %d-sector %d-bit pio transfers,",
d_link->sc_multiple,
(((struct wdc_softc *)d_link->wdc_softc)->sc_cap &
WDC_CAPABILITY_DATA32) == 0 ? 16 : 32);
if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0)
printf(" lba addressing\n");
else
printf(" chs addressing\n");
#if NRND > 0
rnd_attach_source(&wd->rnd_source, wd->sc_dev.dv_xname, RND_TYPE_DISK);
#endif
}
/*
* Read/write routine for a buffer. Validates the arguments and schedules the
* transfer. Does not wait for the transfer to complete.
*/
void
wdstrategy(bp)
struct buf *bp;
{
struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(bp->b_dev)];
struct wd_link *d_link= wd->d_link;
int s;
/* Valid request? */
if (bp->b_blkno < 0 ||
(bp->b_bcount % wd->sc_dk.dk_label->d_secsize) != 0 ||
(bp->b_bcount / wd->sc_dk.dk_label->d_secsize) >= (1 << NBBY)) {
bp->b_error = EINVAL;
goto bad;
}
/* If device invalidated (e.g. media change, door open), error. */
if ((d_link->sc_flags & WDF_LOADED) == 0) {
bp->b_error = EIO;
goto bad;
}
/* If it's a null transfer, return immediately. */
if (bp->b_bcount == 0)
goto done;
/*
* Do bounds checking, adjust transfer. if error, process.
* If end of partition, just return.
*/
if (WDPART(bp->b_dev) != RAW_PART &&
bounds_check_with_label(bp, wd->sc_dk.dk_label,
(d_link->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0)
goto done;
/* Queue transfer on drive, activate drive and controller if idle. */
s = splbio();
disksort(&wd->sc_q, bp);
wdstart(wd);
splx(s);
return;
bad:
bp->b_flags |= B_ERROR;
done:
/* Toss transfer; we're done early. */
bp->b_resid = bp->b_bcount;
biodone(bp);
}
/*
* Queue a drive for I/O.
*/
void
wdstart(arg)
void *arg;
{
struct wd_softc *wd = arg;
struct buf *dp, *bp=0;
struct wd_link *d_link = wd->d_link;
struct wdc_xfer *xfer;
u_long p_offset;
while (d_link->openings > 0) {
/* Is there a buf for us ? */
dp = &wd->sc_q;
if ((bp = dp->b_actf) == NULL) /* yes, an assign */
return;
dp->b_actf = bp->b_actf;
/*
* Make the command. First lock the device
*/
d_link->openings--;
if (WDPART(bp->b_dev) != RAW_PART)
p_offset =
wd->sc_dk.dk_label->d_partitions[WDPART(bp->b_dev)].p_offset;
else
p_offset = 0;
xfer = wdc_get_xfer(0);
if (xfer == NULL)
panic("wdc_xfer");
xfer->d_link = d_link;
xfer->c_bp = bp;
xfer->c_p_offset = p_offset;
xfer->databuf = bp->b_data;
xfer->c_bcount = bp->b_bcount;
xfer->c_flags |= bp->b_flags & (B_READ|B_WRITE);
xfer->c_blkno = bp->b_blkno;
/* Instrumentation. */
disk_busy(&wd->sc_dk);
wdc_exec_xfer((struct wdc_softc *)wd->d_link->wdc_softc,
wd->d_link, xfer);
}
}
int
wdread(dev, uio, flags)
dev_t dev;
struct uio *uio;
int flags;
{
WDDEBUG_PRINT(("wdread\n"));
return (physio(wdstrategy, NULL, dev, B_READ, minphys, uio));
}
int
wdwrite(dev, uio, flags)
dev_t dev;
struct uio *uio;
int flags;
{
WDDEBUG_PRINT(("wdwrite\n"));
return (physio(wdstrategy, NULL, dev, B_WRITE, minphys, uio));
}
/*
* Wait interruptibly for an exclusive lock.
*
* XXX
* Several drivers do this; it should be abstracted and made MP-safe.
*/
int
wdlock(d_link)
struct wd_link *d_link;
{
int error;
int s;
WDDEBUG_PRINT(("wdlock\n"));
s = splbio();
while ((d_link->sc_flags & WDF_LOCKED) != 0) {
d_link->sc_flags |= WDF_WANTED;
if ((error = tsleep(d_link, PRIBIO | PCATCH,
"wdlck", 0)) != 0) {
splx(s);
return error;
}
}
d_link->sc_flags |= WDF_LOCKED;
splx(s);
return 0;
}
/*
* Unlock and wake up any waiters.
*/
void
wdunlock(d_link)
struct wd_link *d_link;
{
WDDEBUG_PRINT(("wdunlock"));
d_link->sc_flags &= ~WDF_LOCKED;
if ((d_link->sc_flags & WDF_WANTED) != 0) {
d_link->sc_flags &= ~WDF_WANTED;
wakeup(d_link);
}
}
int
wdopen(dev, flag, fmt, p)
dev_t dev;
int flag, fmt;
struct proc *p;
{
struct wd_softc *wd;
struct wd_link *d_link;
int unit, part;
int error;
WDDEBUG_PRINT(("wdopen\n"));
unit = WDUNIT(dev);
if (unit >= wd_cd.cd_ndevs)
return ENXIO;
wd = wd_cd.cd_devs[unit];
if (wd == NULL)
return ENXIO;
d_link = wd->d_link;
if ((error = wdlock(d_link)) != 0)
return error;
if (wd->sc_dk.dk_openmask != 0) {
/*
* If any partition is open, but the disk has been invalidated,
* disallow further opens.
*/
if ((d_link->sc_flags & WDF_LOADED) == 0) {
error = EIO;
goto bad3;
}
} else {
if ((d_link->sc_flags & WDF_LOADED) == 0) {
d_link->sc_flags |= WDF_LOADED;
/* Load the physical device parameters. */
if (wdc_get_parms((struct wdc_softc *)d_link->wdc_softc,
d_link) != 0) {
error = ENXIO;
goto bad2;
}
/* Load the partition info if not already loaded. */
wdgetdisklabel(wd);
}
}
part = WDPART(dev);
/* Check that the partition exists. */
if (part != RAW_PART &&
(part >= wd->sc_dk.dk_label->d_npartitions ||
wd->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
error = ENXIO;
goto bad;
}
/* Insure only one open at a time. */
switch (fmt) {
case S_IFCHR:
wd->sc_dk.dk_copenmask |= (1 << part);
break;
case S_IFBLK:
wd->sc_dk.dk_bopenmask |= (1 << part);
break;
}
wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask;
wdunlock(d_link);
return 0;
bad2:
d_link->sc_flags &= ~WDF_LOADED;
bad:
if (wd->sc_dk.dk_openmask == 0) {
}
bad3:
wdunlock(d_link);
return error;
}
int
wdclose(dev, flag, fmt, p)
dev_t dev;
int flag, fmt;
struct proc *p;
{
struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)];
int part = WDPART(dev);
int error;
if ((error = wdlock(wd->d_link)) != 0)
return error;
switch (fmt) {
case S_IFCHR:
wd->sc_dk.dk_copenmask &= ~(1 << part);
break;
case S_IFBLK:
wd->sc_dk.dk_bopenmask &= ~(1 << part);
break;
}
wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask;
if (wd->sc_dk.dk_openmask == 0) {
/* XXXX Must wait for I/O to complete! */
}
wdunlock(wd->d_link);
return 0;
}
void
wdgetdefaultlabel(wd, lp)
struct wd_softc *wd;
struct disklabel *lp;
{
struct wd_link *d_link = wd->d_link;
bzero(lp, sizeof(struct disklabel));
lp->d_secsize = DEV_BSIZE;
lp->d_ntracks = d_link->sc_params.wdp_heads;
lp->d_nsectors = d_link->sc_params.wdp_sectors;
lp->d_ncylinders = d_link->sc_params.wdp_cylinders;
lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
#if 0
strncpy(lp->d_typename, "ST506 disk", 16);
lp->d_type = DTYPE_ST506;
#endif
strncpy(lp->d_packname, d_link->sc_params.wdp_model, 16);
lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
lp->d_rpm = 3600;
lp->d_interleave = 1;
lp->d_flags = 0;
lp->d_partitions[RAW_PART].p_offset = 0;
lp->d_partitions[RAW_PART].p_size =
lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
lp->d_npartitions = RAW_PART + 1;
lp->d_magic = DISKMAGIC;
lp->d_magic2 = DISKMAGIC;
lp->d_checksum = dkcksum(lp);
}
/*
* Fabricate a default disk label, and try to read the correct one.
*/
void
wdgetdisklabel(wd)
struct wd_softc *wd;
{
struct disklabel *lp = wd->sc_dk.dk_label;
struct wd_link *d_link = wd->d_link;
char *errstring;
WDDEBUG_PRINT(("wdgetdisklabel\n"));
bzero(wd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel));
wdgetdefaultlabel(wd, lp);
d_link->sc_badsect[0] = -1;
if (d_link->sc_state > RECAL)
d_link->sc_state = RECAL;
errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART),
wdstrategy, lp, wd->sc_dk.dk_cpulabel);
if (errstring) {
/*
* This probably happened because the drive's default
* geometry doesn't match the DOS geometry. We
* assume the DOS geometry is now in the label and try
* again. XXX This is a kluge.
*/
if (d_link->sc_state > GEOMETRY)
d_link->sc_state = GEOMETRY;
errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART),
wdstrategy, lp, wd->sc_dk.dk_cpulabel);
}
if (errstring) {
printf("%s: %s\n", wd->sc_dev.dv_xname, errstring);
return;
}
if (d_link->sc_state > GEOMETRY)
d_link->sc_state = GEOMETRY;
if ((lp->d_flags & D_BADSECT) != 0)
bad144intern(wd);
}
/*
* Tell the drive what geometry to use.
*/
int
wdsetctlr(d_link)
struct wd_link *d_link;
{
struct wd_softc *wd=(struct wd_softc *)d_link->wd_softc;
WDDEBUG_PRINT(("wd(%d,%d) C%dH%dS%d\n", wd->sc_dev.dv_unit,
d_link->drive, wd->sc_dk.dk_label->d_ncylinders,
wd->sc_dk.dk_label->d_ntracks, wd->sc_dk.dk_label->d_nsectors));
if (wdccommand((struct wdc_softc *)d_link->wdc_softc,
d_link, WDCC_IDP, d_link->drive,
wd->sc_dk.dk_label->d_ncylinders,
wd->sc_dk.dk_label->d_ntracks - 1, 0,
wd->sc_dk.dk_label->d_nsectors) != 0) {
wderror(d_link, NULL, "wdsetctlr: geometry upload failed");
return -1;
}
return 0;
}
int
wdioctl(dev, xfer, addr, flag, p)
dev_t dev;
u_long xfer;
caddr_t addr;
int flag;
struct proc *p;
{
struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)];
struct wd_link *d_link = wd->d_link;
int error;
WDDEBUG_PRINT(("wdioctl\n"));
if ((d_link->sc_flags & WDF_LOADED) == 0)
return EIO;
switch (xfer) {
case DIOCSBAD:
if ((flag & FWRITE) == 0)
return EBADF;
wd->sc_dk.dk_cpulabel->bad = *(struct dkbad *)addr;
wd->sc_dk.dk_label->d_flags |= D_BADSECT;
bad144intern(wd);
return 0;
case DIOCGDINFO:
*(struct disklabel *)addr = *(wd->sc_dk.dk_label);
return 0;
case DIOCGPART:
((struct partinfo *)addr)->disklab = wd->sc_dk.dk_label;
((struct partinfo *)addr)->part =
&wd->sc_dk.dk_label->d_partitions[WDPART(dev)];
return 0;
case DIOCWDINFO:
case DIOCSDINFO:
if ((flag & FWRITE) == 0)
return EBADF;
if ((error = wdlock(wd->d_link)) != 0)
return error;
d_link->sc_flags |= WDF_LABELLING;
error = setdisklabel(wd->sc_dk.dk_label,
(struct disklabel *)addr, /*wd->sc_dk.dk_openmask : */0,
wd->sc_dk.dk_cpulabel);
if (error == 0) {
if (d_link->sc_state > GEOMETRY)
d_link->sc_state = GEOMETRY;
if (xfer == DIOCWDINFO)
error = writedisklabel(WDLABELDEV(dev),
wdstrategy, wd->sc_dk.dk_label,
wd->sc_dk.dk_cpulabel);
}
d_link->sc_flags &= ~WDF_LABELLING;
wdunlock(d_link);
return error;
case DIOCWLABEL:
if ((flag & FWRITE) == 0)
return EBADF;
if (*(int *)addr)
d_link->sc_flags |= WDF_WLABEL;
else
d_link->sc_flags &= ~WDF_WLABEL;
return 0;
case DIOCGDEFLABEL:
wdgetdefaultlabel(wd, (struct disklabel *)addr);
return 0;
#ifdef notyet
case DIOCWFORMAT:
if ((flag & FWRITE) == 0)
return EBADF;
{
register struct format_op *fop;
struct iovec aiov;
struct uio auio;
fop = (struct format_op *)addr;
aiov.iov_base = fop->df_buf;
aiov.iov_len = fop->df_count;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_resid = fop->df_count;
auio.uio_segflg = 0;
auio.uio_offset =
fop->df_startblk * wd->sc_dk.dk_label->d_secsize;
auio.uio_procp = p;
error = physio(wdformat, NULL, dev, B_WRITE, minphys,
&auio);
fop->df_count -= auio.uio_resid;
fop->df_reg[0] = wdc->sc_status;
fop->df_reg[1] = wdc->sc_error;
return error;
}
#endif
default:
return ENOTTY;
}
#ifdef DIAGNOSTIC
panic("wdioctl: impossible");
#endif
}
#ifdef B_FORMAT
int
wdformat(struct buf *bp)
{
bp->b_flags |= B_FORMAT;
return wdstrategy(bp);
}
#endif
int
wdsize(dev)
dev_t dev;
{
struct wd_softc *wd;
int part, unit, omask;
int size;
WDDEBUG_PRINT(("wdsize\n"));
unit = WDUNIT(dev);
if (unit >= wd_cd.cd_ndevs)
return (-1);
wd = wd_cd.cd_devs[unit];
if (wd == NULL)
return (-1);
part = WDPART(dev);
omask = wd->sc_dk.dk_openmask & (1 << part);
if (omask == 0 && wdopen(dev, 0, S_IFBLK, NULL) != 0)
return (-1);
if (wd->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP)
size = -1;
else
size = wd->sc_dk.dk_label->d_partitions[part].p_size *
(wd->sc_dk.dk_label->d_secsize / DEV_BSIZE);
if (omask == 0 && wdclose(dev, 0, S_IFBLK, NULL) != 0)
return (-1);
return (size);
}
#ifndef __BDEVSW_DUMP_OLD_TYPE
/* #define WD_DUMP_NOT_TRUSTED if you just want to watch */
static int wddoingadump;
static int wddumprecalibrated;
/*
* Dump core after a system crash.
*
* XXX: This needs work! Currently, it's a major hack: the
* use of wdc_softc is very bad and should go away.
*/
int
wddump(dev, blkno, va, size)
dev_t dev;
daddr_t blkno;
caddr_t va;
size_t size;
{
struct wd_softc *wd; /* disk unit to do the I/O */
struct wdc_softc *wdc; /* disk controller to do the I/O */
struct disklabel *lp; /* disk's disklabel */
struct wd_link *d_link;
int unit, part;
int nblks; /* total number of sectors left to write */
/* Check if recursive dump; if so, punt. */
if (wddoingadump)
return EFAULT;
wddoingadump = 1;
unit = WDUNIT(dev);
if (unit >= wd_cd.cd_ndevs)
return ENXIO;
wd = wd_cd.cd_devs[unit];
if (wd == (struct wd_softc *)0)
return ENXIO;
d_link = wd->d_link;
part = WDPART(dev);
/* Make sure it was initialized. */
if (d_link->sc_state < READY)
return ENXIO;
wdc = (void *)wd->sc_dev.dv_parent;
/* Convert to disk sectors. Request must be a multiple of size. */
lp = wd->sc_dk.dk_label;
if ((size % lp->d_secsize) != 0)
return EFAULT;
nblks = size / lp->d_secsize;
blkno = blkno / (lp->d_secsize / DEV_BSIZE);
/* Check transfer bounds against partition size. */
if ((blkno < 0) || ((blkno + nblks) > lp->d_partitions[part].p_size))
return EINVAL;
/* Offset block number to start of partition. */
blkno += lp->d_partitions[part].p_offset;
/* Recalibrate, if first dump transfer. */
if (wddumprecalibrated == 0) {
wddumprecalibrated = 1;
if (wdccommandshort(wdc, d_link->drive, WDCC_RECAL) != 0 ||
wait_for_ready(wdc) != 0 || wdsetctlr(d_link) != 0 ||
wait_for_ready(wdc) != 0) {
wderror(d_link, NULL, "wddump: recal failed");
return EIO;
}
}
while (nblks > 0) {
daddr_t xlt_blkno = blkno;
long cylin, head, sector;
if ((lp->d_flags & D_BADSECT) != 0) {
long blkdiff;
int i;
for (i = 0; (blkdiff = d_link->sc_badsect[i]) != -1; i++) {
blkdiff -= xlt_blkno;
if (blkdiff < 0)
continue;
if (blkdiff == 0) {
/* Replace current block of transfer. */
xlt_blkno = lp->d_secperunit -
lp->d_nsectors - i - 1;
}
break;
}
/* Tranfer is okay now. */
}
if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) {
sector = (xlt_blkno >> 0) & 0xff;
cylin = (xlt_blkno >> 8) & 0xffff;
head = (xlt_blkno >> 24) & 0xf;
head |= WDSD_LBA;
} else {
sector = xlt_blkno % lp->d_nsectors;
sector++; /* Sectors begin with 1, not 0. */
xlt_blkno /= lp->d_nsectors;
head = xlt_blkno % lp->d_ntracks;
xlt_blkno /= lp->d_ntracks;
cylin = xlt_blkno;
head |= WDSD_CHS;
}
#ifndef WD_DUMP_NOT_TRUSTED
if (wdccommand((struct wdc_softc *)d_link->wdc_softc, d_link,
WDCC_WRITE, d_link->drive, cylin, head, sector, 1) != 0 ||
wait_for_drq(wdc) != 0) {
wderror(d_link, NULL, "wddump: write failed");
return EIO;
}
/* XXX XXX XXX */
bus_space_write_multi_2(wdc->sc_iot, wdc->sc_ioh, wd_data,
(u_int16_t *)va, lp->d_secsize >> 1);
/* Check data request (should be done). */
if (wait_for_ready(wdc) != 0) {
wderror(d_link, NULL,
"wddump: timeout waiting for ready");
return EIO;
}
#else /* WD_DUMP_NOT_TRUSTED */
/* Let's just talk about this first... */
printf("wd%d: dump addr 0x%x, cylin %d, head %d, sector %d\n",
unit, va, cylin, head, sector);
delay(500 * 1000); /* half a second */
#endif
/* update block count */
nblks -= 1;
blkno += 1;
va += lp->d_secsize;
}
wddoingadump = 0;
return 0;
}
#else /* __BDEVSW_DUMP_NEW_TYPE */
int
wddump(dev, blkno, va, size)
dev_t dev;
daddr_t blkno;
caddr_t va;
size_t size;
{
/* Not implemented. */
return ENXIO;
}
#endif /* __BDEVSW_DUMP_NEW_TYPE */
/*
* Internalize the bad sector table.
*/
void
bad144intern(wd)
struct wd_softc *wd;
{
struct dkbad *bt = &wd->sc_dk.dk_cpulabel->bad;
struct disklabel *lp = wd->sc_dk.dk_label;
struct wd_link *d_link = wd->d_link;
int i = 0;
WDDEBUG_PRINT(("bad144intern\n"));
for (; i < 126; i++) {
if (bt->bt_bad[i].bt_cyl == 0xffff)
break;
d_link->sc_badsect[i] =
bt->bt_bad[i].bt_cyl * lp->d_secpercyl +
(bt->bt_bad[i].bt_trksec >> 8) * lp->d_nsectors +
(bt->bt_bad[i].bt_trksec & 0xff);
}
for (; i < 127; i++)
d_link->sc_badsect[i] = -1;
}
void
wderror(d_link, bp, msg)
struct wd_link *d_link;
struct buf *bp;
char *msg;
{
struct wd_softc *wd = (struct wd_softc *)d_link->wd_softc;
if (bp) {
diskerr(bp, "wd", msg, LOG_PRINTF, bp->b_bcount,
wd->sc_dk.dk_label);
printf("\n");
} else
printf("%s: %s\n", wd->sc_dev.dv_xname, msg);
}
void
wddone(d_link, bp)
struct wd_link *d_link;
struct buf *bp;
{
struct wd_softc *wd = (void *)d_link->wd_softc;
disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid));
#if NRND > 0
rnd_add_uint32(&wd->rnd_source, bp->b_blkno);
#endif
}