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