* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* @(#)wd.c 5.3 (Berkeley) %G%
#include "machine/device.h"
#define RETRIES 5 /* number of retries before giving up */
#define MAXTRANSFER 256 /* max size of transfer in page clusters */
#define WDUNIT(dev) ((minor(dev) & 070) >> 3)
#define b_cylin b_resid /* cylinder number for doing IO to */
/* shares an entry in the buf struct */
* Drive states. Used for open and format operations.
* States < OPEN (> 0) are transient, during an open operation.
* OPENRAW is used for unlabeled disks, and for floppies, to inhibit
#define RAWDISK 8 /* raw disk operation, no translation*/
#define ISRAWSTATE(s) (RAWDISK&(s)) /* are we in a raw state? */
#define DISKSTATE(s) (~RAWDISK&(s)) /* are we in a given state regardless
of raw or cooked mode? */
#define CLOSED 0 /* disk is closed. */
/* "cooked" disk states */
#define WANTOPEN 1 /* open requested, not started */
#define RECAL 2 /* doing restore */
#define RDLABEL 3 /* reading pack label */
#define RDBADTBL 4 /* reading bad-sector table */
#define OPEN 5 /* done with open */
#define WANTOPENRAW (WANTOPEN|RAWDISK) /* raw WANTOPEN */
#define RECALRAW (RECAL|RAWDISK) /* raw open, doing restore */
#define OPENRAW (OPEN|RAWDISK) /* open, but unlabeled disk or floppy */
* The structure of a disk drive.
struct disklabel dk_dd
; /* device configuration data */
long dk_bc
; /* byte count left */
short dk_skip
; /* blocks already transferred */
char dk_unit
; /* physical unit number */
char dk_sdh
; /* sdh prototype */
char dk_state
; /* control state */
u_char dk_status
; /* copy of status reg. */
u_char dk_error
; /* copy of error reg. */
short dk_open
; /* open/closed refcnt */
* This label is used as a default when initializing a new or raw disk.
* It really only lets us access the first track until we know more.
struct disklabel dflt_sizes
= {
17, /* # of sectors per track */
15, /* # of tracks per cylinder */
918, /* # of cylinders per unit */
17*15, /* # of sectors per cylinder */
918*15*17, /* # of sectors per unit */
0 /* write precomp cylinder (none) */
7560, 0, /* A=root filesystem */
123930, 0, /* C=whole disk */
static struct dkbad dkbad
[NWD
];
struct disk wddrives
[NWD
] = {0}; /* table of units */
struct buf wdutab
[NWD
] = {0}; /* head of queue per drive */
struct buf rwdbuf
[NWD
] = {0}; /* buffers for raw IO */
long wdxfer
[NWD
] = {0}; /* count of transfers */
int writeprotected
[NWD
] = { 0 };
int wdprobe(), wdattach(), wdintr();
struct driver wddriver
= {
outb(wdc
+wd_error
, 0x5a) ; /* error register not writable */
/*wdp->wd_cyl_hi = 0xff ;/* only two bits of cylhi are implemented */
outb(wdc
+wd_cyl_lo
, 0xa5) ; /* but all of cyllo are implemented */
if(inb(wdc
+wd_error
) != 0x5a /*&& wdp->wd_cyl_hi == 3*/
&& inb(wdc
+wd_cyl_lo
) == 0xa5)
* attach each drive if possible.
/* Read/write routine for a buffer. Finds the proper unit, range checks
* arguments, and schedules the transfer. Does not wait for the transfer
* to complete. Multi-page transfers are supported. All I/O requests must
* be a multiple of a sector in length.
register struct buf
*bp
; /* IO operation to perform */
register struct disk
*du
; /* Disk unit to do the IO. */
long nblocks
, cyloff
, blknum
;
int unit
= WDUNIT(bp
->b_dev
), xunit
= minor(bp
->b_dev
) & 7;
if ((unit
>= NWD
) || (bp
->b_blkno
< 0)) {
printf("wdstrat: unit = %d, blkno = %d, bcount = %d\n",
unit
, bp
->b_blkno
, bp
->b_bcount
);
pg("wd:error in wdstrategy");
if (writeprotected
[unit
] && (bp
->b_flags
& B_READ
) == 0) {
printf("wd%d: write protected\n", unit
);
if (DISKSTATE(du
->dk_state
) != OPEN
)
* Convert DEV_BSIZE "blocks" to sectors.
* Note: doing the conversions this way limits the partition size
* to about 8 million sectors (1-8 Gb).
blknum
= (unsigned long) bp
->b_blkno
* DEV_BSIZE
/ du
->dk_dd
.dk_secsize
;
if (((u_long
) bp
->b_blkno
* DEV_BSIZE
% du
->dk_dd
.dk_secsize
!= 0) ||
bp
->b_bcount
>= MAXTRANSFER
* CLBYTES
) {
nblocks
= du
->dk_dd
.dk_partition
[xunit
].nblocks
;
cyloff
= du
->dk_dd
.dk_partition
[xunit
].cyloff
;
if (blknum
+ (bp
->b_bcount
/ du
->dk_dd
.dk_secsize
) > nblocks
) {
bp
->b_resid
= bp
->b_bcount
;
bp
->b_cylin
= blknum
/ du
->dk_dd
.dk_secpercyl
+ cyloff
;
wdustart(du
); /* start drive if idle */
wdstart(s
); /* start IO if controller idle */
/* Routine to queue a read or write command to the controller. The request is
* linked into the active list for the controller. If the controller is idle,
* the transfer is started.
register struct disk
*du
;
register struct buf
*bp
, *dp
;
dp
= &wdutab
[du
->dk_unit
];
if (wdtab
.b_actf
== NULL
) /* link unit into active list */
wdtab
.b_actl
->b_forw
= dp
;
dp
->b_active
= 1; /* mark the drive as busy */
* Controller startup routine. This does the calculation, and starts
* a single-sector read or write operation. Called to start a transfer,
* or from the interrupt routine to continue a multi-sector transfer.
* 1. The transfer length must be an exact multiple of the sector size.
register struct disk
*du
; /* disk unit for IO */
register wdc
= IO_WD0
; /*XXX*/
register struct bt_bad
*bt_ptr
;
long blknum
, pagcnt
, cylin
, head
, sector
;
long secpertrk
, secpercyl
, addr
, i
;
wdtab
.b_actf
= dp
->b_forw
;
unit
= WDUNIT(bp
->b_dev
);
if (DISKSTATE(du
->dk_state
) <= RDLABEL
) {
dp
->b_actf
= bp
->av_forw
;
minor_dev
= minor(bp
->b_dev
) & 7;
secpertrk
= du
->dk_dd
.dk_nsectors
;
secpercyl
= du
->dk_dd
.dk_secpercyl
;
* Convert DEV_BSIZE "blocks" to sectors.
blknum
= (unsigned long) bp
->b_blkno
* DEV_BSIZE
/ du
->dk_dd
.dk_secsize
dprintf(DDSK
,"\nwdstart %d: %s %d@%d; map ", unit
,
(bp
->b_flags
& B_READ
) ? "read" : "write",
dprintf(DDSK
," %d)", du
->dk_skip
);
addr
= (int) bp
->b_un
.b_addr
;
if(du
->dk_skip
==0) du
->dk_bc
= bp
->b_bcount
;
cylin
= blknum
/ secpercyl
;
head
= (blknum
% secpercyl
) / secpertrk
;
sector
= blknum
% secpertrk
+ 1;
if (DISKSTATE(du
->dk_state
) == OPEN
)
cylin
+= du
->dk_dd
.dk_partition
[minor_dev
].cyloff
;
* See if the current block is in the bad block list.
* (If we have one, and not formatting.)
if (du
->dk_state
== OPEN
)
for (bt_ptr
= dkbad
[unit
].bt_bad
; bt_ptr
->bt_cyl
!= -1; bt_ptr
++) {
if (bt_ptr
->bt_cyl
> cylin
)
/* Sorted list, and we passed our cylinder. quit. */
if (bt_ptr
->bt_cyl
== cylin
&&
bt_ptr
->bt_trksec
== (head
<< 8) + sector
) {
* Found bad block. Calculate new block addr.
* This starts at the end of the disk (skip the
* last track which is used for the bad block list),
* and works backwards to the front of the disk.
dprintf(DDSK
,"--- badblock code -> Old = %d; ",
blknum
= du
->dk_dd
.dk_secperunit
- du
->dk_dd
.dk_nsectors
- (bt_ptr
- dkbad
[unit
].bt_bad
) - 1;
cylin
= blknum
/ secpercyl
;
head
= (blknum
% secpercyl
) / secpertrk
;
sector
= blknum
% secpertrk
;
dprintf(DDSK
, "new = %d\n", blknum
);
wdtab
.b_active
= 1; /* mark controller active */
outb(wdc
+wd_precomp
, 0xff);
/*wr(wdc+wd_precomp, du->dk_dd.dk_precompcyl / 4);*/
/*if (bp->b_flags & B_FORMAT) {
wr(wdc+wd_sector, du->dk_dd.dk_gap3);
wr(wdc+wd_seccnt, du->dk_dd.dk_nsectors);
outb(wdc
+wd_sector
, sector
);
outb(wdc
+wd_cyl_lo
, cylin
);
outb(wdc
+wd_cyl_hi
, cylin
>> 8);
/* Set up the SDH register (select drive). */
outb(wdc
+wd_sdh
, WDSD_IBM
| (unit
<<4) | (head
& 0xf));
while ((inb(wdc
+wd_altsts
) & WDCS_READY
) == 0) ;
/*if (bp->b_flags & B_FORMAT)
wr(wdc+wd_command, WDCC_FORMAT);
(bp
->b_flags
& B_READ
)? WDCC_READ
: WDCC_WRITE
);
dprintf(DDSK
,"sector %d cylin %d head %d addr %x\n",
sector
, cylin
, head
, addr
);
/* If this is a read operation, just go away until it's done. */
if (bp
->b_flags
& B_READ
) return;
/* Ready to send data? */
while ((inb(wdc
+wd_altsts
) & WDCS_DRQ
) == 0)
nulldev(); /* So compiler won't optimize out */
/* ASSUMES CONTIGUOUS MEMORY */
buff_addr
+= (du
->dk_skip
* 512)/* & CLOFSET*/;
outsw (wdc
+wd_data
, buff_addr
, 256);
* these are globally defined so they can be found
* by the debugger easily in the case of a system crash
unsigned char wd_errstat
;
/* Interrupt routine for the controller. Acknowledge the interrupt, check for
* errors on the current operation, mark it done if necessary, and start
* the next request. Also check for a partially done transfer, and
* continue with the next chunk if so.
register struct disk
*du
;
register wdc
= IO_WD0
; /*XXX*/
register struct buf
*bp
, *dp
;
/* Shouldn't need this, but it may be a slow controller. */
while ((status
= inb(wdc
+wd_altsts
)) & WDCS_BUSY
)
printf("wd: extra interrupt\n");
du
= &wddrives
[WDUNIT(bp
->b_dev
)];
partch
= "abcdefgh"[minor(bp
->b_dev
)&7] ;
if (DISKSTATE(du
->dk_state
) <= RDLABEL
) {
if (status
& (WDCS_ERR
| WDCS_ECCCOR
)) {
printf("error %x\n", wd_errstat
);
/*if (bp->b_flags & B_FORMAT) {
du->dk_error = wdp->wd_error;
wd_errstat
= inb(wdc
+wd_error
); /* save error status */
wd_errsector
= (bp
->b_cylin
* du
->dk_dd
.dk_secpercyl
) +
(((unsigned long) bp
->b_blkno
* DEV_BSIZE
/
du
->dk_dd
.dk_secsize
) % du
->dk_dd
.dk_secpercyl
) +
+ du
->dk_skip
* du
->dk_dd
.dk_secsize
/ DEV_BSIZE
;
if (++wdtab
.b_errcnt
< RETRIES
)
printf("wd%d%c: ", du
->dk_unit
, partch
);
"hard %s error, sn %d bn %d status %b error %b\n",
(bp
->b_flags
& B_READ
)? "read":"write",
wd_errsector
, wd_errbn
, status
, WDCS_BITS
,
bp
->b_flags
|= B_ERROR
; /* flag the error */
log(LOG_WARNING
,"wd%d%c: soft ecc sn %d bn %d\n",
du
->dk_unit
, partch
, wd_errsector
,
* If this was a successful read operation, fetch the data.
if (((bp
->b_flags
& (B_READ
| B_ERROR
)) == B_READ
) && wdtab
.b_active
) {
chk
= min(256,(du
->dk_bc
/2));
/* Ready to receive data? */
while ((inb(wdc
+wd_status
) & WDCS_DRQ
) == 0)
/*dprintf(DDSK,"addr %x\n", (int)bp->b_un.b_addr + du->dk_skip * 512);*/
insw(wdc
+wd_data
,(int)bp
->b_un
.b_addr
+ du
->dk_skip
* 512 ,chk
);
while(chk
++ < 256) insw(wdc
+wd_data
,&dummy
,1);
if ((bp
->b_flags
& B_ERROR
) == 0) {
du
->dk_skip
++; /* Add to successful sectors. */
log(LOG_WARNING
, "wd%d%c: ",
"soft %s error, sn %d bn %d error %b retries %d\n",
(bp
->b_flags
& B_READ
) ? "read" : "write",
wd_errsector
, wd_errbn
, wd_errstat
,
WDERR_BITS
, wdtab
.b_errcnt
);
/* see if more to transfer */
/*if (du->dk_skip < (bp->b_bcount + 511) / 512) {*/
return; /* next chunk is started */
/* done with this transfer, with or without error */
wdtab
.b_actf
= dp
->b_forw
;
dp
->b_actf
= bp
->av_forw
;
wdustart(du
); /* requeue disk if more io to do */
wdstart(); /* start IO on next drive */
register unsigned int unit
;
register struct disk
*du
;
if (unit
>= NWD
) return (ENXIO
) ;
return(0); /* already is open, don't mess with it */
if (du
->dk_state
&& DISKSTATE(du
->dk_state
) <= OPEN
)
wdutab
[unit
].b_actf
= NULL
;
du->dk_state = WANTOPENRAW;
* Use the default sizes until we've read the label,
* or longer if there isn't one there.
* Recal, read of disk label will be done in wdcontrol
* during first read operation.
bp
->b_dev
= dev
& 0xff00;
bp
->b_blkno
= bp
->b_bcount
= 0;
if (bp
->b_flags
& B_ERROR
) {
if (du
->dk_state
== OPENRAW
) {
* Read bad sector table into memory.
bp
->b_flags
= B_BUSY
| B_READ
;
bp
->b_blkno
= du
->dk_dd
.dk_secperunit
- du
->dk_dd
.dk_nsectors
if (du
->dk_dd
.dk_secsize
> DEV_BSIZE
)
bp
->b_blkno
*= du
->dk_dd
.dk_secsize
/ DEV_BSIZE
;
bp
->b_blkno
/= DEV_BSIZE
/ du
->dk_dd
.dk_secsize
;
bp
->b_bcount
= du
->dk_dd
.dk_secsize
;
bp
->b_cylin
= du
->dk_dd
.dk_ncylinders
- 1;
} while ((bp
->b_flags
& B_ERROR
) && (i
+= 2) < 10 &&
i
< du
->dk_dd
.dk_nsectors
);
db
= (struct dkbad
*)(bp
->b_un
.b_addr
);
if ((bp
->b_flags
& B_ERROR
) == 0 && db
->bt_mbz
== 0 &&
db
->bt_flag
== DKBAD_MAGIC
) {
printf("wd%d: %s bad-sector file\n", unit
,
(bp
->b_flags
& B_ERROR
) ? "can't read" : "format error in");
bp
->b_flags
= B_INVAL
| B_AGE
;
* Implement operations other than read/write.
* Called from wdstart or wdintr during opens and formats.
* Uses finite-state-machine to track progress of operation in progress.
* Returns 0 if operation still in progress, 1 if completed.
register struct disk
*du
;
register wdc
= IO_WD0
; /*XXX*/
extern int bootdev
, cyloffset
;
du
= &wddrives
[WDUNIT(bp
->b_dev
)];
switch (DISKSTATE(du
->dk_state
)) {
case WANTOPEN
: /* set SDH, step rate, do restore */
dprintf(DDSK
,"wd%d: recal ", unit
);
s
= splbio(); /* not called from intr level ... */
outb(wdc
+wd_sdh
, WDSD_IBM
| (unit
<< 4));
outb(wdc
+wd_command
, WDCC_RESTORE
| WD_STEP
);
if ((stat
= inb(wdc
+wd_altsts
)) & WDCS_ERR
) {
printf("wd%d: recal", du
->dk_unit
);
printf(": status %b error %b\n",
inb(wdc
+wd_error
), WDERR_BITS
);
if (++wdtab
.b_errcnt
< RETRIES
)
if (ISRAWSTATE(du
->dk_state
)) {
dprintf(DDSK
,"rdlabel ");
* Read in sector 0 to get the pack label and geometry.
outb(wdc
+wd_precomp
, 0xff);/* sometimes this is head bit 3 */
/*if (bp->b_dev == bootdev) {
(wdc+wd_cyl_lo = cyloffset & 0xff;
(wdc+wd_cyl_hi = cyloffset >> 8;
outb(wdc
+wd_cyl_lo
, (cyloffset
& 0xff));
outb(wdc
+wd_cyl_hi
, (cyloffset
>> 8));
outb(wdc
+wd_sdh
, WDSD_IBM
| (unit
<< 4));
outb(wdc
+wd_command
, WDCC_READ
);
if ((stat
= inb(wdc
+wd_status
)) & WDCS_ERR
) {
if (++wdtab
.b_errcnt
< RETRIES
)
printf("wd%d: read label", unit
);
insw(wdc
+wd_data
, bp
->b_un
.b_addr
, 256);
if (((struct disklabel
*)
(bp
->b_un
.b_addr
+ LABELOFFSET
))->dk_magic
== DISKMAGIC
) {
* (struct disklabel
*) (bp
->b_un
.b_addr
+ LABELOFFSET
);
printf("wd%d: bad disk label\n", du
->dk_unit
);
if (du
->dk_state
== RDLABEL
)
* The rest of the initialization can be done
panic("wdcontrol %x", du
->dk_state
);
printf(": status %b error %b\n",
stat
, WDCS_BITS
, inb(wdc
+wd_error
), WDERR_BITS
);
du
= &wddrives
[WDUNIT(dev
)];
/*if (du->dk_open == 0) du->dk_state = CLOSED ; does not work */
wdioctl(dev
,cmd
,addr
,flag
)
register struct disk
*du
;
*(struct disklabel
*)addr
= du
->dk_dd
;
*(struct disklabel
**)addr
= &(du
->dk_dd
);
if ((flag
& FWRITE
) == 0)
register struct format_op
*fop
;
fop
= (struct format_op
*)addr
;
aiov
.iov_base
= fop
->df_buf
;
aiov
.iov_len
= fop
->df_count
;
auio
.uio_resid
= fop
->df_count
;
fop
->df_startblk
* du
->dk_dd
.dk_secsize
;
error
= physio(wdformat
, &rwdbuf
[unit
], dev
, B_WRITE
,
fop
->df_count
-= auio
.uio_resid
;
fop
->df_reg
[0] = du
->dk_status
;
fop
->df_reg
[1] = du
->dk_error
;
* Routines to do raw IO for a unit.
wdread(dev
, uio
) /* character read routine */
if (unit
>= NWD
) return(ENXIO
);
return(physio(wdstrategy
, &rwdbuf
[unit
], dev
, B_READ
, minphys
, uio
));
wdwrite(dev
, uio
) /* character write routine */
if (unit
>= NWD
) return(ENXIO
);
return(physio(wdstrategy
, &rwdbuf
[unit
], dev
, B_WRITE
, minphys
, uio
));
register unit
= WDUNIT(dev
) ;
register xunit
= minor(dev
) & 07;
register struct disk
*du
;
if (unit
>= NWD
) return(-1);
if (wddrives
[unit
].dk_state
== 0) /*{
if (val < 0) return (val) ;
return((int)((u_long
)du
->dk_dd
.dk_partition
[xunit
].nblocks
*
du
->dk_dd
.dk_secsize
/ 512));
wddump(dev
) /* dump core after a system crash */
register struct disk
*du
; /* disk unit to do the IO */
register struct wd1010
*wdp
= (struct wd1010
*) VA_WD
;
register struct bt_bad
*bt_ptr
;
long num
; /* number of sectors to write */
long cyloff
, blknum
, blkcnt
;
long cylin
, head
, sector
;
long secpertrk
, secpercyl
, nblocks
, i
;
extern int dumplo
, totalclusters
;
static wddoingadump
= 0 ;
addr
= (char *) PA_RAM
; /* starting address */
/* size of memory to dump */
num
= totalclusters
* CLSIZE
- PA_RAM
/ PGSIZE
;
unit
= WDUNIT(dev
) ; /* eventually support floppies? */
xunit
= minor(dev
) & 7; /* file system */
/* check for acceptable drive number */
if (unit
>= NWD
) return(ENXIO
);
/* was it ever initialized ? */
if (du
->dk_state
< OPEN
) return (ENXIO
) ;
/* Convert to disk sectors */
num
= (u_long
) num
* PGSIZE
/ du
->dk_dd
.dk_secsize
;
/* check if controller active */
/*if (wdtab.b_active) return(EFAULT); */
if (wddoingadump
) return(EFAULT
);
secpertrk
= du
->dk_dd
.dk_nsectors
;
secpercyl
= du
->dk_dd
.dk_secpercyl
;
nblocks
= du
->dk_dd
.dk_partition
[xunit
].nblocks
;
cyloff
= du
->dk_dd
.dk_partition
[xunit
].cyloff
;
/* check transfer bounds against partition size */
if ((dumplo
< 0) || ((dumplo
+ num
) >= nblocks
))
/*wdtab.b_active = 1; /* mark controller active for if we
wddoingadump
= 1 ; i
= 100000 ;
while ((wdp
->wd_status
& WDCS_BUSY
) && (i
-- > 0)) nulldev() ;
inb(wdc
+wd_sdh
= du
->dk_sdh
;
inb(wdc
+wd_command
= WDCC_RESTORE
| WD_STEP
;
while (inb(wdc
+wd_status
& WDCS_BUSY
) nulldev() ;
if (blkcnt
> MAXTRANSFER
) blkcnt
= MAXTRANSFER
;
if ((blknum
+ blkcnt
- 1) / secpercyl
!= blknum
/ secpercyl
)
blkcnt
= secpercyl
- (blknum
% secpercyl
);
/* keep transfer within current cylinder */
/* compute disk address */
cylin
= blknum
/ secpercyl
;
head
= (blknum
% secpercyl
) / secpertrk
;
sector
= blknum
% secpertrk
;
* See if the current block is in the bad block list.
for (bt_ptr
= dkbad
[unit
].bt_bad
;
bt_ptr
->bt_cyl
!= -1; bt_ptr
++) {
if (bt_ptr
->bt_cyl
> cylin
)
/* Sorted list, and we passed our cylinder.
if (bt_ptr
->bt_cyl
== cylin
&&
bt_ptr
->bt_trksec
== (head
<< 8) + sector
) {
* Found bad block. Calculate new block addr.
* This starts at the end of the disk (skip the
* last track which is used for the bad block list),
* and works backwards to the front of the disk.
blknum
= (du
->dk_dd
.dk_secperunit
)
- (bt_ptr
- dkbad
[unit
].bt_bad
) - 1;
cylin
= blknum
/ secpercyl
;
head
= (blknum
% secpercyl
) / secpertrk
;
sector
= blknum
% secpertrk
;
inb(wdc
+wd_sdh
= du
->dk_sdh
| (head
&07);
while ((inb(wdc
+wd_status
& WDCS_READY
) == 0) nulldev();
/* transfer some blocks */
inb(wdc
+wd_sector
= sector
;
inb(wdc
+wd_cyl_lo
= cylin
;
if (du
->dk_dd
.dk_ntracks
> 8) {
inb(wdc
+wd_precomp
= 0; /* set 3rd head bit */
inb(wdc
+wd_precomp
= 0xff; /* set 3rd head bit */
inb(wdc
+wd_cyl_hi
= cylin
>> 8;
/* lets just talk about this first...*/
printf ("sdh 0%o sector %d cyl %d addr 0x%x\n",
wdp
->wd_sdh
, wdp
->wd_sector
,
wdp
->wd_cyl_hi
*256+wdp
->wd_cyl_lo
, addr
) ;
for (i
=10000; i
> 0 ; i
--)
inb(wdc
+wd_command
= WDCC_WRITE
;
/* Ready to send data? */
while ((inb(wdc
+wd_status
& WDCS_DRQ
) == 0) nulldev();
if (inb(wdc
+wd_status
& WDCS_ERR
) return(EIO
) ;
end
= (char *)addr
+ du
->dk_dd
.dk_secsize
;
for (; addr
< end
; addr
+= 8) {
if (inb(wdc
+wd_status
& WDCS_ERR
) return(EIO
) ;
/* Check data request (should be done). */
if (inb(wdc
+wd_status
& WDCS_DRQ
) return(EIO
) ;
/* wait for completion */
for ( i
= 1000000 ; inb(wdc
+wd_status
& WDCS_BUSY
; i
--) {
if (i
< 0) return (EIO
) ;
/* error check the xfer */
if (inb(wdc
+wd_status
& WDCS_ERR
) return(EIO
) ;
if (num
% 100 == 0) printf(".") ;