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