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