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