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