Home | History | Annotate | Line # | Download | only in ata
wd.c revision 1.167
      1 /*	$NetBSD: wd.c,v 1.167 1998/01/12 09:43:49 thorpej Exp $ */
      2 
      3 /*
      4  * Copyright (c) 1994, 1995 Charles M. Hannum.  All rights reserved.
      5  *
      6  * DMA and multi-sector PIO handling are derived from code contributed by
      7  * Onno van der Linden.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  * 3. All advertising materials mentioning features or use of this software
     18  *    must display the following acknowledgement:
     19  *	This product includes software developed by Charles M. Hannum.
     20  * 4. The name of the author may not be used to endorse or promote products
     21  *    derived from this software without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     28  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     33  */
     34 
     35 #include "rnd.h"
     36 
     37 #include <sys/param.h>
     38 #include <sys/systm.h>
     39 #include <sys/kernel.h>
     40 #include <sys/conf.h>
     41 #include <sys/file.h>
     42 #include <sys/stat.h>
     43 #include <sys/ioctl.h>
     44 #include <sys/buf.h>
     45 #include <sys/uio.h>
     46 #include <sys/malloc.h>
     47 #include <sys/device.h>
     48 #include <sys/disklabel.h>
     49 #include <sys/disk.h>
     50 #include <sys/syslog.h>
     51 #include <sys/proc.h>
     52 #if NRND > 0
     53 #include <sys/rnd.h>
     54 #endif
     55 
     56 #include <vm/vm.h>
     57 
     58 #include <machine/cpu.h>
     59 #include <machine/intr.h>
     60 #include <machine/pio.h>
     61 
     62 #include <dev/isa/isavar.h>
     63 #include <dev/isa/wdreg.h>
     64 #include <dev/isa/wdlink.h>
     65 #include "locators.h"
     66 
     67 #define	WAITTIME	(4 * hz)	/* time to wait for a completion */
     68 
     69 #define	WDIORETRIES	5		/* number of retries before giving up */
     70 
     71 #define	WDUNIT(dev)			DISKUNIT(dev)
     72 #define	WDPART(dev)			DISKPART(dev)
     73 #define	MAKEWDDEV(maj, unit, part)	MAKEDISKDEV(maj, unit, part)
     74 
     75 #define	WDLABELDEV(dev)	(MAKEWDDEV(major(dev), WDUNIT(dev), RAW_PART))
     76 
     77 #ifdef WDDEBUG
     78 #define WDDEBUG_PRINT(args)		printf args
     79 #else
     80 #define WDDEBUG_PRINT(args)
     81 #endif
     82 
     83 struct wd_softc {
     84 	struct device sc_dev;
     85 	struct disk sc_dk;
     86 	struct wd_link *d_link;
     87 	struct buf sc_q;
     88 #if NRND > 0
     89 	rndsource_element_t	rnd_source;
     90 #endif
     91 };
     92 
     93 int	wdprobe		__P((struct device *, void *, void *));
     94 void	wdattach	__P((struct device *, struct device *, void *));
     95 int	wdprint		__P((void *, char *));
     96 
     97 struct cfattach wd_ca = {
     98 	sizeof(struct wd_softc), wdprobe, wdattach
     99 };
    100 
    101 extern struct cfdriver wd_cd;
    102 
    103 void	wdgetdefaultlabel __P((struct wd_softc *, struct disklabel *));
    104 void	wdgetdisklabel	__P((struct wd_softc *));
    105 int	wd_get_parms	__P((struct wd_softc *));
    106 void	wdstrategy	__P((struct buf *));
    107 void	wdstart		__P((void *));
    108 
    109 struct dkdriver wddkdriver = { wdstrategy };
    110 
    111 /* XXX: these should go elsewhere */
    112 cdev_decl(wd);
    113 bdev_decl(wd);
    114 
    115 void	wdfinish	__P((struct wd_softc *, struct buf *));
    116 int	wdsetctlr	__P((struct wd_link *));
    117 static void bad144intern __P((struct wd_softc *));
    118 int	wdlock		__P((struct wd_link *));
    119 void	wdunlock	__P((struct wd_link *));
    120 
    121 int
    122 wdprobe(parent, match, aux)
    123 	struct device *parent;
    124 	void *match, *aux;
    125 {
    126 	struct cfdata *cf = match;
    127 	struct wd_link *d_link = aux;
    128 	int drive;
    129 
    130 	if (d_link == NULL)
    131 		return 0;
    132 	if (d_link->type != ATA)
    133 		return 0;
    134 
    135 	drive = d_link->drive;
    136 	if (cf->cf_loc[ATACF_DRIVE] != ATACF_DRIVE_DEFAULT &&
    137 		cf->cf_loc[ATACF_DRIVE] != drive)
    138 		return 0;
    139 
    140 	return 1;
    141 }
    142 
    143 void
    144 wdattach(parent, self, aux)
    145 	struct device *parent, *self;
    146 	void *aux;
    147 {
    148 	struct wd_softc *wd = (void *)self;
    149 	struct wd_link *d_link= aux;
    150 	int i, blank;
    151 	char buf[41], c, *p, *q;
    152 
    153 	wd->d_link = d_link;
    154 	d_link->openings = 1;
    155 	d_link->wd_softc = (caddr_t)wd;
    156 
    157 	/*
    158 	 * Initialize and attach the disk structure.
    159 	 */
    160 	wd->sc_dk.dk_driver = &wddkdriver;
    161 	wd->sc_dk.dk_name = wd->sc_dev.dv_xname;
    162 	disk_attach(&wd->sc_dk);
    163 
    164 	d_link->sc_lp = wd->sc_dk.dk_label;
    165 
    166 	wdc_get_parms((struct wdc_softc *)d_link->wdc_softc, d_link);
    167 	for (blank = 0, p = d_link->sc_params.wdp_model, q = buf, i = 0;
    168 	     i < sizeof(d_link->sc_params.wdp_model); i++) {
    169 		c = *p++;
    170 		if (c == '\0')
    171 			break;
    172 		if (c != ' ') {
    173 			if (blank) {
    174 				*q++ = ' ';
    175 				blank = 0;
    176 			}
    177 			*q++ = c;
    178 		} else
    179 			blank = 1;
    180 	}
    181 	*q++ = '\0';
    182 
    183 	printf(": <%s>\n%s: %dMB, %d cyl, %d head, %d sec, %d bytes/sec\n",
    184 	    buf, self->dv_xname,
    185 	    d_link->sc_params.wdp_cylinders *
    186 	      (d_link->sc_params.wdp_heads * d_link->sc_params.wdp_sectors) /
    187 		  (1048576 / DEV_BSIZE),
    188 	    d_link->sc_params.wdp_cylinders,
    189 	    d_link->sc_params.wdp_heads,
    190 	    d_link->sc_params.wdp_sectors,
    191 	    DEV_BSIZE);
    192 
    193 	if ((d_link->sc_params.wdp_capabilities & WD_CAP_DMA) != 0 &&
    194 	    d_link->sc_mode == WDM_DMA) {
    195 		d_link->sc_mode = WDM_DMA;
    196 	} else if (d_link->sc_params.wdp_maxmulti > 1) {
    197 		d_link->sc_mode = WDM_PIOMULTI;
    198 		d_link->sc_multiple = min(d_link->sc_params.wdp_maxmulti, 16);
    199 	} else {
    200 		d_link->sc_mode = WDM_PIOSINGLE;
    201 		d_link->sc_multiple = 1;
    202 	}
    203 
    204 	printf("%s: using", wd->sc_dev.dv_xname);
    205 	if (d_link->sc_mode == WDM_DMA)
    206 		printf(" dma transfers,");
    207 	else
    208 		printf(" %d-sector %d-bit pio transfers,",
    209 		    d_link->sc_multiple,
    210 		    (d_link->sc_flags & WDF_32BIT) == 0 ? 16 : 32);
    211 	if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0)
    212 		printf(" lba addressing\n");
    213 	else
    214 		printf(" chs addressing\n");
    215 
    216 #if NRND > 0
    217 	rnd_attach_source(&wd->rnd_source, wd->sc_dev.dv_xname, RND_TYPE_DISK);
    218 #endif
    219 }
    220 
    221 /*
    222  * Read/write routine for a buffer.  Validates the arguments and schedules the
    223  * transfer.  Does not wait for the transfer to complete.
    224  */
    225 void
    226 wdstrategy(bp)
    227 	struct buf *bp;
    228 {
    229 	struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(bp->b_dev)];
    230 	struct wd_link *d_link= wd->d_link;
    231 	int s;
    232 
    233 	/* Valid request?  */
    234 	if (bp->b_blkno < 0 ||
    235 	    (bp->b_bcount % wd->sc_dk.dk_label->d_secsize) != 0 ||
    236 	    (bp->b_bcount / wd->sc_dk.dk_label->d_secsize) >= (1 << NBBY)) {
    237 		bp->b_error = EINVAL;
    238 		goto bad;
    239 	}
    240 
    241 	/* If device invalidated (e.g. media change, door open), error. */
    242 	if ((d_link->sc_flags & WDF_LOADED) == 0) {
    243 		bp->b_error = EIO;
    244 		goto bad;
    245 	}
    246 
    247 	/* If it's a null transfer, return immediately. */
    248 	if (bp->b_bcount == 0)
    249 		goto done;
    250 
    251 	/*
    252 	 * Do bounds checking, adjust transfer. if error, process.
    253 	 * If end of partition, just return.
    254 	 */
    255 	if (WDPART(bp->b_dev) != RAW_PART &&
    256 	    bounds_check_with_label(bp, wd->sc_dk.dk_label,
    257 	    (d_link->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0)
    258 		goto done;
    259 
    260 	/* Queue transfer on drive, activate drive and controller if idle. */
    261 	s = splbio();
    262 	disksort(&wd->sc_q, bp);
    263 	wdstart(wd);
    264 	splx(s);
    265 	return;
    266 
    267 bad:
    268 	bp->b_flags |= B_ERROR;
    269 done:
    270 	/* Toss transfer; we're done early. */
    271 	bp->b_resid = bp->b_bcount;
    272 	biodone(bp);
    273 }
    274 
    275 /*
    276  * Queue a drive for I/O.
    277  */
    278 void
    279 wdstart(arg)
    280 	void *arg;
    281 {
    282 	struct wd_softc *wd = arg;
    283 	struct buf *dp, *bp=0;
    284 	struct wd_link *d_link = wd->d_link;
    285 	struct wdc_xfer *xfer;
    286 	u_long p_offset;
    287 
    288 	while (d_link->openings > 0) {
    289 
    290 		/* Is there a buf for us ? */
    291 		dp = &wd->sc_q;
    292 		if ((bp = dp->b_actf) == NULL)  /* yes, an assign */
    293                  	return;
    294 		dp->b_actf = bp->b_actf;
    295 
    296 		/*
    297 		 * Make the command. First lock the device
    298 		 */
    299 		d_link->openings--;
    300 		if (WDPART(bp->b_dev) != RAW_PART)
    301 			p_offset =
    302 		  wd->sc_dk.dk_label->d_partitions[WDPART(bp->b_dev)].p_offset;
    303 		else
    304 			p_offset = 0;
    305 
    306 		xfer = wdc_get_xfer(0);
    307 		if (xfer == NULL)
    308 			panic("wdc_xfer");
    309 
    310 		xfer->d_link = d_link;
    311 		xfer->c_bp = bp;
    312 		xfer->c_p_offset = p_offset;
    313 		xfer->databuf = bp->b_data;
    314 		xfer->c_bcount = bp->b_bcount;
    315 		xfer->c_flags |= bp->b_flags & (B_READ|B_WRITE);
    316 		xfer->c_blkno = bp->b_blkno;
    317 
    318 		/* Instrumentation. */
    319 		disk_busy(&wd->sc_dk);
    320 		wdc_exec_xfer((struct wdc_softc *)wd->d_link->wdc_softc,
    321 			wd->d_link, xfer);
    322 	}
    323 }
    324 
    325 int
    326 wdread(dev, uio, flags)
    327 	dev_t dev;
    328 	struct uio *uio;
    329 	int flags;
    330 {
    331 
    332 	WDDEBUG_PRINT(("wdread\n"));
    333 	return (physio(wdstrategy, NULL, dev, B_READ, minphys, uio));
    334 }
    335 
    336 int
    337 wdwrite(dev, uio, flags)
    338 	dev_t dev;
    339 	struct uio *uio;
    340 	int flags;
    341 {
    342 
    343 	WDDEBUG_PRINT(("wdwrite\n"));
    344 	return (physio(wdstrategy, NULL, dev, B_WRITE, minphys, uio));
    345 }
    346 
    347 /*
    348  * Wait interruptibly for an exclusive lock.
    349  *
    350  * XXX
    351  * Several drivers do this; it should be abstracted and made MP-safe.
    352  */
    353 int
    354 wdlock(d_link)
    355 	struct wd_link *d_link;
    356 {
    357 	int error;
    358 	int s;
    359 
    360 	WDDEBUG_PRINT(("wdlock\n"));
    361 
    362 	s = splbio();
    363 
    364 	while ((d_link->sc_flags & WDF_LOCKED) != 0) {
    365 		d_link->sc_flags |= WDF_WANTED;
    366 		if ((error = tsleep(d_link, PRIBIO | PCATCH,
    367 		    "wdlck", 0)) != 0) {
    368 			splx(s);
    369 			return error;
    370 		}
    371 	}
    372 	d_link->sc_flags |= WDF_LOCKED;
    373 	splx(s);
    374 	return 0;
    375 }
    376 
    377 /*
    378  * Unlock and wake up any waiters.
    379  */
    380 void
    381 wdunlock(d_link)
    382 	struct wd_link *d_link;
    383 {
    384 
    385 	WDDEBUG_PRINT(("wdunlock"));
    386 
    387 	d_link->sc_flags &= ~WDF_LOCKED;
    388 	if ((d_link->sc_flags & WDF_WANTED) != 0) {
    389 		d_link->sc_flags &= ~WDF_WANTED;
    390 		wakeup(d_link);
    391 	}
    392 }
    393 
    394 int
    395 wdopen(dev, flag, fmt, p)
    396 	dev_t dev;
    397 	int flag, fmt;
    398 	struct proc *p;
    399 {
    400 	struct wd_softc *wd;
    401 	struct wd_link *d_link;
    402 	int unit, part;
    403 	int error;
    404 
    405 	WDDEBUG_PRINT(("wdopen\n"));
    406 
    407 	unit = WDUNIT(dev);
    408 	if (unit >= wd_cd.cd_ndevs)
    409 		return ENXIO;
    410 	wd = wd_cd.cd_devs[unit];
    411 	if (wd == NULL)
    412 		return ENXIO;
    413 
    414 	d_link = wd->d_link;
    415 	if ((error = wdlock(d_link)) != 0)
    416 		return error;
    417 
    418 	if (wd->sc_dk.dk_openmask != 0) {
    419 		/*
    420 		 * If any partition is open, but the disk has been invalidated,
    421 		 * disallow further opens.
    422 		 */
    423 		if ((d_link->sc_flags & WDF_LOADED) == 0) {
    424 			error = EIO;
    425 			goto bad3;
    426 		}
    427 	} else {
    428 		if ((d_link->sc_flags & WDF_LOADED) == 0) {
    429 			d_link->sc_flags |= WDF_LOADED;
    430 
    431 			/* Load the physical device parameters. */
    432 			if (wdc_get_parms((struct wdc_softc *)d_link->wdc_softc,
    433 				d_link) != 0) {
    434 				error = ENXIO;
    435 				goto bad2;
    436 			}
    437 
    438 			/* Load the partition info if not already loaded. */
    439 			wdgetdisklabel(wd);
    440 		}
    441 	}
    442 
    443 	part = WDPART(dev);
    444 
    445 	/* Check that the partition exists. */
    446 	if (part != RAW_PART &&
    447 	    (part >= wd->sc_dk.dk_label->d_npartitions ||
    448 	     wd->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
    449 		error = ENXIO;
    450 		goto bad;
    451 	}
    452 
    453 	/* Insure only one open at a time. */
    454 	switch (fmt) {
    455 	case S_IFCHR:
    456 		wd->sc_dk.dk_copenmask |= (1 << part);
    457 		break;
    458 	case S_IFBLK:
    459 		wd->sc_dk.dk_bopenmask |= (1 << part);
    460 		break;
    461 	}
    462 	wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask;
    463 
    464 	wdunlock(d_link);
    465 	return 0;
    466 
    467 bad2:
    468 	d_link->sc_flags &= ~WDF_LOADED;
    469 
    470 bad:
    471 	if (wd->sc_dk.dk_openmask == 0) {
    472 	}
    473 
    474 bad3:
    475 	wdunlock(d_link);
    476 	return error;
    477 }
    478 
    479 int
    480 wdclose(dev, flag, fmt, p)
    481 	dev_t dev;
    482 	int flag, fmt;
    483 	struct proc *p;
    484 {
    485 	struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)];
    486 	int part = WDPART(dev);
    487 	int error;
    488 
    489 	if ((error = wdlock(wd->d_link)) != 0)
    490 		return error;
    491 
    492 	switch (fmt) {
    493 	case S_IFCHR:
    494 		wd->sc_dk.dk_copenmask &= ~(1 << part);
    495 		break;
    496 	case S_IFBLK:
    497 		wd->sc_dk.dk_bopenmask &= ~(1 << part);
    498 		break;
    499 	}
    500 	wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask;
    501 
    502 	if (wd->sc_dk.dk_openmask == 0) {
    503 		/* XXXX Must wait for I/O to complete! */
    504 	}
    505 
    506 	wdunlock(wd->d_link);
    507 	return 0;
    508 }
    509 
    510 void
    511 wdgetdefaultlabel(wd, lp)
    512 	struct wd_softc *wd;
    513 	struct disklabel *lp;
    514 {
    515 	struct wd_link *d_link = wd->d_link;
    516 
    517 	bzero(lp, sizeof(struct disklabel));
    518 
    519 	lp->d_secsize = DEV_BSIZE;
    520 	lp->d_ntracks = d_link->sc_params.wdp_heads;
    521 	lp->d_nsectors = d_link->sc_params.wdp_sectors;
    522 	lp->d_ncylinders = d_link->sc_params.wdp_cylinders;
    523 	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
    524 
    525 #if 0
    526 	strncpy(lp->d_typename, "ST506 disk", 16);
    527 	lp->d_type = DTYPE_ST506;
    528 #endif
    529 	strncpy(lp->d_packname, d_link->sc_params.wdp_model, 16);
    530 	lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
    531 	lp->d_rpm = 3600;
    532 	lp->d_interleave = 1;
    533 	lp->d_flags = 0;
    534 
    535 	lp->d_partitions[RAW_PART].p_offset = 0;
    536 	lp->d_partitions[RAW_PART].p_size =
    537 	    lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
    538 	lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
    539 	lp->d_npartitions = RAW_PART + 1;
    540 
    541 	lp->d_magic = DISKMAGIC;
    542 	lp->d_magic2 = DISKMAGIC;
    543 	lp->d_checksum = dkcksum(lp);
    544 }
    545 
    546 /*
    547  * Fabricate a default disk label, and try to read the correct one.
    548  */
    549 void
    550 wdgetdisklabel(wd)
    551 	struct wd_softc *wd;
    552 {
    553 	struct disklabel *lp = wd->sc_dk.dk_label;
    554 	struct wd_link *d_link = wd->d_link;
    555 	char *errstring;
    556 
    557 	WDDEBUG_PRINT(("wdgetdisklabel\n"));
    558 
    559 	bzero(wd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel));
    560 
    561 	wdgetdefaultlabel(wd, lp);
    562 
    563 	d_link->sc_badsect[0] = -1;
    564 
    565 	if (d_link->sc_state > RECAL)
    566 		d_link->sc_state = RECAL;
    567 	errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART),
    568 	    wdstrategy, lp, wd->sc_dk.dk_cpulabel);
    569 	if (errstring) {
    570 		/*
    571 		 * This probably happened because the drive's default
    572 		 * geometry doesn't match the DOS geometry.  We
    573 		 * assume the DOS geometry is now in the label and try
    574 		 * again.  XXX This is a kluge.
    575 		 */
    576 		if (d_link->sc_state > GEOMETRY)
    577 			d_link->sc_state = GEOMETRY;
    578 		errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART),
    579 		    wdstrategy, lp, wd->sc_dk.dk_cpulabel);
    580 	}
    581 	if (errstring) {
    582 		printf("%s: %s\n", wd->sc_dev.dv_xname, errstring);
    583 		return;
    584 	}
    585 
    586 	if (d_link->sc_state > GEOMETRY)
    587 		d_link->sc_state = GEOMETRY;
    588 	if ((lp->d_flags & D_BADSECT) != 0)
    589 		bad144intern(wd);
    590 }
    591 
    592 
    593 /*
    594  * Tell the drive what geometry to use.
    595  */
    596 int
    597 wdsetctlr(d_link)
    598 	struct wd_link *d_link;
    599 {
    600 	struct wd_softc *wd=(struct wd_softc *)d_link->wd_softc;
    601 
    602 	WDDEBUG_PRINT(("wd(%d,%d) C%dH%dS%d\n", wd->sc_dev.dv_unit,
    603 	    d_link->drive, wd->sc_dk.dk_label->d_ncylinders,
    604 	    wd->sc_dk.dk_label->d_ntracks, wd->sc_dk.dk_label->d_nsectors));
    605 
    606 	if (wdccommand((struct wdc_softc *)d_link->wdc_softc,
    607 		d_link, WDCC_IDP, d_link->drive,
    608 	    wd->sc_dk.dk_label->d_ncylinders,
    609 	    wd->sc_dk.dk_label->d_ntracks - 1, 0,
    610 	    wd->sc_dk.dk_label->d_nsectors) != 0) {
    611 		wderror(d_link, NULL, "wdsetctlr: geometry upload failed");
    612 		return -1;
    613 	}
    614 
    615 	return 0;
    616 }
    617 
    618 int
    619 wdioctl(dev, xfer, addr, flag, p)
    620 	dev_t dev;
    621 	u_long xfer;
    622 	caddr_t addr;
    623 	int flag;
    624 	struct proc *p;
    625 {
    626 	struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)];
    627 	struct wd_link *d_link = wd->d_link;
    628 	int error;
    629 
    630 	WDDEBUG_PRINT(("wdioctl\n"));
    631 
    632 	if ((d_link->sc_flags & WDF_LOADED) == 0)
    633 		return EIO;
    634 
    635 	switch (xfer) {
    636 	case DIOCSBAD:
    637 		if ((flag & FWRITE) == 0)
    638 			return EBADF;
    639 		wd->sc_dk.dk_cpulabel->bad = *(struct dkbad *)addr;
    640 		wd->sc_dk.dk_label->d_flags |= D_BADSECT;
    641 		bad144intern(wd);
    642 		return 0;
    643 
    644 	case DIOCGDINFO:
    645 		*(struct disklabel *)addr = *(wd->sc_dk.dk_label);
    646 		return 0;
    647 
    648 	case DIOCGPART:
    649 		((struct partinfo *)addr)->disklab = wd->sc_dk.dk_label;
    650 		((struct partinfo *)addr)->part =
    651 		    &wd->sc_dk.dk_label->d_partitions[WDPART(dev)];
    652 		return 0;
    653 
    654 	case DIOCWDINFO:
    655 	case DIOCSDINFO:
    656 		if ((flag & FWRITE) == 0)
    657 			return EBADF;
    658 
    659 		if ((error = wdlock(wd->d_link)) != 0)
    660 			return error;
    661 		d_link->sc_flags |= WDF_LABELLING;
    662 
    663 		error = setdisklabel(wd->sc_dk.dk_label,
    664 		    (struct disklabel *)addr, /*wd->sc_dk.dk_openmask : */0,
    665 		    wd->sc_dk.dk_cpulabel);
    666 		if (error == 0) {
    667 			if (d_link->sc_state > GEOMETRY)
    668 				d_link->sc_state = GEOMETRY;
    669 			if (xfer == DIOCWDINFO)
    670 				error = writedisklabel(WDLABELDEV(dev),
    671 				    wdstrategy, wd->sc_dk.dk_label,
    672 				    wd->sc_dk.dk_cpulabel);
    673 		}
    674 
    675 		d_link->sc_flags &= ~WDF_LABELLING;
    676 		wdunlock(d_link);
    677 		return error;
    678 
    679 	case DIOCWLABEL:
    680 		if ((flag & FWRITE) == 0)
    681 			return EBADF;
    682 		if (*(int *)addr)
    683 			d_link->sc_flags |= WDF_WLABEL;
    684 		else
    685 			d_link->sc_flags &= ~WDF_WLABEL;
    686 		return 0;
    687 
    688 	case DIOCGDEFLABEL:
    689 		wdgetdefaultlabel(wd, (struct disklabel *)addr);
    690 		return 0;
    691 
    692 #ifdef notyet
    693 	case DIOCWFORMAT:
    694 		if ((flag & FWRITE) == 0)
    695 			return EBADF;
    696 	{
    697 		register struct format_op *fop;
    698 		struct iovec aiov;
    699 		struct uio auio;
    700 
    701 		fop = (struct format_op *)addr;
    702 		aiov.iov_base = fop->df_buf;
    703 		aiov.iov_len = fop->df_count;
    704 		auio.uio_iov = &aiov;
    705 		auio.uio_iovcnt = 1;
    706 		auio.uio_resid = fop->df_count;
    707 		auio.uio_segflg = 0;
    708 		auio.uio_offset =
    709 		    fop->df_startblk * wd->sc_dk.dk_label->d_secsize;
    710 		auio.uio_procp = p;
    711 		error = physio(wdformat, NULL, dev, B_WRITE, minphys,
    712 		    &auio);
    713 		fop->df_count -= auio.uio_resid;
    714 		fop->df_reg[0] = wdc->sc_status;
    715 		fop->df_reg[1] = wdc->sc_error;
    716 		return error;
    717 	}
    718 #endif
    719 
    720 	default:
    721 		return ENOTTY;
    722 	}
    723 
    724 #ifdef DIAGNOSTIC
    725 	panic("wdioctl: impossible");
    726 #endif
    727 }
    728 
    729 #ifdef B_FORMAT
    730 int
    731 wdformat(struct buf *bp)
    732 {
    733 
    734 	bp->b_flags |= B_FORMAT;
    735 	return wdstrategy(bp);
    736 }
    737 #endif
    738 
    739 int
    740 wdsize(dev)
    741 	dev_t dev;
    742 {
    743 	struct wd_softc *wd;
    744 	int part, unit, omask;
    745 	int size;
    746 
    747 	WDDEBUG_PRINT(("wdsize\n"));
    748 
    749 	unit = WDUNIT(dev);
    750 	if (unit >= wd_cd.cd_ndevs)
    751 		return (-1);
    752 	wd = wd_cd.cd_devs[unit];
    753 	if (wd == NULL)
    754 		return (-1);
    755 
    756 	part = WDPART(dev);
    757 	omask = wd->sc_dk.dk_openmask & (1 << part);
    758 
    759 	if (omask == 0 && wdopen(dev, 0, S_IFBLK, NULL) != 0)
    760 		return (-1);
    761 	if (wd->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP)
    762 		size = -1;
    763 	else
    764 		size = wd->sc_dk.dk_label->d_partitions[part].p_size *
    765 			(wd->sc_dk.dk_label->d_secsize / DEV_BSIZE);
    766 	if (omask == 0 && wdclose(dev, 0, S_IFBLK, NULL) != 0)
    767 		return (-1);
    768 	return (size);
    769 }
    770 
    771 
    772 #ifndef __BDEVSW_DUMP_OLD_TYPE
    773 /* #define WD_DUMP_NOT_TRUSTED if you just want to watch */
    774 static int wddoingadump;
    775 static int wddumprecalibrated;
    776 
    777 /*
    778  * Dump core after a system crash.
    779  *
    780  * XXX:  This needs work!  Currently, it's a major hack: the
    781  * use of wdc_softc is very bad and should go away.
    782  */
    783 int
    784 wddump(dev, blkno, va, size)
    785         dev_t dev;
    786         daddr_t blkno;
    787         caddr_t va;
    788         size_t size;
    789 {
    790 	struct wd_softc *wd;	/* disk unit to do the I/O */
    791 	struct wdc_softc *wdc;	/* disk controller to do the I/O */
    792 	struct disklabel *lp;	/* disk's disklabel */
    793 	struct wd_link *d_link;
    794 	int	unit, part;
    795 	int	nblks;		/* total number of sectors left to write */
    796 
    797 	/* Check if recursive dump; if so, punt. */
    798 	if (wddoingadump)
    799 		return EFAULT;
    800 	wddoingadump = 1;
    801 
    802 	unit = WDUNIT(dev);
    803 	if (unit >= wd_cd.cd_ndevs)
    804 		return ENXIO;
    805 	wd = wd_cd.cd_devs[unit];
    806 	if (wd == (struct wd_softc *)0)
    807 		return ENXIO;
    808 	d_link = wd->d_link;
    809 
    810 	part = WDPART(dev);
    811 
    812 	/* Make sure it was initialized. */
    813 	if (d_link->sc_state < READY)
    814 		return ENXIO;
    815 
    816 	wdc = (void *)wd->sc_dev.dv_parent;
    817 
    818         /* Convert to disk sectors.  Request must be a multiple of size. */
    819 	lp = wd->sc_dk.dk_label;
    820 	if ((size % lp->d_secsize) != 0)
    821 		return EFAULT;
    822 	nblks = size / lp->d_secsize;
    823 	blkno = blkno / (lp->d_secsize / DEV_BSIZE);
    824 
    825 	/* Check transfer bounds against partition size. */
    826 	if ((blkno < 0) || ((blkno + nblks) > lp->d_partitions[part].p_size))
    827 		return EINVAL;
    828 
    829 	/* Offset block number to start of partition. */
    830 	blkno += lp->d_partitions[part].p_offset;
    831 
    832 	/* Recalibrate, if first dump transfer. */
    833 	if (wddumprecalibrated == 0) {
    834 		wddumprecalibrated = 1;
    835 		if (wdccommandshort(wdc, d_link->drive, WDCC_RECAL) != 0 ||
    836 		    wait_for_ready(wdc) != 0 || wdsetctlr(d_link) != 0 ||
    837 		    wait_for_ready(wdc) != 0) {
    838 			wderror(d_link, NULL, "wddump: recal failed");
    839 			return EIO;
    840 		}
    841 	}
    842 
    843 	while (nblks > 0) {
    844 		daddr_t xlt_blkno = blkno;
    845 		long cylin, head, sector;
    846 
    847 		if ((lp->d_flags & D_BADSECT) != 0) {
    848 			long blkdiff;
    849 			int i;
    850 
    851 			for (i = 0; (blkdiff = d_link->sc_badsect[i]) != -1; i++) {
    852 				blkdiff -= xlt_blkno;
    853 				if (blkdiff < 0)
    854 					continue;
    855 				if (blkdiff == 0) {
    856 					/* Replace current block of transfer. */
    857 					xlt_blkno = lp->d_secperunit -
    858 					    lp->d_nsectors - i - 1;
    859 				}
    860 				break;
    861 			}
    862 			/* Tranfer is okay now. */
    863 		}
    864 
    865 		if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) {
    866 			sector = (xlt_blkno >> 0) & 0xff;
    867 			cylin = (xlt_blkno >> 8) & 0xffff;
    868 			head = (xlt_blkno >> 24) & 0xf;
    869 			head |= WDSD_LBA;
    870 		} else {
    871 			sector = xlt_blkno % lp->d_nsectors;
    872 			sector++;	/* Sectors begin with 1, not 0. */
    873 			xlt_blkno /= lp->d_nsectors;
    874 			head = xlt_blkno % lp->d_ntracks;
    875 			xlt_blkno /= lp->d_ntracks;
    876 			cylin = xlt_blkno;
    877 			head |= WDSD_CHS;
    878 		}
    879 
    880 #ifndef WD_DUMP_NOT_TRUSTED
    881 		if (wdccommand((struct wdc_softc *)d_link->wdc_softc, d_link,
    882 			WDCC_WRITE, d_link->drive, cylin, head, sector, 1) != 0 ||
    883 		    wait_for_drq(wdc) != 0) {
    884 			wderror(d_link, NULL, "wddump: write failed");
    885 			return EIO;
    886 		}
    887 
    888 		/* XXX XXX XXX */
    889 		outsw(wdc->sc_iobase + wd_data, va, lp->d_secsize >> 1);
    890 
    891 		/* Check data request (should be done). */
    892 		if (wait_for_ready(wdc) != 0) {
    893 			wderror(d_link, NULL,
    894 			    "wddump: timeout waiting for ready");
    895 			return EIO;
    896 		}
    897 #else	/* WD_DUMP_NOT_TRUSTED */
    898 		/* Let's just talk about this first... */
    899 		printf("wd%d: dump addr 0x%x, cylin %d, head %d, sector %d\n",
    900 		    unit, va, cylin, head, sector);
    901 		delay(500 * 1000);	/* half a second */
    902 #endif
    903 
    904 		/* update block count */
    905 		nblks -= 1;
    906 		blkno += 1;
    907 		va += lp->d_secsize;
    908 	}
    909 
    910 	wddoingadump = 0;
    911 	return 0;
    912 }
    913 #else /* __BDEVSW_DUMP_NEW_TYPE */
    914 
    915 
    916 int
    917 wddump(dev, blkno, va, size)
    918         dev_t dev;
    919         daddr_t blkno;
    920         caddr_t va;
    921         size_t size;
    922 {
    923 
    924 	/* Not implemented. */
    925 	return ENXIO;
    926 }
    927 #endif /* __BDEVSW_DUMP_NEW_TYPE */
    928 
    929 /*
    930  * Internalize the bad sector table.
    931  */
    932 void
    933 bad144intern(wd)
    934 	struct wd_softc *wd;
    935 {
    936 	struct dkbad *bt = &wd->sc_dk.dk_cpulabel->bad;
    937 	struct disklabel *lp = wd->sc_dk.dk_label;
    938 	struct wd_link *d_link = wd->d_link;
    939 	int i = 0;
    940 
    941 	WDDEBUG_PRINT(("bad144intern\n"));
    942 
    943 	for (; i < 126; i++) {
    944 		if (bt->bt_bad[i].bt_cyl == 0xffff)
    945 			break;
    946 		d_link->sc_badsect[i] =
    947 		    bt->bt_bad[i].bt_cyl * lp->d_secpercyl +
    948 		    (bt->bt_bad[i].bt_trksec >> 8) * lp->d_nsectors +
    949 		    (bt->bt_bad[i].bt_trksec & 0xff);
    950 	}
    951 	for (; i < 127; i++)
    952 		d_link->sc_badsect[i] = -1;
    953 }
    954 
    955 void
    956 wderror(d_link, bp, msg)
    957 	struct wd_link *d_link;
    958 	struct buf *bp;
    959 	char *msg;
    960 {
    961 	struct wd_softc *wd = (struct wd_softc *)d_link->wd_softc;
    962 
    963 	if (bp) {
    964 		diskerr(bp, "wd", msg, LOG_PRINTF, bp->b_bcount,
    965 		    wd->sc_dk.dk_label);
    966 		printf("\n");
    967 	} else
    968 		printf("%s: %s\n", wd->sc_dev.dv_xname, msg);
    969 }
    970 
    971 void
    972 wddone(d_link, bp)
    973 	struct wd_link *d_link;
    974 	struct buf *bp;
    975 {
    976 	struct wd_softc *wd = (void *)d_link->wd_softc;
    977 
    978 	disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid));
    979 #if NRND > 0
    980 	rnd_add_uint32(&wd->rnd_source, bp->b_blkno);
    981 #endif
    982 }
    983