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