wd.c revision 1.118 1 /* $NetBSD: wd.c,v 1.118 1994/11/23 07:54:15 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 printf("%s: using", wd->sc_dev.dv_xname);
353 if (wd->sc_mode == WDM_DMA)
354 printf(" dma transfers,");
355 else
356 printf(" %d-sector %d-bit pio transfers,",
357 wd->sc_multiple, (wd->sc_flags & WDF_32BIT) == 0 ? 16 : 32);
358 if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0)
359 printf(" lba addressing\n");
360 else
361 printf(" chs addressing\n");
362
363 wd->sc_dk.dk_driver = &wddkdriver;
364 }
365
366 /*
367 * Read/write routine for a buffer. Finds the proper unit, range checks
368 * arguments, and schedules the transfer. Does not wait for the transfer to
369 * complete. Multi-page transfers are supported. All I/O requests must be a
370 * multiple of a sector in length.
371 */
372 void
373 wdstrategy(bp)
374 struct buf *bp;
375 {
376 struct wd_softc *wd; /* disk unit to do the IO */
377 int unit = WDUNIT(bp->b_dev);
378 int s;
379
380 /* Valid unit, controller, and request? */
381 if (unit >= wdcd.cd_ndevs ||
382 (wd = wdcd.cd_devs[unit]) == 0 ||
383 bp->b_blkno < 0 ||
384 (bp->b_bcount % DEV_BSIZE) != 0 ||
385 (bp->b_bcount / DEV_BSIZE) >= (1 << NBBY)) {
386 bp->b_error = EINVAL;
387 goto bad;
388 }
389
390 #if 0
391 /* "Soft" write protect check. */
392 if ((wd->sc_flags & WDF_WRITEPROT) && (bp->b_flags & B_READ) == 0) {
393 bp->b_error = EROFS;
394 goto bad;
395 }
396 #endif
397
398 /* If it's a null transfer, return immediately. */
399 if (bp->b_bcount == 0)
400 goto done;
401
402 /* Have partitions and want to use them? */
403 if (WDPART(bp->b_dev) != RAW_PART) {
404 if ((wd->sc_flags & WDF_BSDLABEL) == 0) {
405 bp->b_error = EIO;
406 goto bad;
407 }
408 /*
409 * Do bounds checking, adjust transfer. if error, process.
410 * If end of partition, just return.
411 */
412 if (bounds_check_with_label(bp, &wd->sc_dk.dk_label,
413 (wd->sc_flags & WDF_WLABEL) != 0) <= 0)
414 goto done;
415 /* Otherwise, process transfer request. */
416 }
417
418 /* Don't bother doing rotational optimization. */
419 bp->b_cylin = 0;
420
421 /* Queue transfer on drive, activate drive and controller if idle. */
422 s = splbio();
423 disksort(&wd->sc_q, bp);
424 if (!wd->sc_q.b_active)
425 wdstart(wd); /* Start drive. */
426 #ifdef DIAGNOSTIC
427 else {
428 struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
429 if ((wdc->sc_flags & WDCF_ACTIVE) == 0) {
430 printf("wdstrategy: controller inactive\n");
431 wdcstart(wdc);
432 }
433 }
434 #endif
435 splx(s);
436 return;
437
438 bad:
439 bp->b_flags |= B_ERROR;
440 done:
441 /* Toss transfer; we're done early. */
442 biodone(bp);
443 }
444
445 /*
446 * Routine to queue a command to the controller. The unit's request is linked
447 * into the active list for the controller. If the controller is idle, the
448 * transfer is started.
449 */
450 void
451 wdstart(wd)
452 struct wd_softc *wd;
453 {
454 struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
455 int active = wdc->sc_drives.tqh_first != 0;
456
457 /* Link onto controller queue. */
458 wd->sc_q.b_active = 1;
459 TAILQ_INSERT_TAIL(&wdc->sc_drives, wd, sc_drivechain);
460
461 /* If controller not already active, start it. */
462 if (!active)
463 wdcstart(wdc);
464 }
465
466 void
467 wdfinish(wd, bp)
468 struct wd_softc *wd;
469 struct buf *bp;
470 {
471 struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
472
473 #ifdef INSTRUMENT
474 dk_busy &= ~(1 << wd->sc_dev.dv_unit);
475 #endif
476 wdc->sc_flags &= ~(WDCF_SINGLE | WDCF_ERROR);
477 wdc->sc_errors = 0;
478 /*
479 * Move this drive to the end of the queue to give others a `fair'
480 * chance.
481 */
482 if (wd->sc_drivechain.tqe_next) {
483 TAILQ_REMOVE(&wdc->sc_drives, wd, sc_drivechain);
484 if (bp->b_actf) {
485 TAILQ_INSERT_TAIL(&wdc->sc_drives, wd,
486 sc_drivechain);
487 } else
488 wd->sc_q.b_active = 0;
489 }
490 bp->b_resid = wd->sc_bcount;
491 wd->sc_skip = 0;
492 wd->sc_q.b_actf = bp->b_actf;
493 biodone(bp);
494 }
495
496 /*
497 * Controller startup routine. This does the calculation, and starts a
498 * single-sector read or write operation. Called to start a transfer, or from
499 * the interrupt routine to continue a multi-sector transfer.
500 * RESTRICTIONS:
501 * 1. The transfer length must be an exact multiple of the sector size.
502 */
503 static void
504 wdcstart(wdc)
505 struct wdc_softc *wdc;
506 {
507 struct wd_softc *wd; /* disk unit for IO */
508 struct buf *bp;
509 int nblks;
510
511 loop:
512 /* Is there a drive for the controller to do a transfer with? */
513 wd = wdc->sc_drives.tqh_first;
514 if (wd == NULL)
515 return;
516
517 /* Is there a transfer to this drive? If not, deactivate drive. */
518 bp = wd->sc_q.b_actf;
519 if (bp == NULL) {
520 TAILQ_REMOVE(&wdc->sc_drives, wd, sc_drivechain);
521 wd->sc_q.b_active = 0;
522 goto loop;
523 }
524
525 if (wdc->sc_errors >= WDIORETRIES) {
526 wderror(wd, bp, "hard error");
527 bp->b_error = EIO;
528 bp->b_flags |= B_ERROR;
529 wdfinish(wd, bp);
530 goto loop;
531 }
532
533 /* Do control operations specially. */
534 if (wd->sc_state < OPEN) {
535 /*
536 * Actually, we want to be careful not to mess with the control
537 * state if the device is currently busy, but we can assume
538 * that we never get to this point if that's the case.
539 */
540 if (wdcontrol(wd) == 0) {
541 /* The drive is busy. Wait. */
542 return;
543 }
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) != 0)
588 nblks = 1;
589 else if (wd->sc_mode != WDM_DMA)
590 nblks = wd->sc_bcount / DEV_BSIZE;
591 else
592 nblks = min(wd->sc_bcount / DEV_BSIZE, 8);
593
594 /* Check for bad sectors and adjust transfer, if necessary. */
595 if ((lp->d_flags & D_BADSECT) != 0
596 #ifdef B_FORMAT
597 && (bp->b_flags & B_FORMAT) == 0
598 #endif
599 ) {
600 long blkdiff;
601 int i;
602
603 for (i = 0; (blkdiff = wd->sc_badsect[i]) != -1; i++) {
604 blkdiff -= blkno;
605 if (blkdiff < 0)
606 continue;
607 if (blkdiff == 0) {
608 /* Replace current block of transfer. */
609 blkno =
610 lp->d_secperunit - lp->d_nsectors - i - 1;
611 }
612 if (blkdiff < nblks) {
613 /* Bad block inside transfer. */
614 wdc->sc_flags |= WDCF_SINGLE;
615 nblks = 1;
616 }
617 break;
618 }
619 /* Tranfer is okay now. */
620 }
621
622 if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) {
623 sector = (blkno >> 0) & 0xff;
624 cylin = (blkno >> 8) & 0xffff;
625 head = (blkno >> 24) & 0xf;
626 head |= WDSD_LBA;
627 } else {
628 sector = blkno % lp->d_nsectors;
629 sector++; /* Sectors begin with 1, not 0. */
630 blkno /= lp->d_nsectors;
631 head = blkno % lp->d_ntracks;
632 blkno /= lp->d_ntracks;
633 cylin = blkno;
634 head |= WDSD_CHS;
635 }
636
637 #ifdef INSTRUMENT
638 ++dk_seek[wd->sc_dev.dv_unit];
639 ++dk_xfer[wd->sc_dev.dv_unit];
640 #endif
641
642 #ifdef B_FORMAT
643 if (bp->b_flags & B_FORMAT) {
644 sector = lp->d_gap3;
645 nblks = lp->d_nsectors;
646 command = WDCC_FORMAT;
647 } else
648 #endif
649 switch (wd->sc_mode) {
650 case WDM_DMA:
651 command = (bp->b_flags & B_READ) ?
652 WDCC_READDMA : WDCC_WRITEDMA;
653 isa_dmastart(bp->b_flags & B_READ, bp->b_data,
654 nblks * DEV_BSIZE, wdc->sc_drq);
655 break;
656 case WDM_PIOMULTI:
657 command = (bp->b_flags & B_READ) ?
658 WDCC_READMULTI : WDCC_WRITEMULTI;
659 break;
660 case WDM_PIOSINGLE:
661 command = (bp->b_flags & B_READ) ?
662 WDCC_READ : WDCC_WRITE;
663 break;
664 }
665
666 /* Initiate command! */
667 if (wdcommand(wd, command, cylin, head, sector, nblks) != 0) {
668 wderror(wd, NULL,
669 "wdcstart: timeout waiting for unbusy");
670 wdcunwedge(wdc);
671 return;
672 }
673 #ifdef WDDEBUG
674 printf("sector %d cylin %d head %d addr %x sts %x\n", sector,
675 cylin, head, bp->b_data, inb(wd->sc_iobase+wd_altsts));
676 #endif
677 }
678
679 if (wd->sc_mode == WDM_PIOSINGLE ||
680 (wdc->sc_flags & WDCF_SINGLE) != 0)
681 nblks = 1;
682 else if (wd->sc_mode != WDM_DMA)
683 nblks = min(wd->sc_bcount / DEV_BSIZE, wd->sc_multiple);
684 else
685 nblks = min(wd->sc_bcount / DEV_BSIZE, 8);
686 wdc->sc_nblks = nblks;
687
688 /* If this was a write and not using DMA, push the data. */
689 if (wd->sc_mode != WDM_DMA &&
690 (bp->b_flags & B_READ) == 0) {
691 if (wait_for_drq(wdc) < 0) {
692 wderror(wd, NULL, "wdcstart: timeout waiting for drq");
693 wdcunwedge(wdc);
694 return;
695 }
696
697 /* Then send it! */
698 if ((wd->sc_flags & WDF_32BIT) == 0)
699 outsw(wdc->sc_iobase+wd_data,
700 bp->b_data + wd->sc_skip * DEV_BSIZE,
701 nblks * DEV_BSIZE / sizeof(short));
702 else
703 outsl(wdc->sc_iobase+wd_data,
704 bp->b_data + wd->sc_skip * DEV_BSIZE,
705 nblks * DEV_BSIZE / sizeof(long));
706 }
707
708 wdc->sc_flags |= WDCF_ACTIVE;
709 timeout(wdctimeout, wdc, WAITTIME);
710 }
711
712 /*
713 * Interrupt routine for the controller. Acknowledge the interrupt, check for
714 * errors on the current operation, mark it done if necessary, and start the
715 * next request. Also check for a partially done transfer, and continue with
716 * the next chunk if so.
717 */
718 int
719 wdcintr(wdc)
720 struct wdc_softc *wdc;
721 {
722 struct wd_softc *wd;
723 struct buf *bp;
724 int nblks;
725
726 if ((wdc->sc_flags & WDCF_ACTIVE) == 0) {
727 /* Clear the pending interrupt. */
728 (void) inb(wdc->sc_iobase+wd_status);
729 return 0;
730 }
731
732 wdc->sc_flags &= ~WDCF_ACTIVE;
733 untimeout(wdctimeout, wdc);
734
735 wd = wdc->sc_drives.tqh_first;
736 bp = wd->sc_q.b_actf;
737
738 #ifdef WDDEBUG
739 printf("I%d ", ctrlr);
740 #endif
741
742 if (wait_for_unbusy(wdc) < 0) {
743 wderror(wd, NULL, "wdcintr: timeout waiting for unbusy");
744 wdc->sc_status |= WDCS_ERR; /* XXX */
745 }
746
747 /* Is it not a transfer, but a control operation? */
748 if (wd->sc_state < OPEN) {
749 if (wdcontrol(wd) == 0) {
750 /* The drive is busy. Wait. */
751 return 1;
752 }
753 wdcstart(wdc);
754 return 1;
755 }
756
757 nblks = wdc->sc_nblks;
758
759 if (wd->sc_mode == WDM_DMA)
760 isa_dmadone(bp->b_flags & B_READ, bp->b_data,
761 nblks * DEV_BSIZE, wdc->sc_drq);
762
763 /* Have we an error? */
764 if (wdc->sc_status & WDCS_ERR) {
765 lose:
766 #ifdef WDDEBUG
767 wderror(wd, NULL, "wdcintr");
768 #endif
769 if ((wdc->sc_flags & WDCF_SINGLE) == 0) {
770 wdc->sc_flags |= WDCF_ERROR;
771 goto restart;
772 }
773
774 #ifdef B_FORMAT
775 if (bp->b_flags & B_FORMAT)
776 goto bad;
777 #endif
778
779 if (++wdc->sc_errors < WDIORETRIES)
780 goto restart;
781 wderror(wd, bp, "hard error");
782
783 bad:
784 bp->b_error = EIO;
785 bp->b_flags |= B_ERROR;
786 goto done;
787 }
788
789 if (wdc->sc_status & WDCS_CORR)
790 wderror(wd, bp, "soft ecc");
791
792 /* If this was a read and not using DMA, fetch the data. */
793 if (wd->sc_mode != WDM_DMA &&
794 (bp->b_flags & B_READ) != 0) {
795 if ((wdc->sc_status & (WDCS_DRDY | WDCS_DSC | WDCS_DRQ))
796 != (WDCS_DRDY | WDCS_DSC | WDCS_DRQ)) {
797 wderror(wd, NULL, "wdcintr: read intr before drq");
798 wdcunwedge(wdc);
799 return 1;
800 }
801
802 /* Suck in data. */
803 if ((wd->sc_flags & WDF_32BIT) == 0)
804 insw(wdc->sc_iobase+wd_data,
805 bp->b_data + wd->sc_skip * DEV_BSIZE,
806 nblks * DEV_BSIZE / sizeof(short));
807 else
808 insl(wdc->sc_iobase+wd_data,
809 bp->b_data + wd->sc_skip * DEV_BSIZE,
810 nblks * DEV_BSIZE / sizeof(long));
811 }
812
813 /* If we encountered any abnormalities, flag it as a soft error. */
814 if (wdc->sc_errors) {
815 wderror(wd, bp, "soft error");
816 wdc->sc_errors = 0;
817 }
818
819 /* Ready for the next block, if any. */
820 wd->sc_skip += nblks;
821 wd->sc_bcount -= nblks * DEV_BSIZE;
822
823 /* See if more to transfer. */
824 if (wd->sc_bcount > 0)
825 goto restart;
826
827 done:
828 /* Done with this transfer, with or without error. */
829 wdfinish(wd, bp);
830
831 restart:
832 /* Start the next transfer, if any. */
833 wdcstart(wdc);
834
835 return 1;
836 }
837
838 /*
839 * Initialize a drive.
840 */
841 int
842 wdopen(dev, flag, fmt)
843 dev_t dev;
844 int flag, fmt;
845 {
846 int error;
847 int unit, part;
848 struct wd_softc *wd;
849
850 unit = WDUNIT(dev);
851 if (unit >= wdcd.cd_ndevs)
852 return ENXIO;
853 wd = wdcd.cd_devs[unit];
854 if (wd == 0)
855 return ENXIO;
856
857 part = WDPART(dev);
858
859 while ((wd->sc_flags & WDF_LOCKED) != 0) {
860 wd->sc_flags |= WDF_WANTED;
861 if ((error = tsleep(wd, PRIBIO | PCATCH, "wdopn", 0)) != 0)
862 return error;
863 }
864
865 if (wd->sc_dk.dk_openmask != 0) {
866 /*
867 * If any partition is open, but the disk has been invalidated,
868 * disallow further opens.
869 */
870 if ((wd->sc_flags & WDF_LOADED) == 0)
871 return ENXIO;
872 } else {
873 wd->sc_flags |= WDF_LOCKED;
874
875 if ((wd->sc_flags & WDF_LOADED) == 0) {
876 wd->sc_flags &= ~WDF_BSDLABEL;
877 wd->sc_flags |= WDF_LOADED;
878
879 /* Load the physical device parameters. */
880 if (wd_get_parms(wd) != 0) {
881 error = ENXIO;
882 goto bad2;
883 }
884
885 /* Load the partition info if not already loaded. */
886 wdgetdisklabel(wd);
887 }
888
889 wd->sc_flags &= ~WDF_LOCKED;
890 if ((wd->sc_flags & WDF_WANTED) != 0) {
891 wd->sc_flags &= ~WDF_WANTED;
892 wakeup(wd);
893 }
894 }
895
896 /* Check that the partition exists. */
897 if (part != RAW_PART &&
898 (part >= wd->sc_dk.dk_label.d_npartitions ||
899 wd->sc_dk.dk_label.d_partitions[part].p_fstype == FS_UNUSED)) {
900 error = ENXIO;
901 goto bad;
902 }
903
904 /* Insure only one open at a time. */
905 switch (fmt) {
906 case S_IFCHR:
907 wd->sc_dk.dk_copenmask |= (1 << part);
908 break;
909 case S_IFBLK:
910 wd->sc_dk.dk_bopenmask |= (1 << part);
911 break;
912 }
913 wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask;
914
915 return 0;
916
917 bad2:
918 wd->sc_flags &= ~WDF_LOADED;
919
920 bad:
921 if (wd->sc_dk.dk_openmask == 0) {
922 wd->sc_flags &= ~WDF_LOCKED;
923 if ((wd->sc_flags & WDF_WANTED) != 0) {
924 wd->sc_flags &= ~WDF_WANTED;
925 wakeup(wd);
926 }
927 }
928
929 return error;
930 }
931
932 void
933 wdgetdisklabel(wd)
934 struct wd_softc *wd;
935 {
936 char *errstring;
937
938 if ((wd->sc_flags & WDF_BSDLABEL) != 0)
939 return;
940
941 bzero(&wd->sc_dk.dk_label, sizeof(struct disklabel));
942 bzero(&wd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel));
943
944 wd->sc_dk.dk_label.d_secsize = DEV_BSIZE;
945 wd->sc_dk.dk_label.d_ntracks = wd->sc_params.wdp_heads;
946 wd->sc_dk.dk_label.d_nsectors = wd->sc_params.wdp_sectors;
947 wd->sc_dk.dk_label.d_ncylinders = wd->sc_params.wdp_cylinders;
948 wd->sc_dk.dk_label.d_secpercyl =
949 wd->sc_dk.dk_label.d_ntracks * wd->sc_dk.dk_label.d_nsectors;
950
951 #if 0
952 strncpy(wd->sc_dk.dk_label.d_typename, "ST506 disk", 16);
953 wd->sc_dk.dk_label.d_type = DTYPE_ST506;
954 #endif
955 strncpy(wd->sc_dk.dk_label.d_packname, wd->sc_params.wdp_model, 16);
956 wd->sc_dk.dk_label.d_secperunit =
957 wd->sc_dk.dk_label.d_secpercyl * wd->sc_dk.dk_label.d_ncylinders;
958 wd->sc_dk.dk_label.d_rpm = 3600;
959 wd->sc_dk.dk_label.d_interleave = 1;
960 wd->sc_dk.dk_label.d_flags = 0;
961
962 wd->sc_dk.dk_label.d_partitions[RAW_PART].p_offset = 0;
963 wd->sc_dk.dk_label.d_partitions[RAW_PART].p_size =
964 wd->sc_dk.dk_label.d_secperunit *
965 (wd->sc_dk.dk_label.d_secsize / DEV_BSIZE);
966 wd->sc_dk.dk_label.d_partitions[RAW_PART].p_fstype = FS_UNUSED;
967 wd->sc_dk.dk_label.d_npartitions = RAW_PART + 1;
968
969 wd->sc_dk.dk_label.d_magic = DISKMAGIC;
970 wd->sc_dk.dk_label.d_magic2 = DISKMAGIC;
971 wd->sc_dk.dk_label.d_checksum = dkcksum(&wd->sc_dk.dk_label);
972
973 wd->sc_badsect[0] = -1;
974
975 if (wd->sc_state > RECAL)
976 wd->sc_state = RECAL;
977 errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART),
978 wdstrategy, &wd->sc_dk.dk_label, &wd->sc_dk.dk_cpulabel);
979 if (errstring) {
980 /*
981 * This probably happened because the drive's default
982 * geometry doesn't match the DOS geometry. We
983 * assume the DOS geometry is now in the label and try
984 * again. XXX This is a kluge.
985 */
986 if (wd->sc_state > GEOMETRY)
987 wd->sc_state = GEOMETRY;
988 errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART),
989 wdstrategy, &wd->sc_dk.dk_label, &wd->sc_dk.dk_cpulabel);
990 }
991 if (errstring) {
992 printf("%s: %s\n", wd->sc_dev.dv_xname, errstring);
993 return;
994 }
995
996 if (wd->sc_state > GEOMETRY)
997 wd->sc_state = GEOMETRY;
998 if ((wd->sc_dk.dk_label.d_flags & D_BADSECT) != 0)
999 bad144intern(wd);
1000
1001 wd->sc_flags |= WDF_BSDLABEL;
1002 }
1003
1004 /*
1005 * Implement operations other than read/write.
1006 * Called from wdcstart or wdcintr during opens and formats.
1007 * Uses finite-state-machine to track progress of operation in progress.
1008 * Returns 0 if operation still in progress, 1 if completed.
1009 */
1010 static int
1011 wdcontrol(wd)
1012 struct wd_softc *wd;
1013 {
1014 struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
1015
1016 switch (wd->sc_state) {
1017 case RECAL: /* Set SDH, step rate, do recal. */
1018 if (wdcommandshort(wdc, wd->sc_drive, WDCC_RECAL) != 0) {
1019 wderror(wd, NULL, "wdcontrol: recal failed (1)");
1020 goto bad;
1021 }
1022 wd->sc_state = RECAL_WAIT;
1023 break;
1024
1025 case RECAL_WAIT:
1026 if (wdc->sc_status & WDCS_ERR) {
1027 wderror(wd, NULL, "wdcontrol: recal failed (2)");
1028 goto bad;
1029 }
1030 /* fall through */
1031 case GEOMETRY:
1032 if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0)
1033 goto multimode;
1034 if (wdsetctlr(wd) != 0) {
1035 /* Already printed a message. */
1036 goto bad;
1037 }
1038 wd->sc_state = GEOMETRY_WAIT;
1039 break;
1040
1041 case GEOMETRY_WAIT:
1042 if (wdc->sc_status & WDCS_ERR) {
1043 wderror(wd, NULL, "wdcontrol: geometry failed");
1044 goto bad;
1045 }
1046 /* fall through */
1047 case MULTIMODE:
1048 multimode:
1049 if (wd->sc_mode != WDM_PIOMULTI)
1050 goto open;
1051 outb(wdc->sc_iobase+wd_seccnt, wd->sc_multiple);
1052 if (wdcommandshort(wdc, wd->sc_drive, WDCC_SETMULTI) != 0) {
1053 wderror(wd, NULL, "wdcontrol: setmulti failed (1)");
1054 goto bad;
1055 }
1056 wd->sc_state = MULTIMODE_WAIT;
1057 break;
1058
1059 case MULTIMODE_WAIT:
1060 if (wdc->sc_status & WDCS_ERR) {
1061 wderror(wd, NULL, "wdcontrol: setmulti failed (2)");
1062 goto bad;
1063 }
1064 /* fall through */
1065 case OPEN:
1066 open:
1067 wdc->sc_errors = 0;
1068 wd->sc_state = OPEN;
1069 /*
1070 * The rest of the initialization can be done by normal means.
1071 */
1072 return 1;
1073
1074 bad:
1075 wdcunwedge(wdc);
1076 return 0;
1077 }
1078
1079 wdc->sc_flags |= WDCF_ACTIVE;
1080 timeout(wdctimeout, wdc, WAITTIME);
1081 return 0;
1082 }
1083
1084 /*
1085 * Send a command and wait uninterruptibly until controller is finished.
1086 * Return -1 if controller busy for too long, otherwise return non-zero if
1087 * error. Intended for brief controller commands at critical points.
1088 * Assumes interrupts are blocked.
1089 */
1090 static int
1091 wdcommand(wd, command, cylin, head, sector, count)
1092 struct wd_softc *wd;
1093 int command;
1094 int cylin, head, sector, count;
1095 {
1096 struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
1097 int iobase = wdc->sc_iobase;
1098 int stat;
1099
1100 /* Select drive, head, and addressing mode. */
1101 outb(iobase+wd_sdh, WDSD_IBM | (wd->sc_drive << 4) | head);
1102
1103 /* Wait for it to become ready to accept a command. */
1104 if (command == WDCC_IDP)
1105 stat = wait_for_unbusy(wdc);
1106 else
1107 stat = wdcwait(wdc, WDCS_DRDY);
1108 if (stat < 0)
1109 return -1;
1110
1111 /* Load parameters. */
1112 if (wd->sc_dk.dk_label.d_type == DTYPE_ST506)
1113 outb(iobase+wd_precomp, wd->sc_dk.dk_label.d_precompcyl / 4);
1114 else
1115 outb(iobase+wd_features, 0);
1116 outb(iobase+wd_cyl_lo, cylin);
1117 outb(iobase+wd_cyl_hi, cylin >> 8);
1118 outb(iobase+wd_sector, sector);
1119 outb(iobase+wd_seccnt, count);
1120
1121 /* Send command. */
1122 outb(iobase+wd_command, command);
1123
1124 return 0;
1125 }
1126
1127 int
1128 wdcommandshort(wdc, drive, command)
1129 struct wdc_softc *wdc;
1130 int drive;
1131 int command;
1132 {
1133 int iobase = wdc->sc_iobase;
1134
1135 /* Select drive. */
1136 outb(iobase+wd_sdh, WDSD_IBM | (drive << 4));
1137
1138 if (wdcwait(wdc, WDCS_DRDY) < 0)
1139 return -1;
1140
1141 outb(iobase+wd_command, command);
1142
1143 return 0;
1144 }
1145
1146 /*
1147 * Issue IDP to drive to tell it just what geometry it is to be.
1148 */
1149 static int
1150 wdsetctlr(wd)
1151 struct wd_softc *wd;
1152 {
1153 struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
1154
1155 #ifdef WDDEBUG
1156 printf("wd(%d,%d) C%dH%dS%d\n", wd->sc_dev.dv_unit, wd->sc_drive,
1157 wd->sc_dk.dk_label.d_ncylinders, wd->sc_dk.dk_label.d_ntracks,
1158 wd->sc_dk.dk_label.d_nsectors);
1159 #endif
1160
1161 if (wdcommand(wd, WDCC_IDP, wd->sc_dk.dk_label.d_ncylinders,
1162 wd->sc_dk.dk_label.d_ntracks - 1, 0, wd->sc_dk.dk_label.d_nsectors)
1163 != 0) {
1164 wderror(wd, NULL, "wdsetctlr: geometry upload failed");
1165 return -1;
1166 }
1167
1168 return 0;
1169 }
1170
1171 /*
1172 * Issue IDENTIFY to drive to ask it what it is.
1173 */
1174 int
1175 wd_get_parms(wd)
1176 struct wd_softc *wd;
1177 {
1178 struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent;
1179 int i;
1180 char tb[DEV_BSIZE];
1181
1182 if (wdcommandshort(wdc, wd->sc_drive, WDCC_IDENTIFY) != 0 ||
1183 wait_for_drq(wdc) != 0) {
1184 /*
1185 * We `know' there's a drive here; just assume it's old.
1186 */
1187 strncpy(wd->sc_dk.dk_label.d_typename, "ST506",
1188 sizeof wd->sc_dk.dk_label.d_typename);
1189 wd->sc_dk.dk_label.d_type = DTYPE_ST506;
1190
1191 strncpy(wd->sc_params.wdp_model, "unknown",
1192 sizeof wd->sc_params.wdp_model);
1193 wd->sc_params.wdp_config = WD_CFG_FIXED;
1194 wd->sc_params.wdp_cylinders = 1024;
1195 wd->sc_params.wdp_heads = 8;
1196 wd->sc_params.wdp_sectors = 17;
1197 wd->sc_params.wdp_maxmulti = 0;
1198 wd->sc_params.wdp_usedmovsd = 0;
1199 wd->sc_params.wdp_capabilities = 0;
1200 } else {
1201 /* Obtain parameters. */
1202 insw(wdc->sc_iobase+wd_data, tb, sizeof(tb) / sizeof(short));
1203 bcopy(tb, &wd->sc_params, sizeof(struct wdparams));
1204
1205 /* Shuffle string byte order. */
1206 for (i = 0; i < sizeof(wd->sc_params.wdp_model); i += 2) {
1207 u_short *p;
1208 p = (u_short *)(wd->sc_params.wdp_model + i);
1209 *p = ntohs(*p);
1210 }
1211
1212 strncpy(wd->sc_dk.dk_label.d_typename, "ESDI/IDE",
1213 sizeof wd->sc_dk.dk_label.d_typename);
1214 wd->sc_dk.dk_label.d_type = DTYPE_ESDI;
1215 }
1216
1217 #if 0
1218 printf("gc %x cyl %d trk %d sec %d type %d sz %d model %s\n",
1219 wp->wdp_config, wp->wdp_cylinders, wp->wdp_heads, wp->wdp_sectors,
1220 wp->wdp_buftype, wp->wdp_bufsize, wp->wdp_model);
1221 #endif
1222
1223 /* XXX sometimes possibly needed */
1224 (void) inb(wdc->sc_iobase+wd_status);
1225
1226 return 0;
1227 }
1228
1229 int
1230 wdclose(dev, flag, fmt)
1231 dev_t dev;
1232 int flag, fmt;
1233 {
1234 struct wd_softc *wd = wdcd.cd_devs[WDUNIT(dev)];
1235 int part = WDPART(dev);
1236 int s;
1237
1238 switch (fmt) {
1239 case S_IFCHR:
1240 wd->sc_dk.dk_copenmask &= ~(1 << part);
1241 break;
1242 case S_IFBLK:
1243 wd->sc_dk.dk_bopenmask &= ~(1 << part);
1244 break;
1245 }
1246 wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask;
1247
1248 if (wd->sc_dk.dk_openmask == 0) {
1249 wd->sc_flags |= WDF_LOCKED;
1250
1251 #if 0
1252 s = splbio();
1253 while (...) {
1254 wd->sc_flags |= WDF_WAITING;
1255 if ((error = tsleep(wd, PRIBIO | PCATCH, "wdcls", 0)) != 0)
1256 return error;
1257 }
1258 splx(s);
1259 #endif
1260
1261 wd->sc_flags &= ~WDF_LOCKED;
1262 if ((wd->sc_flags & WDF_WANTED) != 0) {
1263 wd->sc_flags &= WDF_WANTED;
1264 wakeup(wd);
1265 }
1266 }
1267
1268 return 0;
1269 }
1270
1271 int
1272 wdioctl(dev, command, addr, flag, p)
1273 dev_t dev;
1274 u_long command;
1275 caddr_t addr;
1276 int flag;
1277 struct proc *p;
1278 {
1279 struct wd_softc *wd = wdcd.cd_devs[WDUNIT(dev)];
1280 int error;
1281
1282 if ((wd->sc_flags & WDF_LOADED) == 0)
1283 return EIO;
1284
1285 switch (command) {
1286 case DIOCSBAD:
1287 if ((flag & FWRITE) == 0)
1288 return EBADF;
1289 wd->sc_dk.dk_cpulabel.bad = *(struct dkbad *)addr;
1290 wd->sc_dk.dk_label.d_flags |= D_BADSECT;
1291 bad144intern(wd);
1292 return 0;
1293
1294 case DIOCGDINFO:
1295 *(struct disklabel *)addr = wd->sc_dk.dk_label;
1296 return 0;
1297
1298 case DIOCGPART:
1299 ((struct partinfo *)addr)->disklab = &wd->sc_dk.dk_label;
1300 ((struct partinfo *)addr)->part =
1301 &wd->sc_dk.dk_label.d_partitions[WDPART(dev)];
1302 return 0;
1303
1304 case DIOCSDINFO:
1305 if ((flag & FWRITE) == 0)
1306 return EBADF;
1307 error = setdisklabel(&wd->sc_dk.dk_label,
1308 (struct disklabel *)addr,
1309 /*(wd->sc_flags & WDF_BSDLABEL) ? wd->sc_dk.dk_openmask : */0,
1310 &wd->sc_dk.dk_cpulabel);
1311 if (error == 0) {
1312 wd->sc_flags |= WDF_BSDLABEL;
1313 if (wd->sc_state > GEOMETRY)
1314 wd->sc_state = GEOMETRY;
1315 }
1316 return error;
1317
1318 case DIOCWLABEL:
1319 if ((flag & FWRITE) == 0)
1320 return EBADF;
1321 if (*(int *)addr)
1322 wd->sc_flags |= WDF_WLABEL;
1323 else
1324 wd->sc_flags &= ~WDF_WLABEL;
1325 return 0;
1326
1327 case DIOCWDINFO:
1328 if ((flag & FWRITE) == 0)
1329 return EBADF;
1330 error = setdisklabel(&wd->sc_dk.dk_label,
1331 (struct disklabel *)addr,
1332 /*(wd->sc_flags & WDF_BSDLABEL) ? wd->sc_dk.dk_openmask : */0,
1333 &wd->sc_dk.dk_cpulabel);
1334 if (error == 0) {
1335 wd->sc_flags |= WDF_BSDLABEL;
1336 if (wd->sc_state > GEOMETRY)
1337 wd->sc_state = GEOMETRY;
1338
1339 /* Simulate opening partition 0 so write succeeds. */
1340 wd->sc_dk.dk_openmask |= (1 << 0); /* XXX */
1341 error = writedisklabel(WDLABELDEV(dev), wdstrategy,
1342 &wd->sc_dk.dk_label, &wd->sc_dk.dk_cpulabel);
1343 wd->sc_dk.dk_openmask =
1344 wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask;
1345 }
1346 return error;
1347
1348 #ifdef notyet
1349 case DIOCGDINFOP:
1350 *(struct disklabel **)addr = &wd->sc_dk.dk_label;
1351 return 0;
1352
1353 case DIOCWFORMAT:
1354 if ((flag & FWRITE) == 0)
1355 return EBADF;
1356 {
1357 register struct format_op *fop;
1358 struct iovec aiov;
1359 struct uio auio;
1360
1361 fop = (struct format_op *)addr;
1362 aiov.iov_base = fop->df_buf;
1363 aiov.iov_len = fop->df_count;
1364 auio.uio_iov = &aiov;
1365 auio.uio_iovcnt = 1;
1366 auio.uio_resid = fop->df_count;
1367 auio.uio_segflg = 0;
1368 auio.uio_offset =
1369 fop->df_startblk * wd->sc_dk.dk_label.d_secsize;
1370 auio.uio_procp = p;
1371 error = physio(wdformat, NULL, dev, B_WRITE, minphys,
1372 &auio);
1373 fop->df_count -= auio.uio_resid;
1374 fop->df_reg[0] = wdc->sc_status;
1375 fop->df_reg[1] = wdc->sc_error;
1376 return error;
1377 }
1378 #endif
1379
1380 default:
1381 return ENOTTY;
1382 }
1383
1384 #ifdef DIAGNOSTIC
1385 panic("wdioctl: impossible");
1386 #endif
1387 }
1388
1389 #ifdef B_FORMAT
1390 int
1391 wdformat(struct buf *bp)
1392 {
1393
1394 bp->b_flags |= B_FORMAT;
1395 return wdstrategy(bp);
1396 }
1397 #endif
1398
1399 int
1400 wdsize(dev)
1401 dev_t dev;
1402 {
1403 struct wd_softc *wd;
1404 int part;
1405 int size;
1406
1407 if (wdopen(dev, 0, S_IFBLK) != 0)
1408 return -1;
1409 wd = wdcd.cd_devs[WDUNIT(dev)];
1410 part = WDPART(dev);
1411 if ((wd->sc_flags & WDF_BSDLABEL) == 0 ||
1412 wd->sc_dk.dk_label.d_partitions[part].p_fstype != FS_SWAP)
1413 size = -1;
1414 else
1415 size = wd->sc_dk.dk_label.d_partitions[part].p_size;
1416 if (wdclose(dev, 0, S_IFBLK) != 0)
1417 return -1;
1418 return size;
1419 }
1420
1421 /*
1422 * Dump core after a system crash.
1423 */
1424 int
1425 wddump(dev)
1426 dev_t dev;
1427 {
1428 struct wd_softc *wd; /* disk unit to do the IO */
1429 struct wdc_softc *wdc;
1430 struct disklabel *lp;
1431 int unit, part;
1432 long rblkno, nblks;
1433 char *addr;
1434 static wddoingadump = 0;
1435 extern caddr_t CADDR1;
1436 extern pt_entry_t *CMAP1;
1437
1438 if (wddoingadump)
1439 return EFAULT;
1440 wddoingadump = 1;
1441
1442 unit = WDUNIT(dev);
1443 /* Check for acceptable drive number. */
1444 if (unit >= wdcd.cd_ndevs)
1445 return ENXIO;
1446 wd = wdcd.cd_devs[unit];
1447 /* Was it ever initialized? */
1448 if (wd == 0 || wd->sc_state < OPEN)
1449 return ENXIO;
1450
1451 wdc = (void *)wd->sc_dev.dv_parent;
1452 addr = (char *)0; /* starting address */
1453 lp = &wd->sc_dk.dk_label;
1454 part = WDPART(dev);
1455
1456 /* Convert to disk sectors. */
1457 rblkno = lp->d_partitions[part].p_offset + dumplo;
1458 nblks = min(ctob(physmem) / lp->d_secsize,
1459 lp->d_partitions[part].p_size - dumplo);
1460
1461 /* Check transfer bounds against partition size. */
1462 if (dumplo < 0 || nblks <= 0)
1463 return EINVAL;
1464
1465 /* Recalibrate. */
1466 if (wdcommandshort(wdc, wd->sc_drive, WDCC_RECAL) != 0 ||
1467 wait_for_ready(wdc) != 0 || wdsetctlr(wd) != 0 ||
1468 wait_for_ready(wdc) != 0) {
1469 wderror(wd, NULL, "wddump: recal failed");
1470 return EIO;
1471 }
1472
1473 while (nblks > 0) {
1474 long blkno;
1475 long cylin, head, sector;
1476
1477 blkno = rblkno;
1478
1479 if ((lp->d_flags & D_BADSECT) != 0) {
1480 long blkdiff;
1481 int i;
1482
1483 for (i = 0; (blkdiff = wd->sc_badsect[i]) != -1; i++) {
1484 blkdiff -= blkno;
1485 if (blkdiff < 0)
1486 continue;
1487 if (blkdiff == 0) {
1488 /* Replace current block of transfer. */
1489 blkno =
1490 lp->d_secperunit - lp->d_nsectors - i - 1;
1491 }
1492 break;
1493 }
1494 /* Tranfer is okay now. */
1495 }
1496
1497 if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) {
1498 sector = (blkno >> 0) & 0xff;
1499 cylin = (blkno >> 8) & 0xffff;
1500 head = (blkno >> 24) & 0xf;
1501 head |= WDSD_LBA;
1502 } else {
1503 sector = blkno % lp->d_nsectors;
1504 sector++; /* Sectors begin with 1, not 0. */
1505 blkno /= lp->d_nsectors;
1506 head = blkno % lp->d_ntracks;
1507 blkno /= lp->d_ntracks;
1508 cylin = blkno;
1509 head |= WDSD_CHS;
1510 }
1511
1512 #ifdef notdef
1513 /* Let's just talk about this first. */
1514 printf("cylin %d, head %d, sector %d, addr 0x%x", cylin, head,
1515 sector, addr);
1516 #endif
1517 if (wdcommand(wd, WDCC_WRITE, cylin, head, sector, 1) != 0 ||
1518 wait_for_drq(wdc) != 0) {
1519 wderror(wd, NULL, "wddump: write failed");
1520 return EIO;
1521 }
1522
1523 #ifdef notdef /* Cannot use this since this address was mapped differently. */
1524 pmap_enter(kernel_pmap, CADDR1, trunc_page(addr), VM_PROT_READ, TRUE);
1525 #else
1526 *CMAP1 = PG_V | PG_KW | ctob((long)addr);
1527 tlbflush();
1528 #endif
1529
1530 outsw(wdc->sc_iobase+wd_data, CADDR1 + ((int)addr & PGOFSET),
1531 DEV_BSIZE / sizeof(short));
1532
1533 /* Check data request (should be done). */
1534 if (wait_for_ready(wdc) != 0) {
1535 wderror(wd, NULL, "wddump: timeout waiting for ready");
1536 return EIO;
1537 }
1538 if (wdc->sc_status & WDCS_DRQ) {
1539 wderror(wd, NULL, "wddump: extra drq");
1540 return EIO;
1541 }
1542
1543 if ((unsigned)addr % 1048576 == 0)
1544 printf("%d ", nblks / (1048576 / DEV_BSIZE));
1545
1546 /* Update block count. */
1547 nblks--;
1548 rblkno++;
1549 (int)addr += DEV_BSIZE;
1550 }
1551
1552 return 0;
1553 }
1554
1555 /*
1556 * Internalize the bad sector table.
1557 */
1558 void
1559 bad144intern(wd)
1560 struct wd_softc *wd;
1561 {
1562 struct dkbad *bt = &wd->sc_dk.dk_cpulabel.bad;
1563 struct disklabel *lp = &wd->sc_dk.dk_label;
1564 int i = 0;
1565
1566 for (; i < 126; i++) {
1567 if (bt->bt_bad[i].bt_cyl == 0xffff)
1568 break;
1569 wd->sc_badsect[i] =
1570 bt->bt_bad[i].bt_cyl * lp->d_secpercyl +
1571 (bt->bt_bad[i].bt_trksec >> 8) * lp->d_nsectors +
1572 (bt->bt_bad[i].bt_trksec & 0xff);
1573 }
1574 for (; i < 127; i++)
1575 wd->sc_badsect[i] = -1;
1576 }
1577
1578 static int
1579 wdcreset(wdc)
1580 struct wdc_softc *wdc;
1581 {
1582 int iobase = wdc->sc_iobase;
1583
1584 /* Reset the device. */
1585 outb(iobase+wd_ctlr, WDCTL_RST | WDCTL_IDS);
1586 delay(1000);
1587 outb(iobase+wd_ctlr, WDCTL_IDS);
1588 delay(1000);
1589 (void) inb(iobase+wd_error);
1590 outb(iobase+wd_ctlr, WDCTL_4BIT);
1591
1592 if (wait_for_unbusy(wdc) < 0) {
1593 printf("%s: reset failed\n", wdc->sc_dev.dv_xname);
1594 return 1;
1595 }
1596
1597 return 0;
1598 }
1599
1600 static void
1601 wdcrestart(arg)
1602 void *arg;
1603 {
1604 struct wdc_softc *wdc = (struct wdc_softc *)arg;
1605 int s;
1606
1607 s = splbio();
1608 wdcstart(wdc);
1609 splx(s);
1610 }
1611
1612 /*
1613 * Unwedge the controller after an unexpected error. We do this by resetting
1614 * it, marking all drives for recalibration, and stalling the queue for a short
1615 * period to give the reset time to finish.
1616 * NOTE: We use a timeout here, so this routine must not be called during
1617 * autoconfig or dump.
1618 */
1619 static void
1620 wdcunwedge(wdc)
1621 struct wdc_softc *wdc;
1622 {
1623 int unit;
1624
1625 untimeout(wdctimeout, wdc);
1626 (void) wdcreset(wdc);
1627
1628 /* Schedule recalibrate for all drives on this controller. */
1629 for (unit = 0; unit < wdcd.cd_ndevs; unit++) {
1630 struct wd_softc *wd = wdcd.cd_devs[unit];
1631 if (!wd || (void *)wd->sc_dev.dv_parent != wdc)
1632 continue;
1633 if (wd->sc_state > RECAL)
1634 wd->sc_state = RECAL;
1635 }
1636
1637 wdc->sc_flags |= WDCF_ERROR;
1638 ++wdc->sc_errors;
1639
1640 /* Wake up in a little bit and restart the operation. */
1641 timeout(wdcrestart, wdc, RECOVERYTIME);
1642 }
1643
1644 int
1645 wdcwait(wdc, mask)
1646 struct wdc_softc *wdc;
1647 int mask;
1648 {
1649 int iobase = wdc->sc_iobase;
1650 int timeout = 0;
1651 u_char status;
1652 extern int cold;
1653
1654 for (;;) {
1655 wdc->sc_status = status = inb(iobase+wd_status);
1656 if ((status & WDCS_BSY) == 0 && (status & mask) == mask)
1657 break;
1658 if (++timeout > WDCNDELAY)
1659 return -1;
1660 delay(WDCDELAY);
1661 }
1662 if (status & WDCS_ERR) {
1663 wdc->sc_error = inb(iobase+wd_error);
1664 return WDCS_ERR;
1665 }
1666 #ifdef WDCNDELAY_DEBUG
1667 /* After autoconfig, there should be no long delays. */
1668 if (!cold && timeout > WDCNDELAY_DEBUG)
1669 printf("%s: warning: busy-wait took %dus\n",
1670 wdc->sc_dev.dv_xname, WDCDELAY * timeout);
1671 #endif
1672 return 0;
1673 }
1674
1675 static void
1676 wdctimeout(arg)
1677 void *arg;
1678 {
1679 struct wdc_softc *wdc = (struct wdc_softc *)arg;
1680 int s;
1681
1682 s = splbio();
1683 wderror(wdc, NULL, "lost interrupt");
1684 wdcunwedge(wdc);
1685 splx(s);
1686 }
1687
1688 static void
1689 wderror(dev, bp, msg)
1690 void *dev;
1691 struct buf *bp;
1692 char *msg;
1693 {
1694 struct wd_softc *wd = dev;
1695 struct wdc_softc *wdc = dev;
1696
1697 if (bp) {
1698 diskerr(bp, "wd", msg, LOG_PRINTF, wd->sc_skip,
1699 &wd->sc_dk.dk_label);
1700 printf("\n");
1701 } else
1702 printf("%s: %s: status %b error %b\n", wdc->sc_dev.dv_xname,
1703 msg, wdc->sc_status, WDCS_BITS, wdc->sc_error, WDERR_BITS);
1704 }
1705