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