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