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