this version can at least talk to the CSR's of the sgs thompson chip
[unix-history] / usr / src / sys / i386 / isa / wd.c
CommitLineData
cae41ec5
WN
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 *
d4a75cc0 8 * %sccs.include.redist.c%
cae41ec5 9 *
d4a75cc0 10 * @(#)wd.c 7.2 (Berkeley) %G%
cae41ec5 11 */
88b2b5dc 12
d4a75cc0
KB
13/* TODO:peel out buffer at low ipl,
14 speed improvement, rewrite to clean code from garbage artifacts */
15
16
cae41ec5
WN
17#include "wd.h"
18#if NWD > 0
cae41ec5
WN
19
20#include "param.h"
21#include "dkbad.h"
22#include "systm.h"
23#include "conf.h"
24#include "file.h"
8f65e707 25#include "stat.h"
cae41ec5 26#include "ioctl.h"
8f65e707 27#include "disklabel.h"
cae41ec5 28#include "buf.h"
cae41ec5 29#include "uio.h"
8f65e707
WN
30#include "i386/isa/isa_device.h"
31#include "i386/isa/icu.h"
32#include "i386/isa/wdreg.h"
cae41ec5 33#include "syslog.h"
8f65e707 34#include "vm/vm.h"
cae41ec5
WN
35
36#define RETRIES 5 /* number of retries before giving up */
8f65e707 37#define MAXTRANSFER 32 /* max size of transfer in page clusters */
cae41ec5 38
8f65e707
WN
39#define wdctlr(dev) ((minor(dev) & 0x80) >> 7)
40#define wdunit(dev) ((minor(dev) & 0x60) >> 5)
41#define wdpart(dev) ((minor(dev) & 0x1f))
cae41ec5
WN
42
43#define b_cylin b_resid /* cylinder number for doing IO to */
44 /* shares an entry in the buf struct */
45
46/*
47 * Drive states. Used for open and format operations.
48 * States < OPEN (> 0) are transient, during an open operation.
49 * OPENRAW is used for unlabeled disks, and for floppies, to inhibit
50 * bad-sector forwarding.
51 */
52#define RAWDISK 8 /* raw disk operation, no translation*/
53#define ISRAWSTATE(s) (RAWDISK&(s)) /* are we in a raw state? */
54#define DISKSTATE(s) (~RAWDISK&(s)) /* are we in a given state regardless
55 of raw or cooked mode? */
56
57#define CLOSED 0 /* disk is closed. */
58 /* "cooked" disk states */
59#define WANTOPEN 1 /* open requested, not started */
60#define RECAL 2 /* doing restore */
61#define RDLABEL 3 /* reading pack label */
62#define RDBADTBL 4 /* reading bad-sector table */
63#define OPEN 5 /* done with open */
64
65#define WANTOPENRAW (WANTOPEN|RAWDISK) /* raw WANTOPEN */
66#define RECALRAW (RECAL|RAWDISK) /* raw open, doing restore */
67#define OPENRAW (OPEN|RAWDISK) /* open, but unlabeled disk or floppy */
68
69
70/*
71 * The structure of a disk drive.
72 */
73struct disk {
8f65e707
WN
74 struct disklabel dk_dd; /* device configuration data */
75 long dk_bc; /* byte count left */
76 short dk_skip; /* blocks already transferred */
77 char dk_unit; /* physical unit number */
78 char dk_state; /* control state */
79 u_char dk_status; /* copy of status reg. */
80 u_char dk_error; /* copy of error reg. */
81 short dk_open; /* open/closed refcnt */
82 u_long dk_copenpart; /* character units open on this drive */
83 u_long dk_bopenpart; /* block units open on this drive */
84 u_long dk_openpart; /* all units open on this drive */
85 short dk_wlabel; /* label writable? */
cae41ec5
WN
86};
87
88/*
89 * This label is used as a default when initializing a new or raw disk.
90 * It really only lets us access the first track until we know more.
91 */
92struct disklabel dflt_sizes = {
8f65e707 93 DISKMAGIC, DTYPE_ST506, 0, "default", "",
cae41ec5 94 512, /* sector size */
8f65e707
WN
95 17, /* # of sectors per track */
96 8, /* # of tracks per cylinder */
97 766, /* # of cylinders per unit */
98 17*8, /* # of sectors per cylinder */
99 766*8*17, /* # of sectors per unit */
100 0, /* # of spare sectors per track */
101 0, /* # of spare sectors per cylinder */
102 0, /* # of alt. cylinders per unit */
103 3600, /* rotational speed */
104 1, /* hardware sector interleave */
105 0, /* sector 0 skew, per track */
106 0, /* sector 0 skew, per cylinder */
107 0, /* head switch time, usec */
108 0, /* track-to-track seek, usec */
109 0, /* generic flags */
110 0,0,0,0,0,
111 0,0,0,0,0,
112 DISKMAGIC,
113 0,
114 8,
115 8192,
116 8192,
117
118 {{21600, 0, 0,0,0,0}, /* A=root filesystem */
119 {21600, 40, 0,0,0,0},
120 {660890, 0, 0,0,0,0}, /* C=whole disk */
121 {216000, 80, 0,0,0,0},
122 {0, 0, 0,0,0,0},
123 {0, 0, 0,0,0,0},
124 {0, 0, 0,0,0,0},
125 {399600, 480, 0,0,0,0}}
cae41ec5 126};
d405e6a8 127
cae41ec5
WN
128static struct dkbad dkbad[NWD];
129struct disk wddrives[NWD] = {0}; /* table of units */
130struct buf wdtab = {0};
131struct buf wdutab[NWD] = {0}; /* head of queue per drive */
132struct buf rwdbuf[NWD] = {0}; /* buffers for raw IO */
133long wdxfer[NWD] = {0}; /* count of transfers */
134int writeprotected[NWD] = { 0 };
135int wdprobe(), wdattach(), wdintr();
db8f0de7 136struct isa_driver wddriver = {
cae41ec5
WN
137 wdprobe, wdattach, "wd",
138};
cae41ec5 139\f
db8f0de7 140static wdc;
cae41ec5
WN
141/*
142 * Probe routine
143 */
144wdprobe(dvp)
db8f0de7 145 struct isa_device *dvp;
cae41ec5 146{
db8f0de7 147wdc = dvp->id_iobase;
cae41ec5
WN
148
149#ifdef lint
150 wdintr(0);
151#endif
8f65e707 152 /* XXX sorry, needs to be better */
cae41ec5 153 outb(wdc+wd_error, 0x5a) ; /* error register not writable */
cae41ec5 154 outb(wdc+wd_cyl_lo, 0xa5) ; /* but all of cyllo are implemented */
8f65e707 155 if(inb(wdc+wd_error) != 0x5a && inb(wdc+wd_cyl_lo) == 0xa5)
cae41ec5
WN
156 return(1) ;
157 return (0);
158}
159
160/*
161 * attach each drive if possible.
162 */
163wdattach(dvp)
db8f0de7 164 struct isa_device *dvp;
cae41ec5 165{
db8f0de7 166 int unit = dvp->id_unit;
cae41ec5 167
db8f0de7
BJ
168 outb(wdc+wd_ctlr,12);
169 DELAY(1000);
170 outb(wdc+wd_ctlr,8);
cae41ec5
WN
171}
172
173/* Read/write routine for a buffer. Finds the proper unit, range checks
174 * arguments, and schedules the transfer. Does not wait for the transfer
175 * to complete. Multi-page transfers are supported. All I/O requests must
176 * be a multiple of a sector in length.
177 */
178wdstrategy(bp)
179 register struct buf *bp; /* IO operation to perform */
180{
181 register struct buf *dp;
182 register struct disk *du; /* Disk unit to do the IO. */
8f65e707
WN
183 register struct partition *p;
184 long maxsz, sz;
185 int unit = wdunit(bp->b_dev);
cae41ec5
WN
186 int s;
187
188 if ((unit >= NWD) || (bp->b_blkno < 0)) {
88b2b5dc 189 printf("wdstrat: unit = %d, blkno = %d, bcount = %d\n",
cae41ec5 190 unit, bp->b_blkno, bp->b_bcount);
88b2b5dc 191 pg("wd:error in wdstrategy");
cae41ec5
WN
192 bp->b_flags |= B_ERROR;
193 goto bad;
194 }
195 if (writeprotected[unit] && (bp->b_flags & B_READ) == 0) {
196 printf("wd%d: write protected\n", unit);
197 goto bad;
198 }
199 du = &wddrives[unit];
200 if (DISKSTATE(du->dk_state) != OPEN)
201 goto q;
8f65e707 202#ifdef old
cae41ec5
WN
203 /*
204 * Convert DEV_BSIZE "blocks" to sectors.
205 * Note: doing the conversions this way limits the partition size
206 * to about 8 million sectors (1-8 Gb).
207 */
8f65e707
WN
208 blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize;
209 if (((u_long) bp->b_blkno * DEV_BSIZE % du->dk_dd.d_secsize != 0) ||
88b2b5dc 210 bp->b_bcount >= MAXTRANSFER * CLBYTES) {
cae41ec5 211 bp->b_flags |= B_ERROR;
cae41ec5
WN
212 goto bad;
213 }
8f65e707
WN
214 nblocks = du->dk_dd.d_partitions[part].p_size;
215 cyloff = du->dk_dd.d_partitions[part].p_offset;
216 if (blknum + (bp->b_bcount / du->dk_dd.d_secsize) > nblocks) {
cae41ec5
WN
217 if (blknum == nblocks)
218 bp->b_resid = bp->b_bcount;
219 else
220 bp->b_flags |= B_ERROR;
221 goto bad;
222 }
8f65e707
WN
223 bp->b_cylin = blknum / du->dk_dd.d_secpercyl + cyloff;
224#else
225 /*
226 * Determine the size of the transfer, and make sure it is
227 * within the boundaries of the partition.
228 */
229 p = &du->dk_dd.d_partitions[wdpart(bp->b_dev)];
230 maxsz = p->p_size;
231 sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT;
232 if (bp->b_blkno + p->p_offset <= LABELSECTOR &&
233#if LABELSECTOR != 0
234 bp->b_blkno + p->p_offset + sz > LABELSECTOR &&
235#endif
236 (bp->b_flags & B_READ) == 0 && du->dk_wlabel == 0) {
237 bp->b_error = EROFS;
238 goto bad;
239 }
240 if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) {
241 /* if exactly at end of disk, return an EOF */
242 if (bp->b_blkno == maxsz) {
243 bp->b_resid = bp->b_bcount;
244 biodone(bp);
245 return;
246 }
247 /* or truncate if part of it fits */
248 sz = maxsz - bp->b_blkno;
249 if (sz <= 0)
250 goto bad;
251 bp->b_bcount = sz << DEV_BSHIFT;
252 }
253 bp->b_cylin = (bp->b_blkno + p->p_offset) / du->dk_dd.d_secpercyl;
254#endif
cae41ec5
WN
255q:
256 dp = &wdutab[unit];
d405e6a8 257 s = splhigh();
cae41ec5
WN
258 disksort(dp, bp);
259 if (dp->b_active == 0)
260 wdustart(du); /* start drive if idle */
261 if (wdtab.b_active == 0)
262 wdstart(s); /* start IO if controller idle */
263 splx(s);
264 return;
265
266bad:
267 bp->b_error = EINVAL;
268 biodone(bp);
269}
270
271/* Routine to queue a read or write command to the controller. The request is
272 * linked into the active list for the controller. If the controller is idle,
273 * the transfer is started.
274 */
275wdustart(du)
276 register struct disk *du;
277{
278 register struct buf *bp, *dp;
279
280 dp = &wdutab[du->dk_unit];
281 if (dp->b_active)
282 return;
283 bp = dp->b_actf;
284 if (bp == NULL)
285 return;
286 dp->b_forw = NULL;
287 if (wdtab.b_actf == NULL) /* link unit into active list */
288 wdtab.b_actf = dp;
289 else
290 wdtab.b_actl->b_forw = dp;
291 wdtab.b_actl = dp;
292 dp->b_active = 1; /* mark the drive as busy */
293}
294
295/*
296 * Controller startup routine. This does the calculation, and starts
297 * a single-sector read or write operation. Called to start a transfer,
298 * or from the interrupt routine to continue a multi-sector transfer.
299 * RESTRICTIONS:
300 * 1. The transfer length must be an exact multiple of the sector size.
301 */
302
d405e6a8
BJ
303static wd_sebyse;
304
cae41ec5
WN
305wdstart()
306{
307 register struct disk *du; /* disk unit for IO */
cae41ec5
WN
308 register struct buf *bp;
309 struct buf *dp;
310 register struct bt_bad *bt_ptr;
311 long blknum, pagcnt, cylin, head, sector;
312 long secpertrk, secpercyl, addr, i;
8f65e707 313 int unit, s;
cae41ec5
WN
314
315loop:
316 dp = wdtab.b_actf;
317 if (dp == NULL)
318 return;
319 bp = dp->b_actf;
320 if (bp == NULL) {
321 wdtab.b_actf = dp->b_forw;
322 goto loop;
323 }
8f65e707 324 unit = wdunit(bp->b_dev);
cae41ec5
WN
325 du = &wddrives[unit];
326 if (DISKSTATE(du->dk_state) <= RDLABEL) {
327 if (wdcontrol(bp)) {
328 dp->b_actf = bp->av_forw;
329 goto loop; /* done */
330 }
331 return;
332 }
8f65e707
WN
333 secpertrk = du->dk_dd.d_nsectors;
334 secpercyl = du->dk_dd.d_secpercyl;
cae41ec5
WN
335 /*
336 * Convert DEV_BSIZE "blocks" to sectors.
337 */
8f65e707 338 blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize
cae41ec5
WN
339 + du->dk_skip;
340#ifdef WDDEBUG
341 if (du->dk_skip == 0) {
342 dprintf(DDSK,"\nwdstart %d: %s %d@%d; map ", unit,
343 (bp->b_flags & B_READ) ? "read" : "write",
344 bp->b_bcount, blknum);
345 } else {
d405e6a8 346 dprintf(DDSK," %d)%x", du->dk_skip, inb(wdc+wd_altsts));
cae41ec5
WN
347 }
348#endif
349
350 addr = (int) bp->b_un.b_addr;
351 if(du->dk_skip==0) du->dk_bc = bp->b_bcount;
352 cylin = blknum / secpercyl;
353 head = (blknum % secpercyl) / secpertrk;
d405e6a8 354 sector = blknum % secpertrk;
cae41ec5 355 if (DISKSTATE(du->dk_state) == OPEN)
8f65e707
WN
356 cylin += du->dk_dd.d_partitions[wdpart(bp->b_dev)].p_offset
357 / secpercyl;
cae41ec5 358
cae41ec5
WN
359 /*
360 * See if the current block is in the bad block list.
361 * (If we have one, and not formatting.)
362 */
d405e6a8 363 if (DISKSTATE(du->dk_state) == OPEN && wd_sebyse)
cae41ec5
WN
364 for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) {
365 if (bt_ptr->bt_cyl > cylin)
366 /* Sorted list, and we passed our cylinder. quit. */
367 break;
368 if (bt_ptr->bt_cyl == cylin &&
369 bt_ptr->bt_trksec == (head << 8) + sector) {
370 /*
371 * Found bad block. Calculate new block addr.
372 * This starts at the end of the disk (skip the
373 * last track which is used for the bad block list),
374 * and works backwards to the front of the disk.
375 */
376#ifdef WDDEBUG
377 dprintf(DDSK,"--- badblock code -> Old = %d; ",
378 blknum);
379#endif
8f65e707 380 blknum = du->dk_dd.d_secperunit - du->dk_dd.d_nsectors
cae41ec5
WN
381 - (bt_ptr - dkbad[unit].bt_bad) - 1;
382 cylin = blknum / secpercyl;
383 head = (blknum % secpercyl) / secpertrk;
384 sector = blknum % secpertrk;
385#ifdef WDDEBUG
386 dprintf(DDSK, "new = %d\n", blknum);
387#endif
388 break;
389 }
390 }
d405e6a8 391 sector += 1; /* sectors begin with 1, not 0 */
cae41ec5
WN
392
393 wdtab.b_active = 1; /* mark controller active */
394
d405e6a8
BJ
395 if(du->dk_skip==0 || wd_sebyse) {
396 if(wdtab.b_errcnt && (bp->b_flags & B_READ) == 0) du->dk_bc += 512;
397 while ((inb(wdc+wd_status) & WDCS_BUSY) != 0) ;
398 /*while ((inb(wdc+wd_status) & WDCS_DRQ)) inb(wdc+wd_data);*/
cae41ec5
WN
399 outb(wdc+wd_precomp, 0xff);
400 /*wr(wdc+wd_precomp, du->dk_dd.dk_precompcyl / 4);*/
401 /*if (bp->b_flags & B_FORMAT) {
402 wr(wdc+wd_sector, du->dk_dd.dk_gap3);
403 wr(wdc+wd_seccnt, du->dk_dd.dk_nsectors);
404 } else {*/
d405e6a8
BJ
405 if(wd_sebyse)
406 outb(wdc+wd_seccnt, 1);
407 else
408 outb(wdc+wd_seccnt, ((du->dk_bc +511) / 512));
cae41ec5
WN
409 outb(wdc+wd_sector, sector);
410
411 outb(wdc+wd_cyl_lo, cylin);
412 outb(wdc+wd_cyl_hi, cylin >> 8);
413
414 /* Set up the SDH register (select drive). */
415 outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
edf6b89d 416 while ((inb(wdc+wd_status) & WDCS_READY) == 0) ;
cae41ec5
WN
417
418 /*if (bp->b_flags & B_FORMAT)
419 wr(wdc+wd_command, WDCC_FORMAT);
420 else*/
421 outb(wdc+wd_command,
422 (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE);
423#ifdef WDDEBUG
d405e6a8
BJ
424 dprintf(DDSK,"sector %d cylin %d head %d addr %x sts %x\n",
425 sector, cylin, head, addr, inb(wdc+wd_altsts));
cae41ec5 426#endif
d405e6a8 427}
cae41ec5
WN
428
429 /* If this is a read operation, just go away until it's done. */
430 if (bp->b_flags & B_READ) return;
431
432 /* Ready to send data? */
8f65e707 433 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0);
cae41ec5
WN
434
435 /* ASSUMES CONTIGUOUS MEMORY */
d405e6a8 436 outsw (wdc+wd_data, addr+du->dk_skip*512, 256);
cae41ec5
WN
437 du->dk_bc -= 512;
438}
439
440/*
441 * these are globally defined so they can be found
442 * by the debugger easily in the case of a system crash
443 */
444daddr_t wd_errsector;
445daddr_t wd_errbn;
446unsigned char wd_errstat;
447
448/* Interrupt routine for the controller. Acknowledge the interrupt, check for
449 * errors on the current operation, mark it done if necessary, and start
450 * the next request. Also check for a partially done transfer, and
451 * continue with the next chunk if so.
452 */
8f65e707 453wdintr(unit)
cae41ec5
WN
454{
455 register struct disk *du;
cae41ec5
WN
456 register struct buf *bp, *dp;
457 int status;
458 char partch ;
8f65e707 459 static wd_haderror;
cae41ec5
WN
460
461 /* Shouldn't need this, but it may be a slow controller. */
8f65e707 462 while ((status = inb(wdc+wd_status)) & WDCS_BUSY) ;
cae41ec5
WN
463 if (!wdtab.b_active) {
464 printf("wd: extra interrupt\n");
465 return;
466 }
467
ceab7d1a 468#ifdef WDDEBUG
cae41ec5
WN
469 dprintf(DDSK,"I ");
470#endif
471 dp = wdtab.b_actf;
472 bp = dp->b_actf;
8f65e707
WN
473 du = &wddrives[wdunit(bp->b_dev)];
474 partch = wdpart(bp->b_dev) + 'a';
cae41ec5
WN
475 if (DISKSTATE(du->dk_state) <= RDLABEL) {
476 if (wdcontrol(bp))
477 goto done;
478 return;
479 }
480 if (status & (WDCS_ERR | WDCS_ECCCOR)) {
d405e6a8 481 wd_errstat = inb(wdc+wd_error); /* save error status */
cae41ec5 482#ifdef WDDEBUG
d405e6a8 483 printf("status %x error %x\n", status, wd_errstat);
cae41ec5 484#endif
d405e6a8
BJ
485 if(wd_sebyse == 0) {
486 wd_haderror = 1;
487 goto outt;
488 }
cae41ec5
WN
489 /*if (bp->b_flags & B_FORMAT) {
490 du->dk_status = status;
491 du->dk_error = wdp->wd_error;
492 bp->b_flags |= B_ERROR;
493 goto done;
494 }*/
495
8f65e707 496 wd_errsector = (bp->b_cylin * du->dk_dd.d_secpercyl) +
cae41ec5 497 (((unsigned long) bp->b_blkno * DEV_BSIZE /
8f65e707 498 du->dk_dd.d_secsize) % du->dk_dd.d_secpercyl) +
cae41ec5
WN
499 du->dk_skip;
500 wd_errbn = bp->b_blkno
8f65e707 501 + du->dk_skip * du->dk_dd.d_secsize / DEV_BSIZE ;
cae41ec5 502 if (status & WDCS_ERR) {
d405e6a8 503 if (++wdtab.b_errcnt < RETRIES) {
cae41ec5 504 wdtab.b_active = 0;
d405e6a8 505 } else {
cae41ec5
WN
506 printf("wd%d%c: ", du->dk_unit, partch);
507 printf(
508 "hard %s error, sn %d bn %d status %b error %b\n",
509 (bp->b_flags & B_READ)? "read":"write",
510 wd_errsector, wd_errbn, status, WDCS_BITS,
511 wd_errstat, WDERR_BITS);
512 bp->b_flags |= B_ERROR; /* flag the error */
513 }
514 } else
515 log(LOG_WARNING,"wd%d%c: soft ecc sn %d bn %d\n",
516 du->dk_unit, partch, wd_errsector,
517 wd_errbn);
518 }
d405e6a8 519outt:
cae41ec5
WN
520
521 /*
522 * If this was a successful read operation, fetch the data.
523 */
524 if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) {
525 int chk, dummy;
526
d405e6a8 527 chk = min(256,du->dk_bc/2);
cae41ec5 528 /* Ready to receive data? */
8f65e707 529 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
cae41ec5
WN
530
531/*dprintf(DDSK,"addr %x\n", (int)bp->b_un.b_addr + du->dk_skip * 512);*/
532 insw(wdc+wd_data,(int)bp->b_un.b_addr + du->dk_skip * 512 ,chk);
88b2b5dc 533 du->dk_bc -= 2*chk;
d405e6a8 534 while (chk++ < 256) insw (wdc+wd_data,&dummy,1);
cae41ec5
WN
535 }
536
537 wdxfer[du->dk_unit]++;
538 if (wdtab.b_active) {
539 if ((bp->b_flags & B_ERROR) == 0) {
540 du->dk_skip++; /* Add to successful sectors. */
541 if (wdtab.b_errcnt) {
542 log(LOG_WARNING, "wd%d%c: ",
543 du->dk_unit, partch);
544 log(LOG_WARNING,
545 "soft %s error, sn %d bn %d error %b retries %d\n",
546 (bp->b_flags & B_READ) ? "read" : "write",
547 wd_errsector, wd_errbn, wd_errstat,
548 WDERR_BITS, wdtab.b_errcnt);
549 }
550 wdtab.b_errcnt = 0;
551
552 /* see if more to transfer */
88b2b5dc 553 /*if (du->dk_skip < (bp->b_bcount + 511) / 512) {*/
d405e6a8 554 if (du->dk_bc > 0 && wd_haderror == 0) {
cae41ec5
WN
555 wdstart();
556 return; /* next chunk is started */
d405e6a8
BJ
557 } else if (wd_haderror && wd_sebyse == 0) {
558 du->dk_skip = 0;
559 wd_haderror = 0;
560 wd_sebyse = 1;
561 wdstart();
562 return; /* redo xfer sector by sector */
cae41ec5
WN
563 }
564 }
565
566done:
d405e6a8 567 wd_sebyse = 0;
cae41ec5
WN
568 /* done with this transfer, with or without error */
569 wdtab.b_actf = dp->b_forw;
570 wdtab.b_errcnt = 0;
571 du->dk_skip = 0;
572 dp->b_active = 0;
573 dp->b_actf = bp->av_forw;
574 dp->b_errcnt = 0;
575 bp->b_resid = 0;
576 biodone(bp);
577 }
578 wdtab.b_active = 0;
579 if (dp->b_actf)
580 wdustart(du); /* requeue disk if more io to do */
581 if (wdtab.b_actf)
582 wdstart(); /* start IO on next drive */
583}
584
585/*
586 * Initialize a drive.
587 */
8f65e707
WN
588wdopen(dev, flags, fmt)
589 dev_t dev;
590 int flags, fmt;
cae41ec5
WN
591{
592 register unsigned int unit;
593 register struct buf *bp;
594 register struct disk *du;
8f65e707
WN
595 int part = wdpart(dev), mask = 1 << part;
596 struct partition *pp;
cae41ec5
WN
597 struct dkbad *db;
598 int i, error = 0;
599
8f65e707 600 unit = wdunit(dev);
cae41ec5
WN
601 if (unit >= NWD) return (ENXIO) ;
602 du = &wddrives[unit];
8f65e707 603#ifdef notdef
cae41ec5
WN
604 if (du->dk_open){
605 du->dk_open++ ;
606 return(0); /* already is open, don't mess with it */
607 }
cae41ec5
WN
608#endif
609 du->dk_unit = unit;
610 wdutab[unit].b_actf = NULL;
611 /*if (flags & O_NDELAY)
612 du->dk_state = WANTOPENRAW;
613 else*/
614 du->dk_state = WANTOPEN;
615 /*
616 * Use the default sizes until we've read the label,
617 * or longer if there isn't one there.
618 */
619 du->dk_dd = dflt_sizes;
620
621 /*
622 * Recal, read of disk label will be done in wdcontrol
623 * during first read operation.
624 */
625 bp = geteblk(512);
ceab7d1a 626 bp->b_dev = dev & 0xff00;
8f65e707
WN
627 bp->b_bcount = 0;
628 bp->b_blkno = LABELSECTOR;
cae41ec5
WN
629 bp->b_flags = B_READ;
630 wdstrategy(bp);
631 biowait(bp);
632 if (bp->b_flags & B_ERROR) {
cae41ec5
WN
633 error = ENXIO;
634 du->dk_state = CLOSED;
635 goto done;
636 }
637 if (du->dk_state == OPENRAW) {
638 du->dk_state = OPENRAW;
639 goto done;
640 }
cae41ec5
WN
641 /*
642 * Read bad sector table into memory.
643 */
644 i = 0;
645 do {
cae41ec5 646 bp->b_flags = B_BUSY | B_READ;
8f65e707 647 bp->b_blkno = du->dk_dd.d_secperunit - du->dk_dd.d_nsectors
cae41ec5 648 + i;
8f65e707
WN
649 if (du->dk_dd.d_secsize > DEV_BSIZE)
650 bp->b_blkno *= du->dk_dd.d_secsize / DEV_BSIZE;
cae41ec5 651 else
8f65e707
WN
652 bp->b_blkno /= DEV_BSIZE / du->dk_dd.d_secsize;
653 bp->b_bcount = du->dk_dd.d_secsize;
654 bp->b_cylin = du->dk_dd.d_ncylinders - 1;
cae41ec5
WN
655 wdstrategy(bp);
656 biowait(bp);
657 } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 &&
8f65e707 658 i < du->dk_dd.d_nsectors);
cae41ec5 659 db = (struct dkbad *)(bp->b_un.b_addr);
d405e6a8 660#define DKBAD_MAGIC 0x4321
cae41ec5
WN
661 if ((bp->b_flags & B_ERROR) == 0 && db->bt_mbz == 0 &&
662 db->bt_flag == DKBAD_MAGIC) {
663 dkbad[unit] = *db;
664 du->dk_state = OPEN;
665 } else {
666 printf("wd%d: %s bad-sector file\n", unit,
667 (bp->b_flags & B_ERROR) ? "can't read" : "format error in");
8f65e707 668 error = ENXIO ;
cae41ec5
WN
669 du->dk_state = OPENRAW;
670 }
cae41ec5
WN
671done:
672 bp->b_flags = B_INVAL | B_AGE;
673 brelse(bp);
674 if (error == 0)
675 du->dk_open = 1;
8f65e707
WN
676
677 /*
678 * Warn if a partion is opened
679 * that overlaps another partition which is open
680 * unless one is the "raw" partition (whole disk).
681 */
682#define RAWPART 8 /* 'x' partition */ /* XXX */
683 if ((du->dk_openpart & mask) == 0 && part != RAWPART) {
684 int start, end;
685
686 pp = &du->dk_dd.d_partitions[part];
687 start = pp->p_offset;
688 end = pp->p_offset + pp->p_size;
689 for (pp = du->dk_dd.d_partitions;
690 pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions];
691 pp++) {
692 if (pp->p_offset + pp->p_size <= start ||
693 pp->p_offset >= end)
694 continue;
695 if (pp - du->dk_dd.d_partitions == RAWPART)
696 continue;
697 if (du->dk_openpart & (1 << (pp -
698 du->dk_dd.d_partitions)))
699 log(LOG_WARNING,
700 "wd%d%c: overlaps open partition (%c)\n",
701 unit, part + 'a',
702 pp - du->dk_dd.d_partitions + 'a');
703 }
704 }
705 if (part >= du->dk_dd.d_npartitions)
706 return (ENXIO);
707 du->dk_openpart |= mask;
708 switch (fmt) {
709 case S_IFCHR:
710 du->dk_copenpart |= mask;
711 break;
712 case S_IFBLK:
713 du->dk_bopenpart |= mask;
714 break;
715 }
cae41ec5
WN
716 return (error);
717}
718
719/*
720 * Implement operations other than read/write.
721 * Called from wdstart or wdintr during opens and formats.
722 * Uses finite-state-machine to track progress of operation in progress.
723 * Returns 0 if operation still in progress, 1 if completed.
724 */
725wdcontrol(bp)
726 register struct buf *bp;
727{
728 register struct disk *du;
cae41ec5
WN
729 register unit;
730 unsigned char stat;
731 int s, cnt;
732 extern int bootdev, cyloffset;
733
8f65e707 734 du = &wddrives[wdunit(bp->b_dev)];
cae41ec5
WN
735 unit = du->dk_unit;
736 switch (DISKSTATE(du->dk_state)) {
737
738 tryagainrecal:
739 case WANTOPEN: /* set SDH, step rate, do restore */
740#ifdef WDDEBUG
741 dprintf(DDSK,"wd%d: recal ", unit);
742#endif
743 s = splbio(); /* not called from intr level ... */
744 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
745 wdtab.b_active = 1;
746 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
747 du->dk_state++;
748 splx(s);
749 return(0);
750
751 case RECAL:
edf6b89d 752 if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
cae41ec5
WN
753 printf("wd%d: recal", du->dk_unit);
754 if (unit == 0) {
755 printf(": status %b error %b\n",
756 stat, WDCS_BITS,
757 inb(wdc+wd_error), WDERR_BITS);
758 if (++wdtab.b_errcnt < RETRIES)
759 goto tryagainrecal;
760 }
761 goto badopen;
762 }
8f65e707
WN
763
764 /* some compaq controllers require this ... */
765 wdsetctlr(bp->b_dev, du);
766
cae41ec5
WN
767 wdtab.b_errcnt = 0;
768 if (ISRAWSTATE(du->dk_state)) {
769 du->dk_state = OPENRAW;
770 return(1);
771 }
772retry:
773#ifdef WDDEBUG
774 dprintf(DDSK,"rdlabel ");
775#endif
8f65e707 776if( cyloffset < 0 || cyloffset > 8192) cyloffset=0;
cae41ec5 777 /*
8f65e707
WN
778 * Read in sector LABELSECTOR to get the pack label
779 * and geometry.
cae41ec5
WN
780 */
781 outb(wdc+wd_precomp, 0xff);/* sometimes this is head bit 3 */
782 outb(wdc+wd_seccnt, 1);
8f65e707 783 outb(wdc+wd_sector, LABELSECTOR+1);
cae41ec5
WN
784 /*if (bp->b_dev == bootdev) {
785 (wdc+wd_cyl_lo = cyloffset & 0xff;
786 (wdc+wd_cyl_hi = cyloffset >> 8;
787 } else {
788 (wdc+wd_cyl_lo = 0;
789 (wdc+wd_cyl_hi = 0;
790 }*/
791 outb(wdc+wd_cyl_lo, (cyloffset & 0xff));
792 outb(wdc+wd_cyl_hi, (cyloffset >> 8));
793 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
794 outb(wdc+wd_command, WDCC_READ);
795 du->dk_state = RDLABEL;
796 return(0);
797
798 case RDLABEL:
799 if ((stat = inb(wdc+wd_status)) & WDCS_ERR) {
800 if (++wdtab.b_errcnt < RETRIES)
801 goto retry;
802 printf("wd%d: read label", unit);
803 goto badopen;
804 }
805
806 insw(wdc+wd_data, bp->b_un.b_addr, 256);
807
808 if (((struct disklabel *)
8f65e707 809 (bp->b_un.b_addr + LABELOFFSET))->d_magic == DISKMAGIC) {
cae41ec5
WN
810 du->dk_dd =
811 * (struct disklabel *) (bp->b_un.b_addr + LABELOFFSET);
812 } else {
813 printf("wd%d: bad disk label\n", du->dk_unit);
814 du->dk_state = OPENRAW;
815 }
db8f0de7
BJ
816
817 s = splbio(); /* not called from intr level ... */
8f65e707
WN
818 while ((stat = inb(wdc+wd_status)) & WDCS_BUSY);
819
820 wdsetctlr(bp->b_dev, du);
821
db8f0de7
BJ
822 outb(wdc+wd_seccnt, 0);
823 splx(s);
824
cae41ec5
WN
825 if (du->dk_state == RDLABEL)
826 du->dk_state = RDBADTBL;
827 /*
828 * The rest of the initialization can be done
829 * by normal means.
830 */
831 return(1);
832
833 default:
8f65e707 834 panic("wdcontrol");
cae41ec5
WN
835 }
836 /* NOTREACHED */
837
838badopen:
839 printf(": status %b error %b\n",
840 stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS);
841 du->dk_state = OPENRAW;
842 return(1);
843}
844
8f65e707
WN
845wdsetctlr(dev, du) dev_t dev; struct disk *du; {
846 int stat;
847
848 outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders);
849 outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders)>>8);
850 outb(wdc+wd_sdh, WDSD_IBM | (wdunit(dev) << 4) + du->dk_dd.d_ntracks-1);
851 outb(wdc+wd_seccnt, du->dk_dd.d_nsectors);
852 outb(wdc+wd_command, 0x91);
cae41ec5 853
8f65e707
WN
854 while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) ;
855 stat = inb(wdc+wd_error);
856 return(stat);
857}
858
859/* ARGSUSED */
860wdclose(dev, flags, fmt)
861 dev_t dev;
862 int flags, fmt;
863{
864 register struct disk *du;
865
866 du = &wddrives[wdunit(dev)];
cae41ec5
WN
867 du->dk_open-- ;
868 /*if (du->dk_open == 0) du->dk_state = CLOSED ; does not work */
869}
870
871wdioctl(dev,cmd,addr,flag)
872 dev_t dev;
873 caddr_t addr;
874{
8f65e707 875 int unit = wdunit(dev);
cae41ec5
WN
876 register struct disk *du;
877 int error = 0;
878 struct uio auio;
879 struct iovec aiov;
880 /*int wdformat();*/
881
882 du = &wddrives[unit];
883
884 switch (cmd) {
885
886 case DIOCGDINFO:
887 *(struct disklabel *)addr = du->dk_dd;
888 break;
889
8f65e707
WN
890 case DIOCGPART:
891 ((struct partinfo *)addr)->disklab = &du->dk_dd;
892 ((struct partinfo *)addr)->part =
893 &du->dk_dd.d_partitions[wdpart(dev)];
894 break;
895
896 case DIOCSDINFO:
897 if ((flag & FWRITE) == 0)
898 error = EBADF;
899 else
900 error = setdisklabel(&du->dk_dd,
901 (struct disklabel *)addr,
902 0 /*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/);
903 /*if (error == 0 && dk->dk_state == OPENRAW &&
904 vdreset_drive(vddinfo[unit]))
905 dk->dk_state = OPEN;*/
906 wdsetctlr(dev, du);
907 break;
908
909 case DIOCWLABEL:
910 if ((flag & FWRITE) == 0)
911 error = EBADF;
912 else
913 du->dk_wlabel = *(int *)addr;
914 break;
915
916 case DIOCWDINFO:
917 if ((flag & FWRITE) == 0)
918 error = EBADF;
919 else if ((error = setdisklabel(&du->dk_dd, (struct disklabel *)addr,
920 0/*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/)) == 0) {
921 int wlab;
922
923 /*if (error == 0 && dk->dk_state == OPENRAW &&
924 vdreset_drive(vddinfo[unit]))
925 dk->dk_state = OPEN; */
926 wdsetctlr(dev, du);
927
928 /* simulate opening partition 0 so write succeeds */
929 /* dk->dk_openpart |= (1 << 0); /* XXX */
930 wlab = du->dk_wlabel;
931 du->dk_wlabel = 1;
932 error = writedisklabel(dev, wdstrategy, &du->dk_dd,wdpart(dev));
933 /*dk->dk_openpart = dk->dk_copenpart | dk->dk_bopenpart;*/
934 du->dk_wlabel = wlab;
935 }
936 break;
937
938#ifdef notyet
cae41ec5
WN
939 case DIOCGDINFOP:
940 *(struct disklabel **)addr = &(du->dk_dd);
941 break;
942
cae41ec5
WN
943 case DIOCWFORMAT:
944 if ((flag & FWRITE) == 0)
945 error = EBADF;
946 else {
947 register struct format_op *fop;
948
949 fop = (struct format_op *)addr;
950 aiov.iov_base = fop->df_buf;
951 aiov.iov_len = fop->df_count;
952 auio.uio_iov = &aiov;
953 auio.uio_iovcnt = 1;
954 auio.uio_resid = fop->df_count;
955 auio.uio_segflg = 0;
956 auio.uio_offset =
8f65e707 957 fop->df_startblk * du->dk_dd.d_secsize;
cae41ec5
WN
958 error = physio(wdformat, &rwdbuf[unit], dev, B_WRITE,
959 minphys, &auio);
960 fop->df_count -= auio.uio_resid;
961 fop->df_reg[0] = du->dk_status;
962 fop->df_reg[1] = du->dk_error;
963 }
964 break;
965#endif
966
967 default:
968 error = ENOTTY;
969 break;
970 }
971 return (error);
972}
973
974/*wdformat(bp)
975 struct buf *bp;
976{
977
978 bp->b_flags |= B_FORMAT;
979 return (wdstrategy(bp));
980}*/
981
982/*
983 * Routines to do raw IO for a unit.
984 */
985wdread(dev, uio) /* character read routine */
986 dev_t dev;
987 struct uio *uio;
988{
8f65e707 989 int unit = wdunit(dev) ;
cae41ec5
WN
990
991 if (unit >= NWD) return(ENXIO);
992 return(physio(wdstrategy, &rwdbuf[unit], dev, B_READ, minphys, uio));
993}
994
995
996wdwrite(dev, uio) /* character write routine */
997 dev_t dev;
998 struct uio *uio;
999{
8f65e707 1000 int unit = wdunit(dev) ;
cae41ec5
WN
1001
1002 if (unit >= NWD) return(ENXIO);
1003 return(physio(wdstrategy, &rwdbuf[unit], dev, B_WRITE, minphys, uio));
1004}
1005
1006wdsize(dev)
1007 dev_t dev;
1008{
8f65e707
WN
1009 register unit = wdunit(dev);
1010 register part = wdpart(dev);
cae41ec5
WN
1011 register struct disk *du;
1012 register val ;
1013
cae41ec5 1014 if (unit >= NWD) return(-1);
8f65e707
WN
1015 if (wddrives[unit].dk_state == 0) {
1016 val = wdopen (dev, 0);
1017 if (val < 0)
1018 return (-1);
1019 }
cae41ec5 1020 du = &wddrives[unit];
8f65e707
WN
1021 return((int)((u_long)du->dk_dd.d_partitions[part].p_size *
1022 du->dk_dd.d_secsize / 512));
cae41ec5
WN
1023}
1024
8f65e707
WN
1025extern char *vmmap; /* poor name! */
1026
cae41ec5
WN
1027wddump(dev) /* dump core after a system crash */
1028 dev_t dev;
1029{
cae41ec5 1030 register struct disk *du; /* disk unit to do the IO */
cae41ec5
WN
1031 register struct bt_bad *bt_ptr;
1032 long num; /* number of sectors to write */
8f65e707 1033 int unit, part;
cae41ec5 1034 long cyloff, blknum, blkcnt;
8f65e707 1035 long cylin, head, sector, stat;
cae41ec5 1036 long secpertrk, secpercyl, nblocks, i;
8f65e707
WN
1037 char *addr;
1038 extern int Maxmem;
cae41ec5 1039 static wddoingadump = 0 ;
8f65e707
WN
1040 extern CMAP1;
1041 extern char CADDR1[];
cae41ec5 1042
8f65e707
WN
1043
1044#ifdef ARGO
1045outb(0x461,0); /* disable failsafe timer */
1046#endif
1047 addr = (char *) 0; /* starting address */
cae41ec5 1048 /* size of memory to dump */
8f65e707
WN
1049 num = Maxmem;
1050 unit = wdunit(dev); /* eventually support floppies? */
1051 part = wdpart(dev); /* file system */
cae41ec5
WN
1052 /* check for acceptable drive number */
1053 if (unit >= NWD) return(ENXIO);
1054
1055 du = &wddrives[unit];
1056 /* was it ever initialized ? */
1057 if (du->dk_state < OPEN) return (ENXIO) ;
1058
1059 /* Convert to disk sectors */
8f65e707 1060 num = (u_long) num * NBPG / du->dk_dd.d_secsize;
cae41ec5
WN
1061
1062 /* check if controller active */
1063 /*if (wdtab.b_active) return(EFAULT); */
1064 if (wddoingadump) return(EFAULT);
1065
8f65e707
WN
1066 secpertrk = du->dk_dd.d_nsectors;
1067 secpercyl = du->dk_dd.d_secpercyl;
1068 nblocks = du->dk_dd.d_partitions[part].p_size;
1069 cyloff = du->dk_dd.d_partitions[part].p_offset / secpercyl;
cae41ec5 1070
8f65e707 1071/*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/
cae41ec5 1072 /* check transfer bounds against partition size */
8f65e707 1073 if ((dumplo < 0) || ((dumplo + num) > nblocks))
cae41ec5
WN
1074 return(EINVAL);
1075
1076 /*wdtab.b_active = 1; /* mark controller active for if we
1077 panic during the dump */
1078 wddoingadump = 1 ; i = 100000 ;
8f65e707
WN
1079 while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0)) ;
1080 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4));
1081 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP);
1082 while (inb(wdc+wd_status) & WDCS_BUSY) ;
1083
1084 /* some compaq controllers require this ... */
1085 wdsetctlr(dev, du);
cae41ec5
WN
1086
1087 blknum = dumplo;
1088 while (num > 0) {
1089#ifdef notdef
1090 if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER;
1091 if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl)
1092 blkcnt = secpercyl - (blknum % secpercyl);
1093 /* keep transfer within current cylinder */
1094#endif
8f65e707 1095 pmap_enter(pmap_kernel(), vmmap, addr, VM_PROT_READ, TRUE);
cae41ec5
WN
1096
1097 /* compute disk address */
1098 cylin = blknum / secpercyl;
1099 head = (blknum % secpercyl) / secpertrk;
1100 sector = blknum % secpertrk;
88b2b5dc 1101 cylin += cyloff;
cae41ec5 1102
8f65e707 1103#ifdef notyet
cae41ec5
WN
1104 /*
1105 * See if the current block is in the bad block list.
1106 * (If we have one.)
1107 */
1108 for (bt_ptr = dkbad[unit].bt_bad;
1109 bt_ptr->bt_cyl != -1; bt_ptr++) {
1110 if (bt_ptr->bt_cyl > cylin)
1111 /* Sorted list, and we passed our cylinder.
1112 quit. */
1113 break;
1114 if (bt_ptr->bt_cyl == cylin &&
1115 bt_ptr->bt_trksec == (head << 8) + sector) {
1116 /*
1117 * Found bad block. Calculate new block addr.
1118 * This starts at the end of the disk (skip the
1119 * last track which is used for the bad block list),
1120 * and works backwards to the front of the disk.
1121 */
8f65e707
WN
1122 blknum = (du->dk_dd.d_secperunit)
1123 - du->dk_dd.d_nsectors
cae41ec5
WN
1124 - (bt_ptr - dkbad[unit].bt_bad) - 1;
1125 cylin = blknum / secpercyl;
1126 head = (blknum % secpercyl) / secpertrk;
1127 sector = blknum % secpertrk;
1128 break;
1129 }
1130
8f65e707
WN
1131#endif
1132 sector++; /* origin 1 */
1133
cae41ec5 1134 /* select drive. */
8f65e707
WN
1135 outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf));
1136 while ((inb(wdc+wd_status) & WDCS_READY) == 0) ;
cae41ec5
WN
1137
1138 /* transfer some blocks */
8f65e707
WN
1139 outb(wdc+wd_sector, sector);
1140 outb(wdc+wd_seccnt,1);
1141 outb(wdc+wd_cyl_lo, cylin);
1142 outb(wdc+wd_cyl_hi, cylin >> 8);
cae41ec5
WN
1143#ifdef notdef
1144 /* lets just talk about this first...*/
8f65e707
WN
1145 pg ("sdh 0%o sector %d cyl %d addr 0x%x",
1146 inb(wdc+wd_sdh), inb(wdc+wd_sector),
1147 inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr) ;
1148#endif
1149#ifdef ODYSSEUS
1150if(cylin < 46 || cylin > 91)pg("oops");
1151#endif
1152#ifdef PRIAM
1153if(cylin < 40 || cylin > 79)pg("oops");
cae41ec5 1154#endif
8f65e707 1155 outb(wdc+wd_command, WDCC_WRITE);
cae41ec5
WN
1156
1157 /* Ready to send data? */
8f65e707
WN
1158 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ;
1159 if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
1160
1161 outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256);
1162 (int) addr += 512;
1163
1164 if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
cae41ec5 1165 /* Check data request (should be done). */
8f65e707 1166 if (inb(wdc+wd_status) & WDCS_DRQ) return(EIO) ;
cae41ec5
WN
1167
1168 /* wait for completion */
8f65e707 1169 for ( i = 1000000 ; inb(wdc+wd_status) & WDCS_BUSY ; i--) {
cae41ec5 1170 if (i < 0) return (EIO) ;
cae41ec5
WN
1171 }
1172 /* error check the xfer */
8f65e707 1173 if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ;
cae41ec5
WN
1174 /* update block count */
1175 num--;
1176 blknum++ ;
cae41ec5 1177if (num % 100 == 0) printf(".") ;
cae41ec5
WN
1178 }
1179 return(0);
cae41ec5
WN
1180}
1181#endif