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