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