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