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