wd.c revision 1.162.2.2 1 /* $NetBSD: wd.c,v 1.162.2.2 1997/10/14 10:24:04 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 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