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