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