From: bde@kralizec.zeta.org.au (Bruce Evans)
[unix-history] / sys / i386 / isa / wd.c
CommitLineData
80d17733 1#define WD_COUNT_RETRIES
6d2dcc99
NW
2static int wdtest = 0;
3
15637ed4
RG
4/*-
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * William Jolitz.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
80d17733
NW
39 * from: @(#)wd.c 7.2 (Berkeley) 5/9/91
40 * $Id: wd.c,v 1.11 1993/10/16 13:46:31 rgrimes Exp $
15637ed4
RG
41 */
42
80d17733
NW
43/* TODO:
44 * o Fix timeout from 2 to about 4 seconds.
45 * o Bump error count after timeout.
46 * o Satisfy ATA timing in all cases.
47 * o Finish merging berry/sos timeout code (bump error count...).
48 * o Merge/fix TIH/NetBSD bad144 code.
49 * o Merge/fix Dyson/NetBSD clustering code.
50 * o Don't use polling except for initialization. Need to
51 * reorganize the state machine. Then "extra" interrupts
52 * shouldn't happen (except maybe one for initialization).
53 * o Fix disklabel, boot and driver inconsistencies with
54 * bad144 in standard versions.
55 * o Support extended DOS partitions.
56 * o Support swapping to DOS partitions.
57 * o Look at latest linux clustering methods. Our disksort()
58 * gets in the way of clustering.
59 * o Handle bad sectors, clustering, disklabelling, DOS
60 * partitions and swapping driver-independently. Use
61 * i386/dkbad.c for bad sectors. Swapping will need new
62 * driver entries for polled reinit and polled write).
63 */
15637ed4
RG
64
65#include "wd.h"
6d2dcc99 66#if NWDC > 0
15637ed4
RG
67
68#include "param.h"
69#include "dkbad.h"
70#include "systm.h"
fde1aeb2 71#include "kernel.h"
15637ed4
RG
72#include "conf.h"
73#include "file.h"
74#include "stat.h"
75#include "ioctl.h"
76#include "disklabel.h"
77#include "buf.h"
78#include "uio.h"
79#include "malloc.h"
80#include "machine/cpu.h"
7530c2f0 81#include "i386/isa/isa.h"
15637ed4 82#include "i386/isa/isa_device.h"
15637ed4
RG
83#include "i386/isa/wdreg.h"
84#include "syslog.h"
85#include "vm/vm.h"
86
15637ed4 87#ifndef WDCTIMEOUT
4f55eff0 88#define WDCTIMEOUT 10000000 /* arbitrary timeout for drive ready waits */
15637ed4
RG
89#endif
90
91#define RETRIES 5 /* number of retries before giving up */
6d2dcc99 92#define RECOVERYTIME 500000 /* usec for controller to recover after err */
80d17733 93#define MAXTRANSFER 256 /* max size of transfer in sectors */
15637ed4 94
80d17733 95#ifdef notyet
15637ed4 96#define wdnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */
80d17733 97#endif
15637ed4
RG
98#define wddospart(dev) (minor(dev) & 0x40) /* use dos partitions */
99#define wdunit(dev) ((minor(dev) & 0x38) >> 3)
100#define wdpart(dev) (minor(dev) & 0x7)
101#define makewddev(maj, unit, part) (makedev(maj,((unit<<3)+part)))
102#define WDRAW 3 /* 'd' partition isn't a partition! */
103
80d17733
NW
104/* Cylinder number for doing IO to. Shares an entry in the buf struct. */
105#define b_cylin b_resid
15637ed4 106
80d17733
NW
107/*
108 * This biotab field doubles as a field for the physical unit number on
109 * the controller.
110 */
111#define id_physid id_scsiid
6d2dcc99 112
15637ed4
RG
113/*
114 * Drive states. Used to initialize drive.
115 */
116
80d17733
NW
117#define CLOSED 0 /* disk is closed. */
118#define WANTOPEN 1 /* open requested, not started */
119#define RECAL 2 /* doing restore */
120#define OPEN 3 /* done with open */
15637ed4
RG
121
122/*
123 * The structure of a disk drive.
124 */
80d17733 125struct disk {
15637ed4
RG
126 long dk_bc; /* byte count left */
127 short dk_skip; /* blocks already transferred */
6d2dcc99 128 char dk_ctrlr; /* physical controller number */
15637ed4 129 char dk_unit; /* physical unit number */
6d2dcc99 130 char dk_lunit; /* logical unit number */
15637ed4
RG
131 char dk_state; /* control state */
132 u_char dk_status; /* copy of status reg. */
133 u_char dk_error; /* copy of error reg. */
80d17733 134 u_char dk_timeout; /* countdown to next timeout */
15637ed4 135 short dk_port; /* i/o port base */
6d2dcc99 136
80d17733
NW
137 u_long dk_copenpart; /* character units open on this drive */
138 u_long dk_bopenpart; /* block units open on this drive */
139 u_long dk_openpart; /* all units open on this drive */
15637ed4
RG
140 short dk_wlabel; /* label writable? */
141 short dk_flags; /* drive characteistics found */
80d17733
NW
142#define DKFL_DOSPART 0x00001 /* has DOS partition table */
143#define DKFL_SINGLE 0x00004 /* sector at a time mode */
144#define DKFL_ERROR 0x00008 /* processing a disk error */
145#define DKFL_BSDLABEL 0x00010 /* has a BSD disk label */
146#define DKFL_BADSECT 0x00020 /* has a bad144 badsector table */
147#define DKFL_WRITEPROT 0x00040 /* manual unit write protect */
148#define DKFL_LABELLING 0x00080 /* readdisklabel() in progress */
15637ed4
RG
149 struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */
150 struct disklabel dk_dd; /* device configuration data */
80d17733
NW
151 struct disklabel dk_dd2; /* DOS view converted to label */
152 struct dos_partition
15637ed4 153 dk_dospartitions[NDOSPART]; /* DOS view of disk */
80d17733 154 struct dkbad dk_bad; /* bad sector table */
15637ed4
RG
155};
156
80d17733
NW
157static struct disk *wddrives[NWD]; /* table of units */
158static struct buf wdtab[NWDC];
159static struct buf wdutab[NWD]; /* head of queue per drive */
6d2dcc99 160#ifdef notyet
80d17733 161static struct buf rwdbuf[NWD]; /* buffers for raw IO */
15637ed4 162#endif
80d17733
NW
163static long wdxfer[NWD]; /* count of transfers */
164
165static int wdprobe(struct isa_device *dvp);
166static int wdattach(struct isa_device *dvp);
167static void wdustart(struct disk *du);
168static void wdstart(int ctrlr);
169static int wdcontrol(struct buf *bp);
170static int wdcommand(struct disk *du, u_int cylinder, u_int head,
171 u_int sector, u_int count, u_int command);
172static int wdsetctlr(struct disk *du);
173static int wdwsetctlr(struct disk *du);
174static int wdgetctlr(struct disk *du);
175static void wderror(struct buf *bp, struct disk *du, char *mesg);
176static void wdflushirq(struct disk *du, int old_ipl);
177static int wdreset(struct disk *du);
178static void wdsleep(int ctrlr, char *wmesg);
179static int wdtimeout(caddr_t cdu, int ticks);
180static int wdunwedge(struct disk *du);
181static int wdwait(struct disk *du, u_char bits_wanted);
182
183struct isa_driver wdcdriver = {
6d2dcc99 184 wdprobe, wdattach, "wdc",
15637ed4
RG
185};
186
15637ed4
RG
187/*
188 * Probe for controller.
189 */
6d2dcc99 190static int
15637ed4
RG
191wdprobe(struct isa_device *dvp)
192{
80d17733 193 int unit = dvp->id_unit;
15637ed4 194 struct disk *du;
15637ed4 195
80d17733
NW
196 if (unit >= NWDC)
197 return (0);
198 du = malloc(sizeof *du, M_TEMP, M_NOWAIT);
199 if (du == NULL)
200 return (0);
201 bzero(du, sizeof *du);
6d2dcc99 202 du->dk_ctrlr = dvp->id_unit;
80d17733 203 du->dk_port = dvp->id_iobase;
6d2dcc99 204
15637ed4 205 /* check if we have registers that work */
80d17733
NW
206 outb(du->dk_port + wd_cyl_lo, 0xa5); /* wd_cyl_lo is read/write */
207 if (inb(du->dk_port + wd_cyl_lo) != 0xa5)
15637ed4
RG
208 goto nodevice;
209
6d2dcc99
NW
210 if (wdreset(du) != 0 && (DELAY(RECOVERYTIME), wdreset(du)) != 0)
211 goto nodevice;
15637ed4
RG
212
213 /* execute a controller only command */
6d2dcc99
NW
214 if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0
215 || wdwait(du, 0) != 0)
15637ed4
RG
216 goto nodevice;
217
6d2dcc99 218 free(du, M_TEMP);
7530c2f0 219 return (IO_WDCSIZE);
15637ed4
RG
220
221nodevice:
222 free(du, M_TEMP);
15637ed4
RG
223 return (0);
224}
225
226/*
227 * Attach each drive if possible.
228 */
6d2dcc99 229static int
15637ed4
RG
230wdattach(struct isa_device *dvp)
231{
80d17733 232 int unit, lunit;
6d2dcc99
NW
233 struct isa_device *wdup;
234 struct disk *du;
235
236 if (dvp->id_unit >= NWDC)
80d17733 237 return (0);
6d2dcc99
NW
238
239 for (wdup = isa_biotab_wdc; wdup->id_driver != 0; wdup++) {
240 if (wdup->id_iobase != dvp->id_iobase)
241 continue;
242 lunit = wdup->id_unit;
243 if (lunit >= NWD)
244 continue;
245 unit = wdup->id_physid;
246
80d17733
NW
247 du = malloc(sizeof *du, M_TEMP, M_NOWAIT);
248 if (du == NULL)
249 continue;
250 if (wddrives[lunit] != NULL)
251 panic("drive attached twice");
252 wddrives[lunit] = du;
253 bzero(du, sizeof *du);
6d2dcc99
NW
254 du->dk_ctrlr = dvp->id_unit;
255 du->dk_unit = unit;
256 du->dk_lunit = lunit;
257 du->dk_port = dvp->id_iobase;
15637ed4 258
80d17733
NW
259 /*
260 * Print out description of drive, suppressing multiple
261 * blanks.
262 */
263 if (wdgetctlr(du) == 0) {
264 int i, blank;
6d2dcc99 265
80d17733
NW
266 printf("wdc%d: unit %d (wd%d): ",
267 dvp->id_unit, unit, lunit);
268 if (du->dk_params.wdp_heads == 0)
269 printf("size unknown");
270 else
271 printf("%luMB %lu cyl, %lu head, %lu sec",
272 du->dk_dd.d_secperunit
273 * du->dk_dd.d_secsize / (1024 * 1024),
274 du->dk_dd.d_ncylinders,
275 du->dk_dd.d_ntracks,
276 du->dk_dd.d_nsectors);
277 printf(", type ");
278 for (i = blank = 0; i < sizeof(du->dk_params.wdp_model); i++) {
279 char c = du->dk_params.wdp_model[i];
15637ed4 280
6d2dcc99
NW
281 if (blank && c == ' ')
282 continue;
15637ed4
RG
283 if (blank && c != ' ') {
284 printf(" %c", c);
285 blank = 0;
286 continue;
287 }
288 if (c == ' ')
289 blank = 1;
290 else
291 printf("%c", c);
292 }
80d17733
NW
293 printf("\n");
294
295 /*
296 * Start timeout routine for this drive.
297 * XXX timeout should be per controller.
298 */
299 wdtimeout((caddr_t)du, 0);
300 } else {
6d2dcc99 301 free(du, M_TEMP);
80d17733 302 wddrives[lunit] = NULL;
15637ed4
RG
303 }
304 }
80d17733
NW
305
306 /*
307 * Discard any interrupts generated by wdgetctlr(). wdflushirq()
308 * doesn't work now because the ambient ipl is too high.
309 */
310 wdtab[dvp->id_unit].b_active = 2;
311
312 return (1);
15637ed4
RG
313}
314
315/* Read/write routine for a buffer. Finds the proper unit, range checks
316 * arguments, and schedules the transfer. Does not wait for the transfer
317 * to complete. Multi-page transfers are supported. All I/O requests must
318 * be a multiple of a sector in length.
319 */
4c45483e 320void
15637ed4
RG
321wdstrategy(register struct buf *bp)
322{
323 register struct buf *dp;
80d17733 324 struct disk *du;
6d2dcc99 325 int lunit = wdunit(bp->b_dev);
15637ed4
RG
326 int s;
327
328 /* valid unit, controller, and request? */
80d17733 329 if (lunit >= NWD || bp->b_blkno < 0 || (du = wddrives[lunit]) == NULL) {
15637ed4
RG
330
331 bp->b_error = EINVAL;
332 bp->b_flags |= B_ERROR;
333 goto done;
334 }
335
336 /* "soft" write protect check */
337 if ((du->dk_flags & DKFL_WRITEPROT) && (bp->b_flags & B_READ) == 0) {
338 bp->b_error = EROFS;
339 bp->b_flags |= B_ERROR;
340 goto done;
341 }
342
80d17733
NW
343 /*
344 * Do bounds checking, adjust transfer, and set b_cylin.
345 */
346 if (bounds_check_with_label(bp, wddospart(bp->b_dev)
347 ? &du->dk_dd2 : &du->dk_dd,
348 du->dk_wlabel) <= 0)
349 goto done;
15637ed4 350
15637ed4 351 /* queue transfer on drive, activate drive and controller if idle */
6d2dcc99 352 dp = &wdutab[lunit];
15637ed4
RG
353 s = splbio();
354 disksort(dp, bp);
355 if (dp->b_active == 0)
80d17733 356 wdustart(du); /* start drive */
6d2dcc99
NW
357
358 /* Pick up changes made by readdisklabel(). */
359 if (du->dk_flags & DKFL_LABELLING && du->dk_state > RECAL) {
360 wdsleep(du->dk_ctrlr, "wdlab");
361 du->dk_state = WANTOPEN;
362 }
363
364 if (wdtab[du->dk_ctrlr].b_active == 0)
80d17733 365 wdstart(du->dk_ctrlr); /* start controller */
15637ed4
RG
366 splx(s);
367 return;
368
369done:
370 /* toss transfer, we're done early */
371 biodone(bp);
372}
373
374/*
375 * Routine to queue a command to the controller. The unit's
376 * request is linked into the active list for the controller.
377 * If the controller is idle, the transfer is started.
378 */
379static void
380wdustart(register struct disk *du)
381{
6d2dcc99 382 register struct buf *bp, *dp = &wdutab[du->dk_lunit];
80d17733 383 int ctrlr = du->dk_ctrlr;
15637ed4
RG
384
385 /* unit already active? */
386 if (dp->b_active)
387 return;
388
389 /* anything to start? */
390 bp = dp->b_actf;
391 if (bp == NULL)
6d2dcc99 392 return;
15637ed4
RG
393
394 /* link onto controller queue */
395 dp->b_forw = NULL;
80d17733 396 if (wdtab[ctrlr].b_actf == NULL)
6d2dcc99 397 wdtab[ctrlr].b_actf = dp;
15637ed4 398 else
6d2dcc99
NW
399 wdtab[ctrlr].b_actl->b_forw = dp;
400 wdtab[ctrlr].b_actl = dp;
15637ed4
RG
401
402 /* mark the drive unit as busy */
403 dp->b_active = 1;
404}
405
406/*
407 * Controller startup routine. This does the calculation, and starts
408 * a single-sector read or write operation. Called to start a transfer,
409 * or from the interrupt routine to continue a multi-sector transfer.
410 * RESTRICTIONS:
6d2dcc99 411 * 1. The transfer length must be an exact multiple of the sector size.
15637ed4
RG
412 */
413
414static void
6d2dcc99 415wdstart(int ctrlr)
15637ed4 416{
80d17733 417 register struct disk *du;
15637ed4
RG
418 register struct buf *bp;
419 struct disklabel *lp;
420 struct buf *dp;
421 register struct bt_bad *bt_ptr;
6d2dcc99 422 long blknum, cylin, head, sector;
80d17733
NW
423 long secpertrk, secpercyl;
424 int lunit;
15637ed4
RG
425
426loop:
427 /* is there a drive for the controller to do a transfer with? */
6d2dcc99 428 dp = wdtab[ctrlr].b_actf;
15637ed4
RG
429 if (dp == NULL)
430 return;
431
80d17733
NW
432 /*
433 * Is there a transfer to this drive? If so, link it on the
434 * controller's queue.
435 */
15637ed4
RG
436 bp = dp->b_actf;
437 if (bp == NULL) {
6d2dcc99 438 wdtab[ctrlr].b_actf = dp->b_forw;
15637ed4
RG
439 goto loop;
440 }
441
442 /* obtain controller and drive information */
6d2dcc99
NW
443 lunit = wdunit(bp->b_dev);
444 du = wddrives[lunit];
15637ed4
RG
445
446 /* if not really a transfer, do control operations specially */
447 if (du->dk_state < OPEN) {
6d2dcc99
NW
448 if (du->dk_state != WANTOPEN)
449 printf("wd%d: wdstart: weird dk_state %d\n",
80d17733 450 du->dk_lunit, du->dk_state);
6d2dcc99
NW
451 if (wdcontrol(bp) != 0)
452 printf("wd%d: wdstart: wdcontrol returned nonzero, state = %d\n",
80d17733 453 du->dk_lunit, du->dk_state);
15637ed4
RG
454 return;
455 }
456
457 /* calculate transfer details */
458 blknum = bp->b_blkno + du->dk_skip;
6d2dcc99 459#ifdef WDDEBUG
15637ed4 460 if (du->dk_skip == 0)
6d2dcc99 461 printf("wd%d: wdstart: %s %d@%d; map ", lunit,
80d17733
NW
462 (bp->b_flags & B_READ) ? "read" : "write",
463 bp->b_bcount, blknum);
15637ed4 464 else
80d17733 465 printf(" %d)%x", du->dk_skip, inb(du->dk_port + wd_altsts));
15637ed4 466#endif
15637ed4
RG
467 if (du->dk_skip == 0)
468 du->dk_bc = bp->b_bcount;
469
470 lp = &du->dk_dd;
471 secpertrk = lp->d_nsectors;
472 secpercyl = lp->d_secpercyl;
80d17733
NW
473 if (wddospart(bp->b_dev))
474 blknum += du->dk_dd2.d_partitions[wdpart(bp->b_dev)].p_offset;
475 else
15637ed4
RG
476 blknum += lp->d_partitions[wdpart(bp->b_dev)].p_offset;
477 cylin = blknum / secpercyl;
478 head = (blknum % secpercyl) / secpertrk;
479 sector = blknum % secpertrk;
480
481 /*
482 * See if the current block is in the bad block list.
483 * (If we have one, and not formatting.)
484 */
80d17733
NW
485 if ((du->dk_flags & (DKFL_SINGLE | DKFL_BADSECT))
486 == (DKFL_SINGLE | DKFL_BADSECT))
487#define BAD144_NO_CYL 0xffff /* XXX should be in dkbad.h; bad144.c uses -1 */
488 for (bt_ptr = du->dk_bad.bt_bad; bt_ptr->bt_cyl != BAD144_NO_CYL;
6d2dcc99 489 bt_ptr++) {
15637ed4
RG
490 if (bt_ptr->bt_cyl > cylin)
491 /* Sorted list, and we passed our cylinder. quit. */
492 break;
493 if (bt_ptr->bt_cyl == cylin &&
6d2dcc99 494 bt_ptr->bt_trksec == (head << 8) + sector) {
15637ed4 495 /*
80d17733 496 * Found bad block. Calculate new block number.
15637ed4
RG
497 * This starts at the end of the disk (skip the
498 * last track which is used for the bad block list),
499 * and works backwards to the front of the disk.
500 */
80d17733
NW
501#ifdef WDDEBUG
502 printf("--- badblock code -> Old = %ld; ", blknum);
15637ed4 503#endif
6d2dcc99
NW
504
505 /*
506 * XXX the offset of the bad sector table ought
507 * to be stored in the in-core copy of the table.
508 */
509#define BAD144_PART 2 /* XXX scattered magic numbers */
510#define BSD_PART 0 /* XXX should be 2 but bad144.c uses 0 */
511 if (lp->d_partitions[BSD_PART].p_offset != 0)
512 blknum = lp->d_partitions[BAD144_PART].p_offset
513 + lp->d_partitions[BAD144_PART].p_size;
514 else
515 blknum = lp->d_secperunit;
516 blknum -= lp->d_nsectors + (bt_ptr - du->dk_bad.bt_bad)
517 + 1;
518
15637ed4
RG
519 cylin = blknum / secpercyl;
520 head = (blknum % secpercyl) / secpertrk;
521 sector = blknum % secpertrk;
80d17733
NW
522#ifdef WDDEBUG
523 printf("new = %ld\n", blknum);
15637ed4
RG
524#endif
525 break;
526 }
527 }
15637ed4 528
80d17733 529 wdtab[ctrlr].b_active = 1; /* mark controller active */
15637ed4 530
15637ed4
RG
531 /* if starting a multisector transfer, or doing single transfers */
532 if (du->dk_skip == 0 || (du->dk_flags & DKFL_SINGLE)) {
80d17733
NW
533 u_int command;
534 u_int count;
15637ed4 535
6d2dcc99
NW
536 if (wdtab[ctrlr].b_errcnt && (bp->b_flags & B_READ) == 0)
537 du->dk_bc += DEV_BSIZE;
90af2aa0 538
80d17733 539#ifdef B_FORMAT
15637ed4 540 if (bp->b_flags & B_FORMAT) {
6d2dcc99
NW
541 command = WDCC_FORMAT;
542 count = lp->d_nsectors;
543 sector = lp->d_gap3 - 1; /* + 1 later */
544 } else
15637ed4 545#endif
6d2dcc99 546 {
80d17733
NW
547 if (du->dk_flags & DKFL_SINGLE)
548 count = 1;
549 else
550 count = howmany(du->dk_bc, DEV_BSIZE);
551 command = (bp->b_flags & B_READ)
552 ? WDCC_READ : WDCC_WRITE;
15637ed4 553 }
90af2aa0 554
6d2dcc99
NW
555 /*
556 * XXX this loop may never terminate. The code to handle
80d17733
NW
557 * counting down of retries and eventually failing the i/o
558 * is in wdintr() and we can't get there from here.
6d2dcc99
NW
559 */
560 if (wdtest != 0) {
561 if (--wdtest == 0) {
562 wdtest = 100;
563 printf("dummy wdunwedge\n");
564 wdunwedge(du);
565 }
566 }
80d17733
NW
567 while (wdcommand(du, cylin, head, sector, count, command)
568 != 0) {
6d2dcc99 569 wderror(bp, du,
80d17733 570 "wdstart: timeout waiting to give command");
6d2dcc99 571 wdunwedge(du);
4035d7c4 572 }
80d17733
NW
573#ifdef WDDEBUG
574 printf("cylin %ld head %ld sector %ld addr %x sts %x\n",
575 cylin, head, sector,
576 (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE,
577 inb(du->dk_port + wd_altsts));
15637ed4
RG
578#endif
579 }
580
80d17733
NW
581 /*
582 * Schedule wdtimeout() to wake up after a few seconds. Retrying
583 * unmarked bad blocks can take 3 seconds! Then it is not good that
584 * we retry 5 times.
585 *
586 * XXX wdtimeout() doesn't increment the error count so we may loop
587 * forever. More seriously, the loop isn't forever but causes a
588 * crash.
589 *
590 * TODO fix b_resid bug elsewhere (fd.c....). Fix short but positive
591 * counts being discarded after there is an error (in physio I
592 * think). Discarding them would be OK if the (special) file offset
593 * was not advanced.
594 */
595 du->dk_timeout = 1 + 1;
596
597 /* If this is a read operation, just go away until it's done. */
598 if (bp->b_flags & B_READ)
599 return;
15637ed4 600
80d17733
NW
601 /* Ready to send data? */
602 if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ) < 0) {
6d2dcc99
NW
603 wderror(bp, du, "wdstart: timeout waiting for DRQ");
604 /*
605 * XXX what do we do now? If we've just issued the command,
606 * then we can treat this failure the same as a command
607 * failure. But if we are continuing a multi-sector write,
608 * the command was issued ages ago, so we can't simply
609 * restart it.
610 *
80d17733
NW
611 * XXX we waste a lot of time unnecessarily translating block
612 * numbers to cylin/head/sector for continued i/o's.
6d2dcc99 613 */
15637ed4
RG
614 }
615
616 /* then send it! */
80d17733
NW
617 outsw(du->dk_port + wd_data,
618 (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE,
619 DEV_BSIZE / sizeof(short));
15637ed4
RG
620 du->dk_bc -= DEV_BSIZE;
621}
622
623/* Interrupt routine for the controller. Acknowledge the interrupt, check for
624 * errors on the current operation, mark it done if necessary, and start
625 * the next request. Also check for a partially done transfer, and
626 * continue with the next chunk if so.
627 */
628void
6d2dcc99 629wdintr(int unit)
15637ed4
RG
630{
631 register struct disk *du;
632 register struct buf *bp, *dp;
15637ed4 633
80d17733
NW
634 if (wdtab[unit].b_active == 2)
635 return; /* intr in wdflushirq() */
6d2dcc99
NW
636 if (!wdtab[unit].b_active) {
637 printf("wdc%d: extra interrupt\n", unit);
15637ed4
RG
638 return;
639 }
640
6d2dcc99 641 dp = wdtab[unit].b_actf;
15637ed4
RG
642 bp = dp->b_actf;
643 du = wddrives[wdunit(bp->b_dev)];
80d17733 644 du->dk_timeout = 0;
15637ed4 645
6d2dcc99
NW
646 if (wdwait(du, 0) < 0) {
647 wderror(bp, du, "wdintr: timeout waiting for status");
648 du->dk_status |= WDCS_ERR; /* XXX */
649 }
15637ed4
RG
650
651 /* is it not a transfer, but a control operation? */
652 if (du->dk_state < OPEN) {
6d2dcc99 653 wdtab[unit].b_active = 0;
15637ed4 654 if (wdcontrol(bp))
6d2dcc99 655 wdstart(unit);
15637ed4
RG
656 return;
657 }
658
659 /* have we an error? */
6d2dcc99
NW
660 if (du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) {
661oops:
80d17733 662#ifdef WDDEBUG
6d2dcc99 663 wderror(bp, du, "wdintr");
15637ed4 664#endif
80d17733
NW
665 if ((du->dk_flags & DKFL_SINGLE) == 0) {
666 du->dk_flags |= DKFL_ERROR;
15637ed4
RG
667 goto outt;
668 }
669#ifdef B_FORMAT
670 if (bp->b_flags & B_FORMAT) {
80d17733 671 bp->b_error = EIO;
15637ed4
RG
672 bp->b_flags |= B_ERROR;
673 goto done;
674 }
675#endif
6d2dcc99 676
15637ed4 677 /* error or error correction? */
6d2dcc99
NW
678 if (du->dk_status & WDCS_ERR) {
679 if (++wdtab[unit].b_errcnt < RETRIES) {
680 wdtab[unit].b_active = 0;
15637ed4 681 } else {
6d2dcc99 682 wderror(bp, du, "hard error");
80d17733 683 bp->b_error = EIO;
15637ed4
RG
684 bp->b_flags |= B_ERROR; /* flag the error */
685 }
6d2dcc99
NW
686 } else
687 wderror(bp, du, "soft ecc");
15637ed4 688 }
15637ed4
RG
689
690 /*
691 * If this was a successful read operation, fetch the data.
692 */
80d17733
NW
693 if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ)
694 && wdtab[unit].b_active) {
695 int chk, dummy;
15637ed4
RG
696
697 chk = min(DEV_BSIZE / sizeof(short), du->dk_bc / sizeof(short));
698
699 /* ready to receive data? */
6d2dcc99
NW
700 if ((du->dk_status & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ))
701 != (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ))
702 wderror(bp, du, "wdintr: read intr arrived early");
703 if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ) != 0) {
704 wderror(bp, du, "wdintr: read error detected late");
705 goto oops;
706 }
15637ed4
RG
707
708 /* suck in data */
80d17733
NW
709 insw(du->dk_port + wd_data,
710 (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE, chk);
15637ed4
RG
711 du->dk_bc -= chk * sizeof(short);
712
80d17733
NW
713 /* XXX for obsolete fractional sector reads. */
714 while (chk++ < DEV_BSIZE / sizeof(short))
715 insw(du->dk_port + wd_data, &dummy, 1);
15637ed4
RG
716 }
717
6d2dcc99
NW
718 wdxfer[du->dk_lunit]++;
719outt:
720 if (wdtab[unit].b_active) {
15637ed4 721 if ((bp->b_flags & B_ERROR) == 0) {
80d17733 722 du->dk_skip++; /* add to successful sectors */
6d2dcc99
NW
723 if (wdtab[unit].b_errcnt)
724 wderror(bp, du, "soft error");
725 wdtab[unit].b_errcnt = 0;
15637ed4
RG
726
727 /* see if more to transfer */
728 if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) {
6d2dcc99
NW
729 wdtab[unit].b_active = 0;
730 wdstart(unit);
80d17733
NW
731 return; /* next chunk is started */
732 } else if ((du->dk_flags & (DKFL_SINGLE | DKFL_ERROR))
733 == DKFL_ERROR) {
15637ed4
RG
734 du->dk_skip = 0;
735 du->dk_flags &= ~DKFL_ERROR;
80d17733 736 du->dk_flags |= DKFL_SINGLE;
6d2dcc99
NW
737 wdtab[unit].b_active = 0;
738 wdstart(unit);
80d17733 739 return; /* redo xfer sector by sector */
15637ed4
RG
740 }
741 }
6d2dcc99
NW
742#ifdef B_FORMAT
743done: ;
744#endif
15637ed4
RG
745 /* done with this transfer, with or without error */
746 du->dk_flags &= ~DKFL_SINGLE;
6d2dcc99
NW
747 wdtab[unit].b_actf = dp->b_forw;
748 wdtab[unit].b_errcnt = 0;
80d17733 749 bp->b_resid = bp->b_bcount - du->dk_skip * DEV_BSIZE;
15637ed4
RG
750 du->dk_skip = 0;
751 dp->b_active = 0;
752 dp->b_actf = bp->av_forw;
753 dp->b_errcnt = 0;
15637ed4
RG
754 biodone(bp);
755 }
756
757 /* controller idle */
6d2dcc99 758 wdtab[unit].b_active = 0;
15637ed4
RG
759
760 /* anything more on drive queue? */
761 if (dp->b_actf)
762 wdustart(du);
763 /* anything more for controller to do? */
6d2dcc99
NW
764 if (wdtab[unit].b_actf)
765 wdstart(unit);
15637ed4
RG
766}
767
768/*
769 * Initialize a drive.
770 */
771int
772wdopen(dev_t dev, int flags, int fmt, struct proc *p)
773{
6d2dcc99 774 register unsigned int lunit;
15637ed4 775 register struct disk *du;
80d17733 776 int part = wdpart(dev), mask = 1 << part;
6d2dcc99 777 struct partition *pp;
80d17733 778 char *msg;
6d2dcc99 779 struct disklabel save_label;
15637ed4 780
6d2dcc99 781 lunit = wdunit(dev);
80d17733
NW
782 if (lunit >= NWD)
783 return (ENXIO);
6d2dcc99 784 du = wddrives[lunit];
80d17733
NW
785 if (du == NULL)
786 return (ENXIO);
787
788 /* Finish flushing IRQs left over from wdattach(). */
789 if (wdtab[du->dk_ctrlr].b_active == 2)
790 wdtab[du->dk_ctrlr].b_active = 0;
791
792 /*
793 * That's all for valid DOS partitions. We don't need a BSD label.
794 * The openmask is only used for checking BSD partitions so we don't
795 * need to maintain it.
796 */
797 if (wddospart(dev)) {
798 /* XXX we do need a disklabel for now. */
799 if ((du->dk_flags & DKFL_BSDLABEL) == 0)
800 return (ENXIO);
801
802 return (part > NDOSPART ? ENXIO : 0);
803 }
15637ed4 804
6d2dcc99
NW
805 while (du->dk_flags & DKFL_LABELLING)
806 tsleep((caddr_t)&du->dk_flags, PZERO - 1, "wdopen", 1);
15637ed4 807 if ((du->dk_flags & DKFL_BSDLABEL) == 0) {
4035d7c4 808 /*
80d17733
NW
809 * wdtab[ctrlr].b_active != 0 implies
810 * wdutab[lunit].b_actf == NULL (?)
6d2dcc99
NW
811 * so the following guards most things (until the next i/o).
812 * It doesn't guard against a new i/o starting and being
813 * affected by the label being changed. Sigh.
4035d7c4 814 */
6d2dcc99
NW
815 wdsleep(du->dk_ctrlr, "wdopn1");
816
817 du->dk_flags |= DKFL_LABELLING | DKFL_WRITEPROT;
90af2aa0 818 du->dk_state = WANTOPEN;
6d2dcc99 819 wdutab[lunit].b_actf = NULL;
90af2aa0 820
6d2dcc99
NW
821 /*
822 * Read label using WDRAW partition.
823 *
824 * If the drive has an MBR, then the current geometry (from
825 * wdgetctlr()) is used to read it; then the BIOS/DOS
826 * geometry is inferred and used to read the label off the
827 * 'c' partition. Otherwise the label is read using the
828 * current geometry. The label gives the final geometry.
829 * If bad sector handling is enabled, then this geometry
830 * is used to read the bad sector table. The geometry
831 * changes occur inside readdisklabel() and are propagated
832 * to the driver by resetting the state machine.
833 */
834 save_label = du->dk_dd;
80d17733 835#define WDSTRATEGY ((int (*)(struct buf *)) wdstrategy) /* XXX */
6d2dcc99
NW
836 msg = readdisklabel(makewddev(major(dev), lunit, WDRAW),
837 WDSTRATEGY, &du->dk_dd,
838 du->dk_dospartitions, &du->dk_bad,
839 (struct buf **)NULL);
840 du->dk_flags &= ~DKFL_LABELLING;
841 if (msg != NULL) {
842 du->dk_dd = save_label;
3419be7d 843 log(LOG_WARNING, "wd%d: cannot find label (%s)\n",
6d2dcc99 844 lunit, msg);
3419be7d 845 if (part != WDRAW)
6d2dcc99 846 return (EINVAL); /* XXX needs translation */
15637ed4 847 } else {
80d17733
NW
848 int dospart;
849 unsigned long newsize, offset, size;
850
15637ed4
RG
851 du->dk_flags |= DKFL_BSDLABEL;
852 du->dk_flags &= ~DKFL_WRITEPROT;
853 if (du->dk_dd.d_flags & D_BADSECT)
854 du->dk_flags |= DKFL_BADSECT;
80d17733
NW
855
856 /*
857 * Force WDRAW partition to be the whole disk.
858 */
859 offset = du->dk_dd.d_partitions[WDRAW].p_offset;
860 if (offset != 0) {
861 printf(
862 "wd%d: changing offset of 'd' partition from %lu to 0\n",
863 du->dk_lunit, offset);
864 du->dk_dd.d_partitions[WDRAW].p_offset = 0;
865 }
866 size = du->dk_dd.d_partitions[WDRAW].p_size;
867 newsize = du->dk_dd.d_secperunit; /* XXX */
868 if (size != newsize) {
869 printf(
870 "wd%d: changing size of 'd' partition from %lu to %lu\n",
871 du->dk_lunit, size, newsize);
872 du->dk_dd.d_partitions[WDRAW].p_size = newsize;
873 }
874
875 /*
876 * Convert DOS partition data to a label.
877 */
878 du->dk_dd2 = du->dk_dd;
879 bzero(du->dk_dd2.d_partitions,
880 sizeof du->dk_dd2.d_partitions);
881 du->dk_dd2.d_partitions[0].p_size
882 = du->dk_dd.d_secperunit; /* XXX */
883 for (dospart = 1; dospart <= NDOSPART; dospart++) {
884 du->dk_dd2.d_partitions[dospart].p_offset =
885 du->dk_dospartitions[dospart - 1].dp_start;
886 du->dk_dd2.d_partitions[dospart].p_size =
887 du->dk_dospartitions[dospart - 1].dp_size;
888 }
15637ed4
RG
889 }
890
6d2dcc99
NW
891 /* Pick up changes made by readdisklabel(). */
892 wdsleep(du->dk_ctrlr, "wdopn2");
893 du->dk_state = WANTOPEN;
90af2aa0 894 }
6d2dcc99
NW
895
896 /*
80d17733
NW
897 * Warn if a partion is opened that overlaps another partition which
898 * is open unless one is the "raw" partition (whole disk).
6d2dcc99 899 */
80d17733 900 if ((du->dk_openpart & mask) == 0 && part != WDRAW) {
15637ed4
RG
901 int start, end;
902
6d2dcc99
NW
903 pp = &du->dk_dd.d_partitions[part];
904 start = pp->p_offset;
905 end = pp->p_offset + pp->p_size;
906 for (pp = du->dk_dd.d_partitions;
907 pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
908 pp++) {
909 if (pp->p_offset + pp->p_size <= start ||
80d17733 910 pp->p_offset >= end)
6d2dcc99
NW
911 continue;
912 if (pp - du->dk_dd.d_partitions == WDRAW)
913 continue;
80d17733
NW
914 if (du->dk_openpart
915 & (1 << (pp - du->dk_dd.d_partitions)))
6d2dcc99
NW
916 log(LOG_WARNING,
917 "wd%d%c: overlaps open partition (%c)\n",
918 lunit, part + 'a',
919 pp - du->dk_dd.d_partitions + 'a');
920 }
921 }
922 if (part >= du->dk_dd.d_npartitions && part != WDRAW)
923 return (ENXIO);
15637ed4 924
6d2dcc99
NW
925 switch (fmt) {
926 case S_IFCHR:
927 du->dk_copenpart |= mask;
928 break;
929 case S_IFBLK:
930 du->dk_bopenpart |= mask;
931 break;
932 }
80d17733
NW
933 du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
934
15637ed4
RG
935 return (0);
936}
937
938/*
939 * Implement operations other than read/write.
940 * Called from wdstart or wdintr during opens and formats.
941 * Uses finite-state-machine to track progress of operation in progress.
942 * Returns 0 if operation still in progress, 1 if completed.
943 */
944static int
945wdcontrol(register struct buf *bp)
946{
947 register struct disk *du;
80d17733 948 int ctrlr;
15637ed4
RG
949
950 du = wddrives[wdunit(bp->b_dev)];
6d2dcc99 951 ctrlr = du->dk_ctrlr;
90af2aa0 952
6d2dcc99 953 switch (du->dk_state) {
80d17733 954 case WANTOPEN:
6d2dcc99
NW
955tryagainrecal:
956 wdtab[ctrlr].b_active = 1;
957 if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0) {
958 wderror(bp, du, "wdcontrol: wdcommand failed");
959 goto maybe_retry;
960 }
961 du->dk_state = RECAL;
80d17733 962 return (0);
15637ed4 963 case RECAL:
6d2dcc99
NW
964 if (du->dk_status & WDCS_ERR || wdsetctlr(du) != 0) {
965 wderror(bp, du, "wdcontrol: recal failed");
966maybe_retry:
967 if (du->dk_status & WDCS_ERR)
968 wdunwedge(du);
80d17733
NW
969 du->dk_state = WANTOPEN;
970 if (++wdtab[ctrlr].b_errcnt < RETRIES)
15637ed4 971 goto tryagainrecal;
15637ed4 972 bp->b_error = ENXIO; /* XXX needs translation */
6d2dcc99 973 bp->b_flags |= B_ERROR;
80d17733 974 return (1);
15637ed4 975 }
6d2dcc99 976 wdtab[ctrlr].b_errcnt = 0;
15637ed4
RG
977 du->dk_state = OPEN;
978 /*
80d17733
NW
979 * The rest of the initialization can be done by normal
980 * means.
15637ed4 981 */
80d17733 982 return (1);
15637ed4 983 }
6d2dcc99
NW
984 panic("wdcontrol");
985 return (1);
15637ed4
RG
986}
987
988/*
6d2dcc99
NW
989 * Wait uninterruptibly until controller is not busy, then send it a command.
990 * The wait usually terminates immediately because we waited for the previous
991 * command to terminate.
15637ed4
RG
992 */
993static int
6d2dcc99
NW
994wdcommand(struct disk *du, u_int cylinder, u_int head, u_int sector,
995 u_int count, u_int command)
58324b60 996{
80d17733 997 u_int wdc;
15637ed4 998
6d2dcc99
NW
999 if (wdwait(du, 0) < 0)
1000 return (1);
15637ed4 1001 wdc = du->dk_port;
6d2dcc99
NW
1002 outb(wdc + wd_precomp, du->dk_dd.d_precompcyl / 4);
1003 outb(wdc + wd_cyl_lo, cylinder);
1004 outb(wdc + wd_cyl_hi, cylinder >> 8);
80d17733 1005 outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit << 4) | head);
6d2dcc99
NW
1006 outb(wdc + wd_sector, sector + 1);
1007 outb(wdc + wd_seccnt, count);
80d17733
NW
1008 if (wdwait(du, command == WDCC_DIAGNOSE || command == WDCC_IDC
1009 ? 0 : WDCS_READY) < 0)
1010 return (1);
1011 outb(wdc + wd_command, command);
6d2dcc99 1012 return (0);
15637ed4
RG
1013}
1014
1015/*
1016 * issue IDC to drive to tell it just what geometry it is to be.
1017 */
1018static int
6d2dcc99
NW
1019wdsetctlr(struct disk *du)
1020{
80d17733
NW
1021#ifdef WDDEBUG
1022 printf("wd(%d,%d): wdsetctlr: C %lu H %lu S %lu\n",
1023 du->dk_ctrlr, du->dk_unit,
1024 du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks,
1025 du->dk_dd.d_nsectors);
6d2dcc99 1026#endif
80d17733
NW
1027 if (du->dk_dd.d_ntracks > 16) {
1028 printf("wd%d: cannot handle %lu heads (max 16)\n",
1029 du->dk_lunit, du->dk_dd.d_ntracks);
1030 return (1);
1031 }
6d2dcc99
NW
1032 if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0,
1033 du->dk_dd.d_nsectors, WDCC_IDC) != 0
1034 || wdwait(du, WDCS_READY) != 0) {
1035 wderror((struct buf *)NULL, du, "wdsetctlr failed");
1036 return (1);
1037 }
1038 return (0);
1039}
15637ed4 1040
6d2dcc99
NW
1041/*
1042 * Wait until driver is inactive, then set up controller.
1043 */
1044static int
1045wdwsetctlr(struct disk *du)
1046{
80d17733
NW
1047 int stat;
1048 int x;
15637ed4 1049
6d2dcc99 1050 wdsleep(du->dk_ctrlr, "wdwset");
15637ed4 1051 x = splbio();
6d2dcc99 1052 stat = wdsetctlr(du);
80d17733 1053 wdflushirq(du, x);
15637ed4 1054 splx(x);
6d2dcc99 1055 return (stat);
15637ed4
RG
1056}
1057
1058/*
1059 * issue READP to drive to ask it what it is.
1060 */
1061static int
80d17733
NW
1062wdgetctlr(struct disk *du)
1063{
1064 int i;
1065 char tb[DEV_BSIZE];
15637ed4
RG
1066 struct wdparams *wp;
1067
6d2dcc99 1068 if (wdcommand(du, 0, 0, 0, 0, WDCC_READP) != 0
80d17733
NW
1069 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ) < 0) {
1070 /* XXX need to check error status after final transfer. */
6d2dcc99 1071 /*
80d17733
NW
1072 * Old drives don't support WDCC_READP. Try a seek to 0.
1073 * Some IDE controllers return trash if there is no drive
1074 * attached, so first test that the drive can be selected.
1075 * This also avoids long waits for nonexistent drives.
6d2dcc99 1076 */
80d17733
NW
1077 if (wdwait(du, 0) < 0)
1078 return (1);
1079 outb(du->dk_port + wd_sdh, WDSD_IBM | (du->dk_unit << 4));
1080 DELAY(5000); /* usually unnecessary; drive select is fast */
1081 if ((inb(du->dk_port + wd_status) & (WDCS_BUSY | WDCS_READY))
1082 != WDCS_READY
1083 || wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0
1084 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) != 0)
6d2dcc99 1085 return (1);
6d2dcc99 1086
80d17733
NW
1087 /*
1088 * Fake minimal drive geometry for reading the MBR.
1089 * readdisklabel() may enlarge it to read the label and the
1090 * bad sector table.
1091 */
6d2dcc99
NW
1092 du->dk_dd.d_secsize = DEV_BSIZE;
1093 du->dk_dd.d_nsectors = 17;
1094 du->dk_dd.d_ntracks = 1;
1095 du->dk_dd.d_ncylinders = 1;
1096 du->dk_dd.d_secpercyl = 17;
80d17733
NW
1097 du->dk_dd.d_secperunit = 17;
1098
1099 /*
1100 * Fake maximal drive size for writing the label.
1101 */
1102 du->dk_dd.d_partitions[WDRAW].p_size = 64 * 16 * 1024;
3419be7d 1103
6d2dcc99
NW
1104 /*
1105 * Fake some more of the label for printing by disklabel(1)
1106 * in case there is no real label.
1107 */
1108 du->dk_dd.d_type = DTYPE_ST506;
1109 du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
1110 strncpy(du->dk_dd.d_typename, "Fake geometry",
3419be7d 1111 sizeof du->dk_dd.d_typename);
6d2dcc99
NW
1112
1113 /* Fake the model name for printing by wdattach(). */
80d17733 1114 strncpy(du->dk_params.wdp_model, "unknown",
3419be7d 1115 sizeof du->dk_params.wdp_model);
6d2dcc99
NW
1116
1117 return (0);
15637ed4
RG
1118 }
1119
1120 /* obtain parameters */
1121 wp = &du->dk_params;
80d17733 1122 insw(du->dk_port + wd_data, tb, sizeof(tb) / sizeof(short));
15637ed4
RG
1123 bcopy(tb, wp, sizeof(struct wdparams));
1124
1125 /* shuffle string byte order */
80d17733 1126 for (i = 0; i < sizeof(wp->wdp_model); i += 2) {
15637ed4 1127 u_short *p;
80d17733 1128
15637ed4
RG
1129 p = (u_short *) (wp->wdp_model + i);
1130 *p = ntohs(*p);
1131 }
80d17733 1132#ifdef WDDEBUG
6d2dcc99 1133 printf(
80d17733
NW
1134"\nwd(%d,%d): wdgetctlr: gc %x cyl %d trk %d sec %d type %d sz %d model %s\n",
1135 du->dk_ctrlr, du->dk_unit, wp->wdp_config,
1136 wp->wdp_fixedcyl + wp->wdp_removcyl, wp->wdp_heads,
1137 wp->wdp_sectors, wp->wdp_cntype, wp->wdp_cnsbsz,
1138 wp->wdp_model);
6d2dcc99 1139#endif
15637ed4
RG
1140
1141 /* update disklabel given drive information */
6d2dcc99 1142 du->dk_dd.d_secsize = DEV_BSIZE;
80d17733 1143 du->dk_dd.d_ncylinders = wp->wdp_fixedcyl + wp->wdp_removcyl /*+- 1*/ ;
15637ed4
RG
1144 du->dk_dd.d_ntracks = wp->wdp_heads;
1145 du->dk_dd.d_nsectors = wp->wdp_sectors;
1146 du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors;
80d17733
NW
1147 du->dk_dd.d_partitions[WDRAW].p_size = du->dk_dd.d_secperunit
1148 = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders;
15637ed4
RG
1149 /* dubious ... */
1150 bcopy("ESDI/IDE", du->dk_dd.d_typename, 9);
80d17733 1151 bcopy(wp->wdp_model + 20, du->dk_dd.d_packname, 14 - 1);
15637ed4
RG
1152 /* better ... */
1153 du->dk_dd.d_type = DTYPE_ESDI;
1154 du->dk_dd.d_subtype |= DSTYPE_GEOMETRY;
80d17733 1155
15637ed4
RG
1156 return (0);
1157}
1158
1159
1160/* ARGSUSED */
1161int
1162wdclose(dev_t dev, int flags, int fmt)
1163{
1164 register struct disk *du;
80d17733
NW
1165 int part = wdpart(dev), mask = 1 << part;
1166
1167 if (wddospart(dev))
1168 return (0);
15637ed4
RG
1169
1170 du = wddrives[wdunit(dev)];
1171
6d2dcc99
NW
1172 switch (fmt) {
1173 case S_IFCHR:
1174 du->dk_copenpart &= ~mask;
1175 break;
1176 case S_IFBLK:
1177 du->dk_bopenpart &= ~mask;
1178 break;
1179 }
80d17733
NW
1180 du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
1181
1182 return (0);
15637ed4
RG
1183}
1184
1185int
1186wdioctl(dev_t dev, int cmd, caddr_t addr, int flag)
1187{
80d17733 1188 int lunit = wdunit(dev);
15637ed4 1189 register struct disk *du;
80d17733 1190 int error = 0;
6d2dcc99 1191#ifdef notyet
15637ed4
RG
1192 struct uio auio;
1193 struct iovec aiov;
6d2dcc99 1194#endif
15637ed4 1195
6d2dcc99 1196 du = wddrives[lunit];
15637ed4
RG
1197
1198 switch (cmd) {
1199
1200 case DIOCSBAD:
6d2dcc99
NW
1201 if ((flag & FWRITE) == 0)
1202 error = EBADF;
15637ed4
RG
1203 else
1204 du->dk_bad = *(struct dkbad *)addr;
1205 break;
1206
1207 case DIOCGDINFO:
1208 *(struct disklabel *)addr = du->dk_dd;
1209 break;
1210
6d2dcc99 1211 case DIOCGPART:
80d17733
NW
1212 if (wddospart(dev))
1213 return (EINVAL);
6d2dcc99
NW
1214 ((struct partinfo *)addr)->disklab = &du->dk_dd;
1215 ((struct partinfo *)addr)->part =
1216 &du->dk_dd.d_partitions[wdpart(dev)];
1217 break;
1218
1219 case DIOCSDINFO:
1220 if ((flag & FWRITE) == 0)
1221 error = EBADF;
1222 else
1223 error = setdisklabel(&du->dk_dd,
80d17733
NW
1224 (struct disklabel *)addr,
1225#if 0
1226 /*
1227 * XXX setdisklabel() uses the
1228 * openmask to allow it to reject
1229 * changing open partitions. Why
1230 * are we pretending nothing is
1231 * open?
1232 */
1233 du->dk_flags & DKFL_BSDLABEL
1234 ? du->dk_openpart :
1235#endif
1236 0,
1237 du->dk_dospartitions);
6d2dcc99 1238 if (error == 0) {
15637ed4 1239 du->dk_flags |= DKFL_BSDLABEL;
6d2dcc99 1240 wdwsetctlr(du); /* XXX - check */
15637ed4 1241 }
6d2dcc99 1242 break;
15637ed4 1243
6d2dcc99 1244 case DIOCWLABEL:
15637ed4 1245 du->dk_flags &= ~DKFL_WRITEPROT;
6d2dcc99
NW
1246 if ((flag & FWRITE) == 0)
1247 error = EBADF;
1248 else
1249 du->dk_wlabel = *(int *)addr;
1250 break;
15637ed4 1251
6d2dcc99 1252 case DIOCWDINFO:
15637ed4 1253 du->dk_flags &= ~DKFL_WRITEPROT;
6d2dcc99
NW
1254 if ((flag & FWRITE) == 0)
1255 error = EBADF;
80d17733
NW
1256 else if ((error = setdisklabel(&du->dk_dd,
1257 (struct disklabel *)addr,
1258#if 0
1259 du->dk_flags & DKFL_BSDLABEL
1260 ? du->dk_openpart :
1261#endif
1262 0,
1263 du->dk_dospartitions)) == 0) {
1264 int wlab;
15637ed4
RG
1265
1266 du->dk_flags |= DKFL_BSDLABEL;
80d17733 1267 wdwsetctlr(du); /* XXX - check */
15637ed4 1268
6d2dcc99
NW
1269 /* simulate opening partition 0 so write succeeds */
1270 du->dk_openpart |= (1 << 0); /* XXX */
1271 wlab = du->dk_wlabel;
1272 du->dk_wlabel = 1;
80d17733
NW
1273 error = writedisklabel(dev, WDSTRATEGY, &du->dk_dd,
1274 du->dk_dospartitions);
6d2dcc99
NW
1275 du->dk_openpart = du->dk_copenpart | du->dk_bopenpart;
1276 du->dk_wlabel = wlab;
1277 }
1278 break;
15637ed4
RG
1279
1280#ifdef notyet
1281 case DIOCGDINFOP:
1282 *(struct disklabel **)addr = &(du->dk_dd);
1283 break;
1284
1285 case DIOCWFORMAT:
1286 if ((flag & FWRITE) == 0)
1287 error = EBADF;
1288 else {
1289 register struct format_op *fop;
1290
1291 fop = (struct format_op *)addr;
1292 aiov.iov_base = fop->df_buf;
1293 aiov.iov_len = fop->df_count;
1294 auio.uio_iov = &aiov;
1295 auio.uio_iovcnt = 1;
1296 auio.uio_resid = fop->df_count;
1297 auio.uio_segflg = 0;
1298 auio.uio_offset =
80d17733
NW
1299 fop->df_startblk * du->dk_dd.d_secsize;
1300#error /* XXX the 386BSD interface is different */
1301 error = physio(wdformat, &rwdbuf[lunit], 0, dev,
1302 B_WRITE, minphys, &auio);
15637ed4
RG
1303 fop->df_count -= auio.uio_resid;
1304 fop->df_reg[0] = du->dk_status;
1305 fop->df_reg[1] = du->dk_error;
1306 }
1307 break;
1308#endif
1309
1310 default:
1311 error = ENOTTY;
1312 break;
1313 }
1314 return (error);
1315}
1316
80d17733 1317#ifdef B_FORMAT
15637ed4
RG
1318int
1319wdformat(struct buf *bp)
1320{
1321
1322 bp->b_flags |= B_FORMAT;
1323 return (wdstrategy(bp));
1324}
1325#endif
1326
1327int
1328wdsize(dev_t dev)
1329{
80d17733 1330 int lunit = wdunit(dev), part = wdpart(dev), val;
15637ed4
RG
1331 struct disk *du;
1332
80d17733
NW
1333 if (lunit >= NWD || wddospart(dev) || (du = wddrives[lunit]) == NULL)
1334 return (-1);
6d2dcc99 1335 val = 0;
80d17733
NW
1336 if (du->dk_state == CLOSED)
1337 val = wdopen(makewddev(major(dev), lunit, WDRAW),
1338 FREAD, S_IFBLK, 0);
1339 if (val != 0 || du->dk_flags & DKFL_WRITEPROT)
15637ed4 1340 return (-1);
80d17733 1341 return ((int)du->dk_dd.d_partitions[part].p_size);
15637ed4
RG
1342}
1343
80d17733 1344extern char *vmmap; /* poor name! */
15637ed4 1345
80d17733
NW
1346/*
1347 * Dump core after a system crash.
1348 */
15637ed4 1349int
80d17733 1350wddump(dev_t dev)
15637ed4 1351{
80d17733 1352 register struct disk *du;
15637ed4 1353 register struct bt_bad *bt_ptr;
80d17733
NW
1354 struct disklabel *lp;
1355 long num; /* number of sectors to write */
1356 int lunit, part;
6d2dcc99 1357 long blkoff, blknum;
80d17733 1358 long blkchk, blkcnt, blknext;
6d2dcc99 1359 long cylin, head, sector;
80d17733
NW
1360 long secpertrk, secpercyl, nblocks;
1361 char *addr;
1362 extern int Maxmem;
1363 static int wddoingadump = 0;
15637ed4
RG
1364 extern caddr_t CADDR1;
1365
80d17733
NW
1366 /* Toss any characters present prior to dump. */
1367 while (sgetc(1) & 0xff) /* EWS: A hack to work with syscons */
15637ed4
RG
1368 ;
1369
80d17733
NW
1370 /* Check for acceptable device. */
1371 /* XXX should reset to maybe allow du->dk_state < OPEN. */
1372 lunit = wdunit(dev); /* eventually support floppies? */
1373 part = wdpart(dev);
1374 if (lunit >= NWD || wddospart(dev) || (du = wddrives[lunit]) == NULL
1375 || du->dk_state < OPEN || du->dk_flags & DKFL_WRITEPROT)
1376 return (ENXIO);
15637ed4 1377
80d17733
NW
1378 /* Size of memory to dump, in disk sectors. */
1379 num = (u_long)Maxmem * NBPG / du->dk_dd.d_secsize;
15637ed4
RG
1380
1381 secpertrk = du->dk_dd.d_nsectors;
1382 secpercyl = du->dk_dd.d_secpercyl;
1383 nblocks = du->dk_dd.d_partitions[part].p_size;
1384 blkoff = du->dk_dd.d_partitions[part].p_offset;
1385
80d17733
NW
1386#if 0
1387 pg("part %x, nblocks %d, dumplo %d num %d\n",
1388 part, nblocks, dumplo, num);
1389#endif
1390
1391 /* Check transfer bounds against partition size. */
1392 if (dumplo < 0 || dumplo + num > nblocks)
1393 return (EINVAL);
1394
1395 /* Check if we are being called recursively. */
1396 if (wddoingadump)
1397 return (EFAULT);
15637ed4 1398
6d2dcc99 1399#if 0
80d17733
NW
1400 /* Mark controller active for if we panic during the dump. */
1401 wdtab[du->dk_ctrlr].b_active = 1;
6d2dcc99 1402#endif
80d17733
NW
1403 wddoingadump = 1;
1404
1405 /* Recalibrate the drive. */
1406 DELAY(5); /* ATA spec XXX NOT */
1407 if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0
1408 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) != 0
1409 || wdsetctlr(du) != 0) {
1410 wderror((struct buf *)NULL, du, "wddump: recalibrate failed");
1411 return (EIO);
1412 }
6d2dcc99 1413
80d17733
NW
1414 du->dk_flags |= DKFL_SINGLE;
1415 addr = (char *) 0;
15637ed4
RG
1416 blknum = dumplo + blkoff;
1417 while (num > 0) {
80d17733
NW
1418 blkcnt = num;
1419 if (blkcnt > MAXTRANSFER)
1420 blkcnt = MAXTRANSFER;
1421 /* Keep transfer within current cylinder. */
15637ed4
RG
1422 if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl)
1423 blkcnt = secpercyl - (blknum % secpercyl);
80d17733 1424 blknext = blknum + blkcnt;
15637ed4 1425
15637ed4 1426 /*
80d17733
NW
1427 * See if one of the sectors is in the bad sector list
1428 * (if we have one). If the first sector is bad, then
1429 * reduce the transfer to this one bad sector; if another
1430 * sector is bad, then reduce reduce the transfer to
1431 * avoid any bad sectors.
15637ed4 1432 */
80d17733
NW
1433 if ((du->dk_flags & (DKFL_SINGLE | DKFL_BADSECT))
1434 == (DKFL_SINGLE | DKFL_BADSECT))
1435 for (blkchk = blknum; blkchk < blknum + blkcnt; blkchk++) {
1436 cylin = blkchk / secpercyl;
1437 head = (blkchk % secpercyl) / secpertrk;
1438 sector = blkchk % secpertrk;
1439 for (bt_ptr = du->dk_bad.bt_bad;
1440 bt_ptr->bt_cyl != BAD144_NO_CYL; bt_ptr++) {
15637ed4 1441 if (bt_ptr->bt_cyl > cylin)
80d17733
NW
1442 /*
1443 * Sorted list, and we passed our cylinder.
1444 * quit.
1445 */
15637ed4
RG
1446 break;
1447 if (bt_ptr->bt_cyl == cylin &&
80d17733
NW
1448 bt_ptr->bt_trksec == (head << 8) + sector) {
1449 /* Found bad block. */
1450 blkcnt = blkchk - blknum;
1451 if (blkcnt > 0) {
1452 blknext = blknum + blkcnt;
1453 goto out;
1454 }
1455 blkcnt = 1;
1456 blknext = blknum + blkcnt;
15637ed4 1457 /*
80d17733 1458 * Found bad block. Calculate new block number.
15637ed4
RG
1459 * This starts at the end of the disk (skip the
1460 * last track which is used for the bad block list),
1461 * and works backwards to the front of the disk.
1462 */
80d17733
NW
1463 /* XXX as usual. */
1464#ifdef WDDEBUG
1465 printf("--- badblock code -> Old = %ld; ",
1466 blknum);
1467#endif
1468 lp = &du->dk_dd;
6d2dcc99
NW
1469 if (lp->d_partitions[BSD_PART].p_offset != 0)
1470 blknum = lp->d_partitions[BAD144_PART]
1471 .p_offset
1472 + lp->d_partitions[BAD144_PART]
1473 .p_size;
1474 else
1475 blknum = lp->d_secperunit;
80d17733 1476 blknum -= lp->d_nsectors
6d2dcc99 1477 + (bt_ptr - du->dk_bad.bt_bad) + 1;
80d17733
NW
1478#ifdef WDDEBUG
1479 printf("new = %ld\n", blknum);
1480#endif
15637ed4
RG
1481 break;
1482 }
80d17733
NW
1483 }
1484 }
1485out:
1486
1487 /* Compute disk address. */
1488 cylin = blknum / secpercyl;
1489 head = (blknum % secpercyl) / secpertrk;
1490 sector = blknum % secpertrk;
6d2dcc99 1491
80d17733
NW
1492#if 0
1493 /* Let's just talk about this first... */
1494 pg("cylin l%d head %ld sector %ld addr 0x%x count %ld",
1495 cylin, head, sector, addr, blkcnt);
1496#endif
15637ed4 1497
80d17733
NW
1498 /* Do the write. */
1499 if (wdcommand(du, cylin, head, sector, blkcnt, WDCC_WRITE)
1500 != 0) {
1501 wderror((struct buf *)NULL, du,
1502 "wddump: timeout waiting to to give command");
1503 return (EIO);
1504 }
1505 while (blkcnt != 0) {
1506 pmap_enter(kernel_pmap, CADDR1, trunc_page(addr),
1507 VM_PROT_READ, TRUE);
1508
1509 /* Ready to send data? */
1510 DELAY(5); /* ATA spec */
1511 if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)
1512 < 0) {
1513 wderror((struct buf *)NULL, du,
1514 "wddump: timeout waiting for DRQ");
1515 return (EIO);
1516 }
1517 outsw(du->dk_port + wd_data,
1518 CADDR1 + ((int)addr & (NBPG - 1)),
1519 DEV_BSIZE / sizeof(short));
1520 addr += DEV_BSIZE;
1521 if ((unsigned)addr % (1024 * 1024) == 0)
1522 printf("%ld ", num / (1024 * 1024 / DEV_BSIZE));
1523 num--;
1524 blkcnt--;
1525 }
15637ed4 1526
80d17733
NW
1527 /* Wait for completion. */
1528 DELAY(5); /* ATA spec XXX NOT */
1529 if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) < 0) {
1530 wderror((struct buf *)NULL, du,
1531 "wddump: timeout waiting for status");
1532 return (EIO);
1533 }
15637ed4 1534
80d17733
NW
1535 /* Check final status. */
1536 if (du->dk_status
1537 & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ | WDCS_ERR)
1538 != (WDCS_READY | WDCS_SEEKCMPLT)) {
1539 wderror((struct buf *)NULL, du,
1540 "wddump: extra DRQ, or error");
1541 return (EIO);
15637ed4 1542 }
80d17733
NW
1543
1544 /* Update block count. */
1545 blknum = blknext;
1546
1547 /* Operator aborting dump? */
1548 if (sgetc(1) & 0xff) /* EWS: A hack to work with syscons */
1549 return (EINTR);
15637ed4 1550 }
80d17733 1551 return (0);
15637ed4 1552}
6d2dcc99
NW
1553
1554static void
1555wderror(struct buf *bp, struct disk *du, char *mesg)
1556{
1557 if (bp == NULL)
1558 printf("wd%d: %s:", du->dk_lunit, mesg);
1559 else
1560 diskerr(bp, "wd", mesg, LOG_PRINTF, du->dk_skip, &du->dk_dd);
1561 printf(" status %b error %b\n",
80d17733
NW
1562 du->dk_status, WDCS_BITS, du->dk_error, WDERR_BITS);
1563}
1564
1565/*
1566 * Discard any interrupts that were latched by the interrupt system while
1567 * we were doing polled i/o.
1568 */
1569static void
1570wdflushirq(struct disk *du, int old_ipl)
1571{
1572 wdtab[du->dk_ctrlr].b_active = 2;
1573 splx(old_ipl);
1574 (void)splbio();
1575 wdtab[du->dk_ctrlr].b_active = 0;
6d2dcc99
NW
1576}
1577
1578/*
1579 * Reset the controller.
1580 */
1581static int
1582wdreset(struct disk *du)
1583{
80d17733 1584 int wdc;
6d2dcc99
NW
1585
1586 wdc = du->dk_port;
1587 (void)wdwait(du, 0);
1588 outb(wdc + wd_ctlr, WDCTL_IDS | WDCTL_RST);
1589 DELAY(10 * 1000);
1590 outb(wdc + wd_ctlr, WDCTL_IDS);
1591 if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) != 0
1592 || (du->dk_error = inb(wdc + wd_error)) != 0x01)
1593 return (1);
1594 outb(wdc + wd_ctlr, WDCTL_4BIT);
1595 return (0);
1596}
1597
1598/*
1599 * Sleep until driver is inactive.
1600 * This is used only for avoiding rare race conditions, so it is unimportant
1601 * that the sleep may be far too short or too long.
1602 */
1603static void
1604wdsleep(int ctrlr, char *wmesg)
1605{
1606 while (wdtab[ctrlr].b_active)
1607 tsleep((caddr_t)&wdtab[ctrlr].b_active, PZERO - 1, wmesg, 1);
1608}
1609
80d17733
NW
1610/* XXX void */
1611static int
1612wdtimeout(caddr_t cdu, int ticks)
1613{
1614 struct disk *du;
1615 int x;
1616
1617 du = (struct disk *)cdu;
1618 x = splbio();
1619 if (du->dk_timeout != 0 && --du->dk_timeout == 0) {
1620 wderror((struct buf *)NULL, du, "interrupt timeout");
1621 wdunwedge(du);
1622 wdflushirq(du, x);
1623 du->dk_skip = 0;
1624 du->dk_flags |= DKFL_SINGLE;
1625 wdstart(du->dk_ctrlr);
1626 }
1627 timeout(wdtimeout, cdu, hz);
1628 splx(x);
1629 return (0);
1630}
1631
6d2dcc99
NW
1632/*
1633 * Reset the controller after it has become wedged. This is different from
1634 * wdreset() so that wdreset() can be used in the probe and so that this
1635 * can restore the geometry .
1636 */
1637static int
1638wdunwedge(struct disk *du)
1639{
1640 struct disk *du1;
80d17733 1641 int lunit;
6d2dcc99
NW
1642
1643 /* Schedule other drives for recalibration. */
1644 for (lunit = 0; lunit < NWD; lunit++)
1645 if ((du1 = wddrives[lunit]) != NULL && du1 != du
1646 && du1->dk_ctrlr == du->dk_ctrlr
1647 && du1->dk_state > WANTOPEN)
1648 du1->dk_state = WANTOPEN;
1649
1650 DELAY(RECOVERYTIME);
1651 if (wdreset(du) == 0) {
1652 /*
1653 * XXX - recalibrate current drive now because some callers
1654 * aren't prepared to have its state change.
1655 */
1656 if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) == 0
1657 && wdwait(du, WDCS_READY | WDCS_SEEKCMPLT) == 0
1658 && wdsetctlr(du) == 0)
1659 return (0);
1660 }
1661 wderror((struct buf *)NULL, du, "wdunwedge failed");
1662 return (1);
1663}
1664
1665/*
1666 * Wait uninterruptibly until controller is not busy and either certain
1667 * status bits are set or an error has occurred.
1668 * The wait is usually short unless it is for the controller to process
1669 * an entire critical command.
1670 * Return 1 for (possibly stale) controller errors, -1 for timeout errors,
1671 * or 0 for no errors.
1672 * Return controller status in du->dk_status and, if there was a controller
1673 * error, return the error code in du->dk_error.
1674 */
80d17733 1675#ifdef WD_COUNT_RETRIES
6d2dcc99 1676static int min_retries[NWDC];
80d17733
NW
1677#endif
1678
6d2dcc99
NW
1679static int
1680wdwait(struct disk *du, u_char bits_wanted)
1681{
80d17733
NW
1682 int retries;
1683 int wdc;
1684 u_char status;
1685
6d2dcc99 1686#define POLLING 1000
80d17733 1687#define TIMEOUT 2000 /* WDCC_DIAGNOSE can take > 300 msec */
6d2dcc99
NW
1688
1689 wdc = du->dk_port;
1690 retries = POLLING + TIMEOUT;
1691 do {
80d17733
NW
1692#ifdef WD_COUNT_RETRIES
1693 if (min_retries[du->dk_ctrlr] > retries
1694 || min_retries[du->dk_ctrlr] == 0)
1695 min_retries[du->dk_ctrlr] = retries;
1696#endif
1697 DELAY(5); /* ATA spec XXX NOT */
6d2dcc99
NW
1698 du->dk_status = status = inb(wdc + wd_status);
1699 if (!(status & WDCS_BUSY)) {
1700 if (status & WDCS_ERR) {
1701 du->dk_error = inb(wdc + wd_error);
1702 /*
1703 * We once returned here. This is wrong
1704 * because the error bit is apparently only
1705 * valid after the controller has interrupted
1706 * (e.g., the error bit is stale when we wait
1707 * for DRQ for writes). So we can't depend
1708 * on the error bit at all when polling for
1709 * command completion.
1710 */
1711 }
1712 if ((status & bits_wanted) == bits_wanted)
1713 return (status & WDCS_ERR);
1714 }
1715 if (retries < TIMEOUT)
1716 /*
1717 * Switch to a polling rate of about 1 KHz so that
1718 * the timeout is almost machine-independent. The
1719 * controller is taking a long time to respond, so
1720 * an extra msec won't matter.
1721 */
1722 DELAY(1000);
1723 } while (--retries != 0);
1724 return (-1);
1725}
1726
80d17733 1727#endif /* NWDC > 0 */