wd.c revision 1.115 1 /* $NetBSD: wd.c,v 1.115 1994/11/22 10:20:16 mycroft Exp $ */
2
3 /*
4 * Copyright (c) 1994 Charles Hannum. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Charles Hannum.
17 * 4. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * Copyright (c) 1990 The Regents of the University of California.
21 * All rights reserved.
22 *
23 * This code is derived from software contributed to Berkeley by
24 * William Jolitz.
25 *
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
28 * are met:
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in the
33 * documentation and/or other materials provided with the distribution.
34 * 3. All advertising materials mentioning features or use of this software
35 * must display the following acknowledgement:
36 * This product includes software developed by the University of
37 * California, Berkeley and its contributors.
38 * 4. Neither the name of the University nor the names of its contributors
39 * may be used to endorse or promote products derived from this software
40 * without specific prior written permission.
41 *
42 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
43 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
46 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52 * SUCH DAMAGE.
53 *
54 * @(#)wd.c 7.2 (Berkeley) 5/9/91
55 */
56
57 #define INSTRUMENT /* instrumentation stuff by Brad Parker */
58
59 #include <sys/param.h>
60 #include <sys/dkbad.h>
61 #include <sys/systm.h>
62 #include <sys/kernel.h>
63 #include <sys/conf.h>
64 #include <sys/file.h>
65 #include <sys/stat.h>
66 #include <sys/ioctl.h>
67 #include <sys/buf.h>
68 #include <sys/uio.h>
69 #include <sys/malloc.h>
70 #include <sys/device.h>
71 #include <sys/disklabel.h>
72 #include <sys/disk.h>
73 #include <sys/syslog.h>
74 #ifdef INSTRUMENT
75 #include <sys/dkstat.h>
76 #endif
77
78 #include <vm/vm.h>
79
80 #include <machine/cpu.h>
81 #include <machine/pio.h>
82
83 #include <i386/isa/isavar.h>
84 #include <i386/isa/wdreg.h>
85
86 #define WDCNDELAY 100000 /* delay = 100us; so 10s for a controller state change */
87 #define WDCDELAY 100
88
89 #define WAITTIME (4 * hz) /* time to wait for a completion */
90 #define RECOVERYTIME (hz / 2) /* time to recover from an error */
91
92 #if 0
93 /* If you enable this, it will report any delays more than 100us * N long. */
94 #define WDCNDELAY_DEBUG 10
95 #endif
96
97 #define WDIORETRIES 5 /* number of retries before giving up */
98
99 #define WDUNIT(dev) DISKUNIT(dev)
100 #define WDPART(dev) DISKPART(dev)
101 #define MAKEWDDEV(maj, unit, part) MAKEDISKDEV(maj, unit, part)
102
103 #define WDLABELDEV(dev) (MAKEWDDEV(major(dev), WDUNIT(dev), RAW_PART))
104
105 #define b_cylin b_resid /* cylinder number for doing IO to */
106 /* shares an entry in the buf struct */
107
108 /*
109 * Drive status.
110 */
111 struct wd_softc {
112 struct device sc_dev;
113 struct dkdevice sc_dk;
114
115 long sc_bcount; /* byte count left */
116 short sc_skip; /* blocks already transferred */
117 char sc_drive; /* physical unit number */
118 char sc_state; /* control state */
119 #define RECAL 0 /* recalibrate */
120 #define RECAL_WAIT 1 /* done recalibrating */
121 #define GEOMETRY 2 /* upload geometry */
122 #define GEOMETRY_WAIT 3 /* done uploading geometry */
123 #define MULTIMODE 4 /* set multiple mode */
124 #define MULTIMODE_WAIT 5 /* done setting multiple mode */
125 #define OPEN 6 /* done with open */
126 char sc_mode; /* transfer mode */
127 #define WDM_PIOSINGLE 0 /* single-sector PIO */
128 #define WDM_PIOMULTI 1 /* multi-sector PIO */
129 #define WDM_DMA 2 /* DMA */
130 u_char sc_multiple; /* multiple for WDM_PIOMULTI */
131 u_char sc_flags; /* drive characteistics found */
132 #define WDF_LOCKED 0x01
133 #define WDF_WANTED 0x02
134 #define WDF_LOADED 0x04
135 #define WDF_BSDLABEL 0x08 /* has a BSD disk label */
136 #define WDF_WLABEL 0x10 /* label is writable */
137 #define WDF_32BIT 0x20 /* can do 32-bit transfer */
138 TAILQ_ENTRY(wd_softc) sc_drivechain;
139 struct buf sc_q;
140 struct wdparams sc_params; /* ESDI/IDE drive/controller parameters */
141 long sc_badsect[127]; /* 126 plus trailing -1 marker */
142 };
143
144 struct wdc_softc {
145 struct device sc_dev;
146 struct intrhand sc_ih;
147
148 u_char sc_flags;
149 #define WDCF_ACTIVE 0x01 /* controller is active */
150 #define WDCF_SINGLE 0x02 /* sector at a time mode */
151 #define WDCF_ERROR 0x04 /* processing a disk error */
152 u_char sc_status; /* copy of status register */
153 u_char sc_error; /* copy of error register */
154 int sc_iobase; /* I/O port base */
155 int sc_drq; /* DMA channel */
156 int sc_errors; /* count of errors during current transfer */
157 int sc_nblks; /* number of blocks currently transferring */
158 TAILQ_HEAD(drivehead, wd_softc) sc_drives;
159 };
160
161 int wdcprobe __P((struct device *, void *, void *));
162 void wdcattach __P((struct device *, struct device *, void *));
163
164 struct cfdriver wdccd = {
165 NULL, "wdc", wdcprobe, wdcattach, DV_DULL, sizeof(struct wd_softc)
166 };
167
168 int wdprobe __P((struct device *, void *, void *));
169 void wdattach __P((struct device *, struct device *, void *));
170
171 struct cfdriver wdcd = {
172 NULL, "wd", wdprobe, wdattach, DV_DISK, sizeof(struct wd_softc)
173 };
174
175 void wdgetdisklabel __P((struct wd_softc *));
176 int wd_get_parms __P((struct wd_softc *));
177 void wdstrategy __P((struct buf *));
178 void wdstart __P((struct wd_softc *));
179
180 struct dkdriver wddkdriver = { wdstrategy };
181
182 void wdfinish __P((struct wd_softc *, struct buf *));
183 int wdcintr __P((struct wdc_softc *));
184 static void wdcstart __P((struct wdc_softc *));
185 static int wdcommand __P((struct wd_softc *, int, int, int, int, int));
186 static int wdcommandshort __P((struct wdc_softc *, int, int));
187 static int wdcontrol __P((struct wd_softc *));
188 static int wdsetctlr __P((struct wd_softc *));
189 static void bad144intern __P((struct wd_softc *));
190 static int wdcreset __P((struct wdc_softc *));
191 static void wdcrestart __P((void *arg));
192 static void wdcunwedge __P((struct wdc_softc *));
193 static void wdctimeout __P((void *arg));
194 static void wderror __P((void *, struct buf *, char *));
195 int wdcwait __P((struct wdc_softc *, int));
196 /* ST506 spec says that if READY or SEEKCMPLT go off, then the read or write
197 command is aborted. */
198 #define wait_for_drq(d) wdcwait(d, WDCS_DRDY | WDCS_DSC | WDCS_DRQ)
199 #define wait_for_ready(d) wdcwait(d, WDCS_DRDY | WDCS_DSC)
200 #define wait_for_unbusy(d) wdcwait(d, 0)
201
202 /*
203 * Probe for controller.
204 */
205 int
206 wdcprobe(parent, match, aux)
207 struct device *parent;
208 void *match, *aux;
209 {
210 struct wdc_softc *wdc = match;
211 struct isa_attach_args *ia = aux;
212 int iobase;
213
214 wdc->sc_iobase = iobase = ia->ia_iobase;
215
216 /* Check if we have registers that work. */
217 outb(iobase+wd_error, 0x5a); /* Error register not writable. */
218 outb(iobase+wd_cyl_lo, 0xa5); /* But all of cyllo are implemented. */
219 if (inb(iobase+wd_error) == 0x5a || inb(iobase+wd_cyl_lo) != 0xa5)
220 return 0;
221
222 if (wdcreset(wdc) != 0) {
223 delay(500000);
224 if (wdcreset(wdc) != 0)
225 return 0;
226 }
227
228 outb(iobase+wd_sdh, WDSD_IBM | 0);
229
230 /* Wait for controller to become ready. */
231 if (wait_for_unbusy(wdc) < 0)
232 return 0;
233
234 /* Send command. */
235 outb(iobase+wd_command, WDCC_DIAGNOSE);
236
237 /* Wait for command to complete. */
238 if (wait_for_unbusy(wdc) != 0)
239 return 0;
240
241 ia->ia_iosize = 8;
242 ia->ia_msize = 0;
243 return 1;
244 }
245
246 struct wdc_attach_args {
247 int wa_drive;
248 };
249
250 int
251 wdprint(aux, wdc)
252 void *aux;
253 char *wdc;
254 {
255 struct wdc_attach_args *wa = aux;
256
257 if (!wdc)
258 printf(" drive %d", wa->wa_drive);
259 return QUIET;
260 }
261
262 void
263 wdcattach(parent, self, aux)
264 struct device *parent, *self;
265 void *aux;
266 {
267 struct wdc_softc *wdc = (void *)self;
268 struct isa_attach_args *ia = aux;
269 struct wdc_attach_args wa;
270
271 TAILQ_INIT(&wdc->sc_drives);
272 wdc->sc_drq = ia->ia_drq;
273
274 printf("\n");
275
276 wdc->sc_ih.ih_fun = wdcintr;
277 wdc->sc_ih.ih_arg = wdc;
278 wdc->sc_ih.ih_level = IPL_BIO;
279 intr_establish(ia->ia_irq, &wdc->sc_ih);
280
281 for (wa.wa_drive = 0; wa.wa_drive < 2; wa.wa_drive++)
282 (void)config_found(self, (void *)&wa, wdprint);
283 }
284
285 int
286 wdprobe(parent, match, aux)
287 struct device *parent;
288 void *match, *aux;
289 {
290 struct wdc_softc *wdc = (void *)parent;
291 struct cfdata *cf = match;
292 struct wdc_attach_args *wa = aux;
293 int drive = wa->wa_drive;
294
295 if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != drive)
296 return 0;
297
298 if (wdcommandshort(wdc, drive, WDCC_RECAL) != 0 ||
299 wait_for_ready(wdc) != 0)
300 return 0;
301
302 return 1;
303 }
304
305 void
306 wdattach(parent, self, aux)
307 struct device *parent, *self;
308 void *aux;
309 {
310 struct wd_softc *wd = (void *)self;
311 struct wdc_softc *wdc = (void *)parent;
312 struct wdc_attach_args *wa = aux;
313 int i, blank;
314
315 wd->sc_drive = wa->wa_drive;
316
317 wd_get_parms(wd);
318 printf(": %dMB, %d cyl, %d head, %d sec, %d bytes/sec <",
319 wd->sc_params.wdp_cylinders *
320 (wd->sc_params.wdp_heads * wd->sc_params.wdp_sectors) /
321 (1048576 / DEV_BSIZE),
322 wd->sc_params.wdp_cylinders,
323 wd->sc_params.wdp_heads,
324 wd->sc_params.wdp_sectors,
325 DEV_BSIZE);
326 for (i = blank = 0; i < sizeof(wd->sc_params.wdp_model); i++) {
327 char c = wd->sc_params.wdp_model[i];
328 if (c == '\0')
329 break;
330 if (c != ' ') {
331 if (blank)
332 printf(" %c", c);
333 else
334 printf("%c", c);
335 blank = 0;
336 } else
337 blank = 1;
338 }
339 printf(">\n");
340
341 if ((wd->sc_params.wdp_capabilities & WD_CAP_DMA) != 0 &&
342 wdc->sc_drq != DRQUNK) {
343 wd->sc_mode = WDM_DMA;
344 } else if (wd->sc_params.wdp_maxmulti > 1) {
345 wd->sc_mode = WDM_PIOMULTI;
346 wd->sc_multiple = min(wd->sc_params.wdp_maxmulti, 16);
347 } else {
348 wd->sc_mode = WDM_PIOSINGLE;
349 wd->sc_multiple = 1;
350 }
351
352 if (wd->sc_mode == WDM_DMA)
353 printf("%s: using dma\n", wd->sc_dev.dv_xname);
354 else
355 printf("%s: using %d-sector %d-bit pio\n", wd->sc_dev.dv_xname,
356 wd->sc_multiple, (wd->sc_flags & WDF_32BIT) == 0 ? 16 : 32);
357
358 wd->sc_dk.dk_driver = &wddkdriver;
359 }
360
361 /*
362 * Read/write routine for a buffer. Finds the proper unit, range checks
363 * arguments, and schedules the transfer. Does not wait for the transfer to
364 * complete. Multi-page transfers are supported. All I/O requests must be a
365 * multiple of a sector in length.
366 */
367 void
368 wdstrategy(bp)
369 struct buf *bp;
370 {
371 struct wd_softc *wd; /* disk unit to do the IO */
372 int unit = WDUNIT(bp->b_dev);
373 int s;
374
375 /* Valid unit, controller, and request? */
376 if (unit >= wdcd.cd_ndevs ||
377 (wd = wdcd.cd_devs[unit]) == 0 ||
378 bp->b_blkno < 0 ||
379 (bp->b_bcount % DEV_BSIZE) != 0 ||
380 (bp->b_bcount / DEV_BSIZE) >= (1 << NBBY)) {
381 bp->b_error = EINVAL;
382 goto bad;
383 }
384
385 #if 0
386 /* "Soft" write protect check. */
387 if ((wd->sc_flags & WDF_WRITEPROT) && (bp->b_flags & B_READ) == 0) {
388 bp->b_error = EROFS;
389 goto bad;
390 }
391 #endif
392
393 /* If it's a null transfer, return immediately. */
394 if (bp->b_bcount == 0)
395 goto done;
396
397 /* Have partitions and want to use them? */
398 if (WDPART(bp->b_dev) != RAW_PART) {
399 if ((wd->sc_flags & WDF_BSDLABEL) == 0) {
400 bp->b_error = EIO;
401 goto bad;
402 }
403 /*
404 * Do bounds checking, adjust transfer. if error, process.
405 * If end of partition, just return.
406 */
407 if (bounds_check_with_label(bp, &wd->sc_dk.dk_label,
408 (wd->sc_flags & WDF_WLABEL) != 0) <= 0)
409 goto done;
410 /* Otherwise, process transfer request. */
411 }
412
413 /* Don't bother doing rotational optimization. */
414 bp->b_cylin = 0;
415
416 /* Queue transfer on drive, activate drive and controller if idle. */
417 s = splbio();
418 disksort(&wd->sc_q, bp);
419 if (!wd->sc_q.b_active)
420 wdstart(wd); /* Start drive. */
421 #ifdef DIAGNOSTIC
422 else {
423 struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
424 if ((wdc->sc_flags & WDCF_ACTIVE) == 0) {
425 printf("wdstrategy: controller inactive\n");
426 wdcstart(wdc);
427 }
428 }
429 #endif
430 splx(s);
431 return;
432
433 bad:
434 bp->b_flags |= B_ERROR;
435 done:
436 /* Toss transfer; we're done early. */
437 biodone(bp);
438 }
439
440 /*
441 * Routine to queue a command to the controller. The unit's request is linked
442 * into the active list for the controller. If the controller is idle, the
443 * transfer is started.
444 */
445 void
446 wdstart(wd)
447 struct wd_softc *wd;
448 {
449 struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
450 int active = wdc->sc_drives.tqh_first != 0;
451
452 /* Link onto controller queue. */
453 wd->sc_q.b_active = 1;
454 TAILQ_INSERT_TAIL(&wdc->sc_drives, wd, sc_drivechain);
455
456 /* If controller not already active, start it. */
457 if (!active)
458 wdcstart(wdc);
459 }
460
461 void
462 wdfinish(wd, bp)
463 struct wd_softc *wd;
464 struct buf *bp;
465 {
466 struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
467
468 #ifdef INSTRUMENT
469 dk_busy &= ~(1 << wd->sc_dev.dv_unit);
470 #endif
471 wdc->sc_flags &= ~(WDCF_SINGLE | WDCF_ERROR);
472 wdc->sc_errors = 0;
473 /*
474 * Move this drive to the end of the queue to give others a `fair'
475 * chance.
476 */
477 if (wd->sc_drivechain.tqe_next) {
478 TAILQ_REMOVE(&wdc->sc_drives, wd, sc_drivechain);
479 if (bp->b_actf) {
480 TAILQ_INSERT_TAIL(&wdc->sc_drives, wd,
481 sc_drivechain);
482 } else
483 wd->sc_q.b_active = 0;
484 }
485 bp->b_resid = wd->sc_bcount;
486 wd->sc_skip = 0;
487 wd->sc_q.b_actf = bp->b_actf;
488 biodone(bp);
489 }
490
491 /*
492 * Controller startup routine. This does the calculation, and starts a
493 * single-sector read or write operation. Called to start a transfer, or from
494 * the interrupt routine to continue a multi-sector transfer.
495 * RESTRICTIONS:
496 * 1. The transfer length must be an exact multiple of the sector size.
497 */
498 static void
499 wdcstart(wdc)
500 struct wdc_softc *wdc;
501 {
502 struct wd_softc *wd; /* disk unit for IO */
503 struct buf *bp;
504 int nblks;
505
506 loop:
507 /* Is there a drive for the controller to do a transfer with? */
508 wd = wdc->sc_drives.tqh_first;
509 if (wd == NULL)
510 return;
511
512 /* Is there a transfer to this drive? If not, deactivate drive. */
513 bp = wd->sc_q.b_actf;
514 if (bp == NULL) {
515 TAILQ_REMOVE(&wdc->sc_drives, wd, sc_drivechain);
516 wd->sc_q.b_active = 0;
517 goto loop;
518 }
519
520 if (wdc->sc_errors >= WDIORETRIES) {
521 wderror(wd, bp, "hard error");
522 bp->b_error = EIO;
523 bp->b_flags |= B_ERROR;
524 wdfinish(wd, bp);
525 goto loop;
526 }
527
528 /* Mark the controller active and set a timeout. */
529 if (wdc->sc_flags & WDCF_ACTIVE)
530 untimeout(wdctimeout, wdc);
531 else
532 wdc->sc_flags |= WDCF_ACTIVE;
533 timeout(wdctimeout, wdc, WAITTIME);
534
535 /* Do control operations specially. */
536 if (wd->sc_state < OPEN) {
537 /*
538 * Actually, we want to be careful not to mess with the control
539 * state if the device is currently busy, but we can assume
540 * that we never get to this point if that's the case.
541 */
542 (void) wdcontrol(wd);
543 return;
544 }
545
546 /*
547 * WDCF_ERROR is set by wdcunwedge() and wdcintr() when an error is
548 * encountered. If we are in multi-sector mode, then we switch to
549 * single-sector mode and retry the operation from the start.
550 */
551 if (wdc->sc_flags & WDCF_ERROR) {
552 wdc->sc_flags &= ~WDCF_ERROR;
553 if ((wdc->sc_flags & WDCF_SINGLE) == 0) {
554 wdc->sc_flags |= WDCF_SINGLE;
555 wd->sc_skip = 0;
556 }
557 }
558
559 if (wd->sc_skip == 0) {
560 #ifdef WDDEBUG
561 printf("\n%s: wdcstart %s %d@%d; map ", wd->sc_dev.dv_xname,
562 (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount,
563 bp->b_blkno);
564 #endif
565 wd->sc_bcount = bp->b_bcount;
566 #ifdef INSTRUMENT
567 dk_busy |= (1 << wd->sc_dev.dv_unit);
568 dk_wds[wd->sc_dev.dv_unit] += bp->b_bcount >> 6;
569 #endif
570 } else {
571 #ifdef WDDEBUG
572 printf(" %d)%x", wd->sc_skip, inb(wd->sc_iobase+wd_altsts));
573 #endif
574 }
575
576 /* If starting a multisector transfer, or doing single transfers. */
577 if (wd->sc_skip == 0 || (wdc->sc_flags & WDCF_SINGLE) != 0) {
578 struct disklabel *lp;
579 long blkno;
580 long cylin, head, sector;
581 int command;
582
583 lp = &wd->sc_dk.dk_label;
584 blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) + wd->sc_skip;
585 if (WDPART(bp->b_dev) != RAW_PART)
586 blkno += lp->d_partitions[WDPART(bp->b_dev)].p_offset;
587 if (wdc->sc_flags & WDCF_SINGLE)
588 nblks = 1;
589 else
590 nblks = howmany(wd->sc_bcount, lp->d_secsize);
591 if (wd->sc_mode == WDM_DMA && nblks > 8)
592 nblks = 8;
593
594 /*
595 * Check for bad sectors if we have them, and not formatting. Only do
596 * this in single-sector mode, or when starting a multiple-sector
597 * transfer.
598 */
599 if ((wd->sc_dk.dk_label.d_flags & D_BADSECT) != 0
600 #ifdef B_FORMAT
601 && (bp->b_flags & B_FORMAT) == 0
602 #endif
603 ) {
604 long blkdiff;
605 int i;
606
607 for (i = 0; (blkdiff = wd->sc_badsect[i]) != -1; i++) {
608 blkdiff -= blkno;
609 if (blkdiff < 0)
610 continue;
611 if (blkdiff == 0) {
612 /* Replace current block of transfer. */
613 blkno =
614 lp->d_secperunit - lp->d_nsectors - i - 1;
615 }
616 if (blkdiff < nblks) {
617 /* Bad block inside transfer. */
618 wdc->sc_flags |= WDCF_SINGLE;
619 nblks = 1;
620 }
621 break;
622 }
623 /* Tranfer is okay now. */
624 }
625
626 cylin = blkno / lp->d_secpercyl;
627 head = (blkno % lp->d_secpercyl) / lp->d_nsectors;
628 sector = blkno % lp->d_nsectors;
629 sector++; /* Sectors begin with 1, not 0. */
630
631 #ifdef INSTRUMENT
632 ++dk_seek[wd->sc_dev.dv_unit];
633 ++dk_xfer[wd->sc_dev.dv_unit];
634 #endif
635
636 #ifdef B_FORMAT
637 if (bp->b_flags & B_FORMAT) {
638 sector = lp->d_gap3;
639 nblks = lp->d_nsectors;
640 command = WDCC_FORMAT;
641 } else
642 #endif
643 switch (wd->sc_mode) {
644 case WDM_DMA:
645 command = (bp->b_flags & B_READ) ?
646 WDCC_READDMA : WDCC_WRITEDMA;
647 isa_dmastart(bp->b_flags & B_READ, bp->b_data,
648 nblks * DEV_BSIZE, wdc->sc_drq);
649 break;
650 case WDM_PIOMULTI:
651 command = (bp->b_flags & B_READ) ?
652 WDCC_READMULTI : WDCC_WRITEMULTI;
653 break;
654 case WDM_PIOSINGLE:
655 command = (bp->b_flags & B_READ) ?
656 WDCC_READ : WDCC_WRITE;
657 break;
658 }
659
660 /* Initiate command! */
661 if (wdcommand(wd, command, cylin, head, sector, nblks) != 0) {
662 wderror(wd, NULL,
663 "wdcstart: timeout waiting for unbusy");
664 wdcunwedge(wdc);
665 return;
666 }
667 #ifdef WDDEBUG
668 printf("sector %d cylin %d head %d addr %x sts %x\n", sector,
669 cylin, head, bp->b_data, inb(wd->sc_iobase+wd_altsts));
670 #endif
671 }
672
673 if (wd->sc_mode == WDM_PIOSINGLE ||
674 (wdc->sc_flags & WDCF_SINGLE) != 0)
675 nblks = 1;
676 else if (wd->sc_mode == WDM_PIOMULTI)
677 nblks = min(wd->sc_bcount / DEV_BSIZE, wd->sc_multiple);
678 else
679 nblks = min(wd->sc_bcount / DEV_BSIZE, 8);
680 wdc->sc_nblks = nblks;
681
682 /* If this was a write and not using DMA, push the data. */
683 if (wd->sc_mode != WDM_DMA &&
684 (bp->b_flags & B_READ) == 0) {
685 if (wait_for_drq(wdc) < 0) {
686 wderror(wd, NULL, "wdcstart: timeout waiting for drq");
687 wdcunwedge(wdc);
688 return;
689 }
690
691 /* Then send it! */
692 if ((wd->sc_flags & WDF_32BIT) == 0)
693 outsw(wdc->sc_iobase+wd_data,
694 bp->b_data + wd->sc_skip * DEV_BSIZE,
695 nblks * DEV_BSIZE / sizeof(short));
696 else
697 outsl(wdc->sc_iobase+wd_data,
698 bp->b_data + wd->sc_skip * DEV_BSIZE,
699 nblks * DEV_BSIZE / sizeof(long));
700 }
701 }
702
703 /*
704 * Interrupt routine for the controller. Acknowledge the interrupt, check for
705 * errors on the current operation, mark it done if necessary, and start the
706 * next request. Also check for a partially done transfer, and continue with
707 * the next chunk if so.
708 */
709 int
710 wdcintr(wdc)
711 struct wdc_softc *wdc;
712 {
713 struct wd_softc *wd;
714 struct buf *bp;
715 int nblks;
716
717 if ((wdc->sc_flags & WDCF_ACTIVE) == 0) {
718 /* Clear the pending interrupt. */
719 (void) inb(wdc->sc_iobase+wd_status);
720 return 0;
721 }
722
723 wd = wdc->sc_drives.tqh_first;
724 bp = wd->sc_q.b_actf;
725
726 #ifdef WDDEBUG
727 printf("I%d ", ctrlr);
728 #endif
729
730 if (wait_for_unbusy(wdc) < 0) {
731 wderror(wd, NULL, "wdcintr: timeout waiting for unbusy");
732 wdc->sc_status |= WDCS_ERR; /* XXX */
733 }
734
735 /* Is it not a transfer, but a control operation? */
736 if (wd->sc_state < OPEN) {
737 if (wdcontrol(wd))
738 wdcstart(wdc);
739 return 1;
740 }
741
742 wdc->sc_flags &= ~WDCF_ACTIVE;
743 untimeout(wdctimeout, wdc);
744 nblks = wdc->sc_nblks;
745
746 if (wd->sc_mode == WDM_DMA)
747 isa_dmadone(bp->b_flags & B_READ, bp->b_data,
748 nblks * DEV_BSIZE, wdc->sc_drq);
749
750 /* Have we an error? */
751 if (wdc->sc_status & WDCS_ERR) {
752 lose:
753 #ifdef WDDEBUG
754 wderror(wd, NULL, "wdcintr");
755 #endif
756 if ((wdc->sc_flags & WDCF_SINGLE) == 0) {
757 wdc->sc_flags |= WDCF_ERROR;
758 goto restart;
759 }
760
761 #ifdef B_FORMAT
762 if (bp->b_flags & B_FORMAT)
763 goto bad;
764 #endif
765
766 if (++wdc->sc_errors < WDIORETRIES)
767 goto restart;
768 wderror(wd, bp, "hard error");
769
770 bad:
771 bp->b_error = EIO;
772 bp->b_flags |= B_ERROR;
773 goto done;
774 }
775
776 if (wdc->sc_status & WDCS_CORR)
777 wderror(wd, bp, "soft ecc");
778
779 /* If this was a read and not using DMA, fetch the data. */
780 if (wd->sc_mode != WDM_DMA &&
781 (bp->b_flags & B_READ) != 0) {
782 if ((wdc->sc_status & (WDCS_DRDY | WDCS_DSC | WDCS_DRQ))
783 != (WDCS_DRDY | WDCS_DSC | WDCS_DRQ)) {
784 wderror(wd, NULL, "wdcintr: read intr before drq");
785 wdcunwedge(wdc);
786 return 1;
787 }
788
789 /* Suck in data. */
790 if ((wd->sc_flags & WDF_32BIT) == 0)
791 insw(wdc->sc_iobase+wd_data,
792 bp->b_data + wd->sc_skip * DEV_BSIZE,
793 nblks * DEV_BSIZE / sizeof(short));
794 else
795 insl(wdc->sc_iobase+wd_data,
796 bp->b_data + wd->sc_skip * DEV_BSIZE,
797 nblks * DEV_BSIZE / sizeof(long));
798 }
799
800 /* If we encountered any abnormalities, flag it as a soft error. */
801 if (wdc->sc_errors) {
802 wderror(wd, bp, "soft error");
803 wdc->sc_errors = 0;
804 }
805
806 /* Ready for the next block, if any. */
807 wd->sc_skip += nblks;
808 wd->sc_bcount -= nblks * DEV_BSIZE;
809
810 /* See if more to transfer. */
811 if (wd->sc_bcount > 0)
812 goto restart;
813
814 done:
815 /* Done with this transfer, with or without error. */
816 wdfinish(wd, bp);
817
818 restart:
819 /* Start the next transfer, if any. */
820 wdcstart(wdc);
821
822 return 1;
823 }
824
825 /*
826 * Initialize a drive.
827 */
828 int
829 wdopen(dev, flag, fmt)
830 dev_t dev;
831 int flag, fmt;
832 {
833 int error;
834 int unit, part;
835 struct wd_softc *wd;
836
837 unit = WDUNIT(dev);
838 if (unit >= wdcd.cd_ndevs)
839 return ENXIO;
840 wd = wdcd.cd_devs[unit];
841 if (wd == 0)
842 return ENXIO;
843
844 part = WDPART(dev);
845
846 while ((wd->sc_flags & WDF_LOCKED) != 0) {
847 wd->sc_flags |= WDF_WANTED;
848 if ((error = tsleep(wd, PRIBIO | PCATCH, "wdopn", 0)) != 0)
849 return error;
850 }
851
852 if (wd->sc_dk.dk_openmask != 0) {
853 /*
854 * If any partition is open, but the disk has been invalidated,
855 * disallow further opens.
856 */
857 if ((wd->sc_flags & WDF_LOADED) == 0)
858 return ENXIO;
859 } else {
860 wd->sc_flags |= WDF_LOCKED;
861
862 if ((wd->sc_flags & WDF_LOADED) == 0) {
863 wd->sc_flags &= ~WDF_BSDLABEL;
864 wd->sc_flags |= WDF_LOADED;
865
866 /* Load the physical device parameters. */
867 if (wd_get_parms(wd) != 0) {
868 error = ENXIO;
869 goto bad2;
870 }
871
872 /* Load the partition info if not already loaded. */
873 wdgetdisklabel(wd);
874 }
875
876 wd->sc_flags &= ~WDF_LOCKED;
877 if ((wd->sc_flags & WDF_WANTED) != 0) {
878 wd->sc_flags &= ~WDF_WANTED;
879 wakeup(wd);
880 }
881 }
882
883 /* Check that the partition exists. */
884 if (part != RAW_PART &&
885 (part >= wd->sc_dk.dk_label.d_npartitions ||
886 wd->sc_dk.dk_label.d_partitions[part].p_fstype == FS_UNUSED)) {
887 error = ENXIO;
888 goto bad;
889 }
890
891 /* Insure only one open at a time. */
892 switch (fmt) {
893 case S_IFCHR:
894 wd->sc_dk.dk_copenmask |= (1 << part);
895 break;
896 case S_IFBLK:
897 wd->sc_dk.dk_bopenmask |= (1 << part);
898 break;
899 }
900 wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask;
901
902 return 0;
903
904 bad2:
905 wd->sc_flags &= ~WDF_LOADED;
906
907 bad:
908 wd->sc_flags &= ~WDF_LOCKED;
909 if ((wd->sc_flags & WDF_WANTED) != 0) {
910 wd->sc_flags &= ~WDF_WANTED;
911 wakeup(wd);
912 }
913
914 return error;
915 }
916
917 void
918 wdgetdisklabel(wd)
919 struct wd_softc *wd;
920 {
921 char *errstring;
922
923 if ((wd->sc_flags & WDF_BSDLABEL) != 0)
924 return;
925
926 bzero(&wd->sc_dk.dk_label, sizeof(struct disklabel));
927 bzero(&wd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel));
928
929 wd->sc_dk.dk_label.d_secsize = DEV_BSIZE;
930 wd->sc_dk.dk_label.d_ntracks = wd->sc_params.wdp_heads;
931 wd->sc_dk.dk_label.d_nsectors = wd->sc_params.wdp_sectors;
932 wd->sc_dk.dk_label.d_ncylinders = wd->sc_params.wdp_cylinders;
933 wd->sc_dk.dk_label.d_secpercyl =
934 wd->sc_dk.dk_label.d_ntracks * wd->sc_dk.dk_label.d_nsectors;
935
936 #if 0
937 strncpy(wd->sc_dk.dk_label.d_typename, "ST506 disk", 16);
938 wd->sc_dk.dk_label.d_type = DTYPE_ST506;
939 #endif
940 strncpy(wd->sc_dk.dk_label.d_packname, wd->sc_params.wdp_model, 16);
941 wd->sc_dk.dk_label.d_secperunit =
942 wd->sc_dk.dk_label.d_secpercyl * wd->sc_dk.dk_label.d_ncylinders;
943 wd->sc_dk.dk_label.d_rpm = 3600;
944 wd->sc_dk.dk_label.d_interleave = 1;
945 wd->sc_dk.dk_label.d_flags = 0;
946
947 wd->sc_dk.dk_label.d_partitions[RAW_PART].p_offset = 0;
948 wd->sc_dk.dk_label.d_partitions[RAW_PART].p_size =
949 wd->sc_dk.dk_label.d_secperunit *
950 (wd->sc_dk.dk_label.d_secsize / DEV_BSIZE);
951 wd->sc_dk.dk_label.d_partitions[RAW_PART].p_fstype = FS_UNUSED;
952 wd->sc_dk.dk_label.d_npartitions = RAW_PART + 1;
953
954 wd->sc_dk.dk_label.d_magic = DISKMAGIC;
955 wd->sc_dk.dk_label.d_magic2 = DISKMAGIC;
956 wd->sc_dk.dk_label.d_checksum = dkcksum(&wd->sc_dk.dk_label);
957
958 wd->sc_badsect[0] = -1;
959
960 if (wd->sc_state > RECAL)
961 wd->sc_state = RECAL;
962 errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART),
963 wdstrategy, &wd->sc_dk.dk_label, &wd->sc_dk.dk_cpulabel);
964 if (errstring) {
965 /*
966 * This probably happened because the drive's default
967 * geometry doesn't match the DOS geometry. We
968 * assume the DOS geometry is now in the label and try
969 * again. XXX This is a kluge.
970 */
971 if (wd->sc_state > GEOMETRY)
972 wd->sc_state = GEOMETRY;
973 errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART),
974 wdstrategy, &wd->sc_dk.dk_label, &wd->sc_dk.dk_cpulabel);
975 }
976 if (errstring) {
977 printf("%s: %s\n", wd->sc_dev.dv_xname, errstring);
978 return;
979 }
980
981 if (wd->sc_state > GEOMETRY)
982 wd->sc_state = GEOMETRY;
983 if ((wd->sc_dk.dk_label.d_flags & D_BADSECT) != 0)
984 bad144intern(wd);
985
986 wd->sc_flags |= WDF_BSDLABEL;
987 }
988
989 /*
990 * Implement operations other than read/write.
991 * Called from wdcstart or wdcintr during opens and formats.
992 * Uses finite-state-machine to track progress of operation in progress.
993 * Returns 0 if operation still in progress, 1 if completed.
994 */
995 static int
996 wdcontrol(wd)
997 struct wd_softc *wd;
998 {
999 struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
1000
1001 switch (wd->sc_state) {
1002 case RECAL: /* Set SDH, step rate, do recal. */
1003 if (wdcommandshort(wdc, wd->sc_drive, WDCC_RECAL) != 0) {
1004 wderror(wd, NULL, "wdcontrol: recal failed (1)");
1005 goto bad;
1006 }
1007 wd->sc_state = RECAL_WAIT;
1008 return 0;
1009
1010 case RECAL_WAIT:
1011 if (wdc->sc_status & WDCS_ERR) {
1012 wderror(wd, NULL, "wdcontrol: recal failed (2)");
1013 goto bad;
1014 }
1015 /* fall through */
1016 case GEOMETRY:
1017 if (wdsetctlr(wd) != 0) {
1018 /* Already printed a message. */
1019 goto bad;
1020 }
1021 wd->sc_state = GEOMETRY_WAIT;
1022 return 0;
1023
1024 case GEOMETRY_WAIT:
1025 if (wdc->sc_status & WDCS_ERR) {
1026 wderror(wd, NULL, "wdcontrol: geometry failed");
1027 goto bad;
1028 }
1029 /* fall through */
1030 case MULTIMODE:
1031 if (wd->sc_mode != WDM_PIOMULTI)
1032 goto open;
1033 outb(wdc->sc_iobase+wd_seccnt, wd->sc_multiple);
1034 if (wdcommandshort(wdc, wd->sc_drive, WDCC_SETMULTI) != 0) {
1035 wderror(wd, NULL, "wdcontrol: setmulti failed (1)");
1036 goto bad;
1037 }
1038 wd->sc_state = MULTIMODE_WAIT;
1039 return 0;
1040
1041 case MULTIMODE_WAIT:
1042 if (wdc->sc_status & WDCS_ERR) {
1043 wderror(wd, NULL, "wdcontrol: setmulti failed (2)");
1044 goto bad;
1045 }
1046 /* fall through */
1047 open:
1048 wdc->sc_errors = 0;
1049 wd->sc_state = OPEN;
1050 /*
1051 * The rest of the initialization can be done by normal means.
1052 */
1053 return 1;
1054
1055 bad:
1056 wdcunwedge(wdc);
1057 return 0;
1058 }
1059
1060 #ifdef DIAGNOSTIC
1061 panic("wdcontrol: impossible");
1062 #endif
1063 }
1064
1065 /*
1066 * Send a command and wait uninterruptibly until controller is finished.
1067 * Return -1 if controller busy for too long, otherwise return non-zero if
1068 * error. Intended for brief controller commands at critical points.
1069 * Assumes interrupts are blocked.
1070 */
1071 static int
1072 wdcommand(wd, command, cylin, head, sector, count)
1073 struct wd_softc *wd;
1074 int command;
1075 int cylin, head, sector, count;
1076 {
1077 struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
1078 int iobase = wdc->sc_iobase;
1079 int stat;
1080
1081 /* Select drive. */
1082 outb(iobase+wd_sdh, WDSD_IBM | (wd->sc_drive << 4) | head);
1083
1084 /* Wait for it to become ready to accept a command. */
1085 if (command == WDCC_IDP)
1086 stat = wait_for_unbusy(wdc);
1087 else
1088 stat = wdcwait(wdc, WDCS_DRDY);
1089 if (stat < 0)
1090 return -1;
1091
1092 /* Load parameters. */
1093 if (wd->sc_dk.dk_label.d_type == DTYPE_ST506)
1094 outb(iobase+wd_precomp, wd->sc_dk.dk_label.d_precompcyl / 4);
1095 else
1096 outb(iobase+wd_features, 0);
1097 outb(iobase+wd_cyl_lo, cylin);
1098 outb(iobase+wd_cyl_hi, cylin >> 8);
1099 outb(iobase+wd_sector, sector);
1100 outb(iobase+wd_seccnt, count);
1101
1102 /* Send command. */
1103 outb(iobase+wd_command, command);
1104
1105 return 0;
1106 }
1107
1108 int
1109 wdcommandshort(wdc, drive, command)
1110 struct wdc_softc *wdc;
1111 int drive;
1112 int command;
1113 {
1114 int iobase = wdc->sc_iobase;
1115
1116 /* Select drive. */
1117 outb(iobase+wd_sdh, WDSD_IBM | (drive << 4));
1118
1119 if (wdcwait(wdc, WDCS_DRDY) < 0)
1120 return -1;
1121
1122 outb(iobase+wd_command, command);
1123
1124 return 0;
1125 }
1126
1127 /*
1128 * Issue IDP to drive to tell it just what geometry it is to be.
1129 */
1130 static int
1131 wdsetctlr(wd)
1132 struct wd_softc *wd;
1133 {
1134 struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
1135
1136 #ifdef WDDEBUG
1137 printf("wd(%d,%d) C%dH%dS%d\n", wd->sc_dev.dv_unit, wd->sc_drive,
1138 wd->sc_dk.dk_label.d_ncylinders, wd->sc_dk.dk_label.d_ntracks,
1139 wd->sc_dk.dk_label.d_nsectors);
1140 #endif
1141
1142 if (wdcommand(wd, WDCC_IDP, wd->sc_dk.dk_label.d_ncylinders,
1143 wd->sc_dk.dk_label.d_ntracks - 1, 0, wd->sc_dk.dk_label.d_nsectors)
1144 != 0) {
1145 wderror(wd, NULL, "wdsetctlr: geometry upload failed");
1146 return -1;
1147 }
1148
1149 return 0;
1150 }
1151
1152 /*
1153 * Issue IDENTIFY to drive to ask it what it is.
1154 */
1155 int
1156 wd_get_parms(wd)
1157 struct wd_softc *wd;
1158 {
1159 struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
1160 int i;
1161 char tb[DEV_BSIZE];
1162
1163 if (wdcommandshort(wdc, wd->sc_drive, WDCC_IDENTIFY) != 0 ||
1164 wait_for_drq(wdc) != 0) {
1165 /*
1166 * We `know' there's a drive here; just assume it's old.
1167 */
1168 strncpy(wd->sc_dk.dk_label.d_typename, "ST506",
1169 sizeof wd->sc_dk.dk_label.d_typename);
1170 wd->sc_dk.dk_label.d_type = DTYPE_ST506;
1171
1172 strncpy(wd->sc_params.wdp_model, "unknown",
1173 sizeof wd->sc_params.wdp_model);
1174 wd->sc_params.wdp_config = WD_CFG_FIXED;
1175 wd->sc_params.wdp_cylinders = 1024;
1176 wd->sc_params.wdp_heads = 8;
1177 wd->sc_params.wdp_sectors = 17;
1178 wd->sc_params.wdp_maxmulti = 0;
1179 wd->sc_params.wdp_usedmovsd = 0;
1180 wd->sc_params.wdp_capabilities = 0;
1181 } else {
1182 /* Obtain parameters. */
1183 insw(wdc->sc_iobase+wd_data, tb, sizeof(tb) / sizeof(short));
1184 bcopy(tb, &wd->sc_params, sizeof(struct wdparams));
1185
1186 /* Shuffle string byte order. */
1187 for (i = 0; i < sizeof(wd->sc_params.wdp_model); i += 2) {
1188 u_short *p;
1189 p = (u_short *)(wd->sc_params.wdp_model + i);
1190 *p = ntohs(*p);
1191 }
1192
1193 strncpy(wd->sc_dk.dk_label.d_typename, "ESDI/IDE",
1194 sizeof wd->sc_dk.dk_label.d_typename);
1195 wd->sc_dk.dk_label.d_type = DTYPE_ESDI;
1196 }
1197
1198 #if 0
1199 printf("gc %x cyl %d trk %d sec %d type %d sz %d model %s\n",
1200 wp->wdp_config, wp->wdp_cylinders, wp->wdp_heads, wp->wdp_sectors,
1201 wp->wdp_buftype, wp->wdp_bufsize, wp->wdp_model);
1202 #endif
1203
1204 /* XXX sometimes possibly needed */
1205 (void) inb(wdc->sc_iobase+wd_status);
1206
1207 return 0;
1208 }
1209
1210 int
1211 wdclose(dev, flag, fmt)
1212 dev_t dev;
1213 int flag, fmt;
1214 {
1215 struct wd_softc *wd = wdcd.cd_devs[WDUNIT(dev)];
1216 int part = WDPART(dev);
1217 int s;
1218
1219 switch (fmt) {
1220 case S_IFCHR:
1221 wd->sc_dk.dk_copenmask &= ~(1 << part);
1222 break;
1223 case S_IFBLK:
1224 wd->sc_dk.dk_bopenmask &= ~(1 << part);
1225 break;
1226 }
1227 wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask;
1228
1229 if (wd->sc_dk.dk_openmask == 0) {
1230 wd->sc_flags |= WDF_LOCKED;
1231
1232 #if 0
1233 s = splbio();
1234 while (...) {
1235 wd->sc_flags |= WDF_WAITING;
1236 if ((error = tsleep(wd, PRIBIO | PCATCH, "wdcls", 0)) != 0)
1237 return error;
1238 }
1239 splx(s);
1240 #endif
1241
1242 wd->sc_flags &= ~WDF_LOCKED;
1243 if ((wd->sc_flags & WDF_WANTED) != 0) {
1244 wd->sc_flags &= WDF_WANTED;
1245 wakeup(wd);
1246 }
1247 }
1248
1249 return 0;
1250 }
1251
1252 int
1253 wdioctl(dev, command, addr, flag, p)
1254 dev_t dev;
1255 u_long command;
1256 caddr_t addr;
1257 int flag;
1258 struct proc *p;
1259 {
1260 struct wd_softc *wd = wdcd.cd_devs[WDUNIT(dev)];
1261 int error;
1262
1263 if ((wd->sc_flags & WDF_LOADED) == 0)
1264 return EIO;
1265
1266 switch (command) {
1267 case DIOCSBAD:
1268 if ((flag & FWRITE) == 0)
1269 return EBADF;
1270 wd->sc_dk.dk_cpulabel.bad = *(struct dkbad *)addr;
1271 wd->sc_dk.dk_label.d_flags |= D_BADSECT;
1272 bad144intern(wd);
1273 return 0;
1274
1275 case DIOCGDINFO:
1276 *(struct disklabel *)addr = wd->sc_dk.dk_label;
1277 return 0;
1278
1279 case DIOCGPART:
1280 ((struct partinfo *)addr)->disklab = &wd->sc_dk.dk_label;
1281 ((struct partinfo *)addr)->part =
1282 &wd->sc_dk.dk_label.d_partitions[WDPART(dev)];
1283 return 0;
1284
1285 case DIOCSDINFO:
1286 if ((flag & FWRITE) == 0)
1287 return EBADF;
1288 error = setdisklabel(&wd->sc_dk.dk_label,
1289 (struct disklabel *)addr,
1290 /*(wd->sc_flags & WDF_BSDLABEL) ? wd->sc_dk.dk_openmask : */0,
1291 &wd->sc_dk.dk_cpulabel);
1292 if (error == 0) {
1293 wd->sc_flags |= WDF_BSDLABEL;
1294 if (wd->sc_state > GEOMETRY)
1295 wd->sc_state = GEOMETRY;
1296 }
1297 return error;
1298
1299 case DIOCWLABEL:
1300 if ((flag & FWRITE) == 0)
1301 return EBADF;
1302 if (*(int *)addr)
1303 wd->sc_flags |= WDF_WLABEL;
1304 else
1305 wd->sc_flags &= ~WDF_WLABEL;
1306 return 0;
1307
1308 case DIOCWDINFO:
1309 if ((flag & FWRITE) == 0)
1310 return EBADF;
1311 error = setdisklabel(&wd->sc_dk.dk_label,
1312 (struct disklabel *)addr,
1313 /*(wd->sc_flags & WDF_BSDLABEL) ? wd->sc_dk.dk_openmask : */0,
1314 &wd->sc_dk.dk_cpulabel);
1315 if (error == 0) {
1316 wd->sc_flags |= WDF_BSDLABEL;
1317 if (wd->sc_state > GEOMETRY)
1318 wd->sc_state = GEOMETRY;
1319
1320 /* Simulate opening partition 0 so write succeeds. */
1321 wd->sc_dk.dk_openmask |= (1 << 0); /* XXX */
1322 error = writedisklabel(WDLABELDEV(dev), wdstrategy,
1323 &wd->sc_dk.dk_label, &wd->sc_dk.dk_cpulabel);
1324 wd->sc_dk.dk_openmask =
1325 wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask;
1326 }
1327 return error;
1328
1329 #ifdef notyet
1330 case DIOCGDINFOP:
1331 *(struct disklabel **)addr = &wd->sc_dk.dk_label;
1332 return 0;
1333
1334 case DIOCWFORMAT:
1335 if ((flag & FWRITE) == 0)
1336 return EBADF;
1337 {
1338 register struct format_op *fop;
1339 struct iovec aiov;
1340 struct uio auio;
1341
1342 fop = (struct format_op *)addr;
1343 aiov.iov_base = fop->df_buf;
1344 aiov.iov_len = fop->df_count;
1345 auio.uio_iov = &aiov;
1346 auio.uio_iovcnt = 1;
1347 auio.uio_resid = fop->df_count;
1348 auio.uio_segflg = 0;
1349 auio.uio_offset =
1350 fop->df_startblk * wd->sc_dk.dk_label.d_secsize;
1351 auio.uio_procp = p;
1352 error = physio(wdformat, NULL, dev, B_WRITE, minphys,
1353 &auio);
1354 fop->df_count -= auio.uio_resid;
1355 fop->df_reg[0] = wdc->sc_status;
1356 fop->df_reg[1] = wdc->sc_error;
1357 return error;
1358 }
1359 #endif
1360
1361 default:
1362 return ENOTTY;
1363 }
1364
1365 #ifdef DIAGNOSTIC
1366 panic("wdioctl: impossible");
1367 #endif
1368 }
1369
1370 #ifdef B_FORMAT
1371 int
1372 wdformat(struct buf *bp)
1373 {
1374
1375 bp->b_flags |= B_FORMAT;
1376 return wdstrategy(bp);
1377 }
1378 #endif
1379
1380 int
1381 wdsize(dev)
1382 dev_t dev;
1383 {
1384 struct wd_softc *wd;
1385 int part;
1386 int size;
1387
1388 if (wdopen(dev, 0, S_IFBLK) != 0)
1389 return -1;
1390 wd = wdcd.cd_devs[WDUNIT(dev)];
1391 part = WDPART(dev);
1392 if ((wd->sc_flags & WDF_BSDLABEL) == 0 ||
1393 wd->sc_dk.dk_label.d_partitions[part].p_fstype != FS_SWAP)
1394 size = -1;
1395 else
1396 size = wd->sc_dk.dk_label.d_partitions[part].p_size;
1397 if (wdclose(dev, 0, S_IFBLK) != 0)
1398 return -1;
1399 return size;
1400 }
1401
1402 /*
1403 * Dump core after a system crash.
1404 */
1405 int
1406 wddump(dev)
1407 dev_t dev;
1408 {
1409 struct wd_softc *wd; /* disk unit to do the IO */
1410 struct wdc_softc *wdc;
1411 long num; /* number of sectors to write */
1412 int unit, part;
1413 long blkoff, blkno;
1414 long cylin, head, sector;
1415 long secpertrk, secpercyl, nblocks;
1416 char *addr;
1417 static wddoingadump = 0;
1418 extern caddr_t CADDR1;
1419 extern pt_entry_t *CMAP1;
1420
1421 addr = (char *)0; /* starting address */
1422
1423 #if DO_NOT_KNOW_HOW
1424 /* Toss any characters present prior to dump, ie. non-blocking getc. */
1425 while (cngetc())
1426 ;
1427 #endif
1428
1429 unit = WDUNIT(dev);
1430 /* Check for acceptable drive number. */
1431 if (unit >= wdcd.cd_ndevs)
1432 return ENXIO;
1433 wd = wdcd.cd_devs[unit];
1434 /* Was it ever initialized? */
1435 if (wd == 0 || wd->sc_state < OPEN)
1436 return ENXIO;
1437
1438 wdc = (void *)wd->sc_dev.dv_parent;
1439
1440 /* Convert to disk sectors. */
1441 num = ctob(physmem) / wd->sc_dk.dk_label.d_secsize;
1442
1443 secpertrk = wd->sc_dk.dk_label.d_nsectors;
1444 secpercyl = wd->sc_dk.dk_label.d_secpercyl;
1445 part = WDPART(dev);
1446 nblocks = wd->sc_dk.dk_label.d_partitions[part].p_size;
1447 blkoff = wd->sc_dk.dk_label.d_partitions[part].p_offset;
1448
1449 /*printf("part %x, nblocks %d, dumplo %d, num %d\n", part, nblocks,
1450 dumplo, num);*/
1451
1452 /* Check transfer bounds against partition size. */
1453 if (dumplo < 0 || dumplo + num > nblocks)
1454 return EINVAL;
1455
1456 if (wddoingadump)
1457 return EFAULT;
1458 wddoingadump = 1;
1459
1460 /* Recalibrate. */
1461 if (wdcommandshort(wdc, wd->sc_drive, WDCC_RECAL) != 0 ||
1462 wait_for_ready(wdc) != 0 || wdsetctlr(wd) != 0 ||
1463 wait_for_ready(wdc) != 0) {
1464 wderror(wd, NULL, "wddump: recal failed");
1465 return EIO;
1466 }
1467
1468 blkno = dumplo + blkoff;
1469 while (num > 0) {
1470 /* Compute disk address. */
1471 cylin = blkno / secpercyl;
1472 head = (blkno % secpercyl) / secpertrk;
1473 sector = blkno % secpertrk;
1474
1475 if ((wd->sc_dk.dk_label.d_flags & D_BADSECT) != 0) {
1476 long newblk;
1477 int i;
1478
1479 for (i = 0; wd->sc_badsect[i] != -1; i++) {
1480 if (blkno < wd->sc_badsect[i]) {
1481 /* Sorted list, passed our block by. */
1482 break;
1483 } else if (blkno == wd->sc_badsect[i]) {
1484 newblk =
1485 wd->sc_dk.dk_label.d_secperunit -
1486 wd->sc_dk.dk_label.d_nsectors -
1487 i - 1;
1488 cylin = newblk / secpercyl;
1489 head = (newblk % secpercyl) / secpertrk;
1490 sector = newblk % secpertrk;
1491 /* Found and replaced; done. */
1492 break;
1493 }
1494 }
1495 }
1496 sector++; /* origin 1 */
1497
1498 #ifdef notdef
1499 /* Let's just talk about this first. */
1500 printf("cylin %d, head %d, sector %d, addr 0x%x", cylin, head,
1501 sector, addr);
1502 #endif
1503 if (wdcommand(wd, WDCC_WRITE, cylin, head, sector, 1) != 0 ||
1504 wait_for_drq(wdc) != 0) {
1505 wderror(wd, NULL, "wddump: write failed");
1506 return EIO;
1507 }
1508
1509 #ifdef notdef /* Cannot use this since this address was mapped differently. */
1510 pmap_enter(kernel_pmap, CADDR1, trunc_page(addr), VM_PROT_READ, TRUE);
1511 #else
1512 *CMAP1 = PG_V | PG_KW | ctob((long)addr);
1513 tlbflush();
1514 #endif
1515
1516 outsw(wdc->sc_iobase+wd_data, CADDR1 + ((int)addr & PGOFSET),
1517 DEV_BSIZE / sizeof(short));
1518
1519 /* Check data request (should be done). */
1520 if (wait_for_ready(wdc) != 0) {
1521 wderror(wd, NULL, "wddump: timeout waiting for ready");
1522 return EIO;
1523 }
1524 if (wdc->sc_status & WDCS_DRQ) {
1525 wderror(wd, NULL, "wddump: extra drq");
1526 return EIO;
1527 }
1528
1529 if ((unsigned)addr % 1048576 == 0)
1530 printf("%d ", num / (1048576 / DEV_BSIZE));
1531
1532 /* Update block count. */
1533 num--;
1534 blkno++;
1535 (int)addr += DEV_BSIZE;
1536
1537 #if DO_NOT_KNOW_HOW
1538 /* Operator aborting dump? non-blocking getc() */
1539 if (cngetc())
1540 return EINTR;
1541 #endif
1542 }
1543 return 0;
1544 }
1545
1546 /*
1547 * Internalize the bad sector table.
1548 */
1549 void
1550 bad144intern(wd)
1551 struct wd_softc *wd;
1552 {
1553 struct dkbad *bt = &wd->sc_dk.dk_cpulabel.bad;
1554 struct disklabel *lp = &wd->sc_dk.dk_label;
1555 int i = 0;
1556
1557 for (; i < 126; i++) {
1558 if (bt->bt_bad[i].bt_cyl == 0xffff)
1559 break;
1560 wd->sc_badsect[i] =
1561 bt->bt_bad[i].bt_cyl * lp->d_secpercyl +
1562 (bt->bt_bad[i].bt_trksec >> 8) * lp->d_nsectors +
1563 (bt->bt_bad[i].bt_trksec & 0xff);
1564 }
1565 for (; i < 127; i++)
1566 wd->sc_badsect[i] = -1;
1567 }
1568
1569 static int
1570 wdcreset(wdc)
1571 struct wdc_softc *wdc;
1572 {
1573 int iobase = wdc->sc_iobase;
1574
1575 /* Reset the device. */
1576 outb(iobase+wd_ctlr, WDCTL_RST | WDCTL_IDS);
1577 delay(1000);
1578 outb(iobase+wd_ctlr, WDCTL_IDS);
1579 delay(1000);
1580 (void) inb(iobase+wd_error);
1581 outb(iobase+wd_ctlr, WDCTL_4BIT);
1582
1583 if (wait_for_unbusy(wdc) < 0) {
1584 printf("%s: reset failed\n", wdc->sc_dev.dv_xname);
1585 return 1;
1586 }
1587
1588 return 0;
1589 }
1590
1591 static void
1592 wdcrestart(arg)
1593 void *arg;
1594 {
1595 struct wdc_softc *wdc = (struct wdc_softc *)arg;
1596 int s;
1597
1598 s = splbio();
1599 wdcstart(wdc);
1600 splx(s);
1601 }
1602
1603 /*
1604 * Unwedge the controller after an unexpected error. We do this by resetting
1605 * it, marking all drives for recalibration, and stalling the queue for a short
1606 * period to give the reset time to finish.
1607 * NOTE: We use a timeout here, so this routine must not be called during
1608 * autoconfig or dump.
1609 */
1610 static void
1611 wdcunwedge(wdc)
1612 struct wdc_softc *wdc;
1613 {
1614 int unit;
1615
1616 untimeout(wdctimeout, wdc);
1617 (void) wdcreset(wdc);
1618
1619 /* Schedule recalibrate for all drives on this controller. */
1620 for (unit = 0; unit < wdcd.cd_ndevs; unit++) {
1621 struct wd_softc *wd = wdcd.cd_devs[unit];
1622 if (!wd || (void *)wd->sc_dev.dv_parent != wdc)
1623 continue;
1624 if (wd->sc_state > RECAL)
1625 wd->sc_state = RECAL;
1626 }
1627
1628 wdc->sc_flags |= WDCF_ERROR;
1629 ++wdc->sc_errors;
1630
1631 /* Wake up in a little bit and restart the operation. */
1632 timeout(wdcrestart, wdc, RECOVERYTIME);
1633 }
1634
1635 int
1636 wdcwait(wdc, mask)
1637 struct wdc_softc *wdc;
1638 int mask;
1639 {
1640 int iobase = wdc->sc_iobase;
1641 int timeout = 0;
1642 u_char status;
1643 extern int cold;
1644
1645 for (;;) {
1646 wdc->sc_status = status = inb(iobase+wd_status);
1647 if ((status & WDCS_BSY) == 0 && (status & mask) == mask)
1648 break;
1649 if (++timeout > WDCNDELAY)
1650 return -1;
1651 delay(WDCDELAY);
1652 }
1653 if (status & WDCS_ERR) {
1654 wdc->sc_error = inb(iobase+wd_error);
1655 return WDCS_ERR;
1656 }
1657 #ifdef WDCNDELAY_DEBUG
1658 /* After autoconfig, there should be no long delays. */
1659 if (!cold && timeout > WDCNDELAY_DEBUG)
1660 printf("%s: warning: busy-wait took %dus\n",
1661 wdc->sc_dev.dv_xname, WDCDELAY * timeout);
1662 #endif
1663 return 0;
1664 }
1665
1666 static void
1667 wdctimeout(arg)
1668 void *arg;
1669 {
1670 struct wdc_softc *wdc = (struct wdc_softc *)arg;
1671 int s;
1672
1673 s = splbio();
1674 wderror(wdc, NULL, "lost interrupt");
1675 wdcunwedge(wdc);
1676 splx(s);
1677 }
1678
1679 static void
1680 wderror(dev, bp, msg)
1681 void *dev;
1682 struct buf *bp;
1683 char *msg;
1684 {
1685 struct wd_softc *wd = dev;
1686 struct wdc_softc *wdc = dev;
1687
1688 if (bp) {
1689 diskerr(bp, "wd", msg, LOG_PRINTF, wd->sc_skip,
1690 &wd->sc_dk.dk_label);
1691 printf("\n");
1692 } else
1693 printf("%s: %s: status %b error %b\n", wdc->sc_dev.dv_xname,
1694 msg, wdc->sc_status, WDCS_BITS, wdc->sc_error, WDERR_BITS);
1695 }
1696