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