* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* @(#)wd.c 7.3 (Berkeley) 5/4/91
/* device driver for winchester disk */
#include "i386/isa/isa.h"
#include "i386/isa/wdreg.h"
#define NWD 2 /* number of hard disk units supported, max 2 */
#define RETRIES 5 /* number of retries before giving up */
extern struct disklabel disklabel
;
struct disklabel wdsizes
[NWD
];
extern cyloffset
; /* bootstrap's idea of cylinder for disklabel */
* Record for the bad block forwarding code.
* This is initialized to be empty until the bad-sector table
#define TRKSEC(trk,sec) ((trk << 8) + sec)
register struct disklabel
*dd
;
dd
= &wdsizes
[io
->i_unit
];
_stop("Invalid partition number");
_stop("Invalid controller number");
_stop("wd initialization error");
io
->i_boff
= dd
->d_partitions
[io
->i_part
].p_offset
;
register int iosize
; /* number of sectors to do IO for this loop */
register struct disklabel
*dd
;
printf("wdstrat %d %d ", unit
, partition
);
iosize
= io
->i_cc
/ dd
->d_secsize
;
* Convert PGSIZE "blocks" to sectors.
* Note: doing the conversions this way limits the partition size
* to about 8 million sectors (1-8 Gb).
sector
= (unsigned long) io
->i_bn
* DEV_BSIZE
/ dd
->d_secsize
;
nblocks
= dd
->d_partitions
[partition
].p_size
;
if (iosize
< 0 || sector
+ iosize
> nblocks
|| sector
< 0) {
printf("bn = %d; sectors = %d; partition = %d; fssize = %d\n",
io
->i_bn
, iosize
, partition
, nblocks
);
printf("wdstrategy - I/O out of filesystem boundaries\n");
if (io
->i_bn
* DEV_BSIZE
% dd
->d_secsize
) {
printf("wdstrategy - transfer starts in midsector\n");
if (io
->i_cc
% dd
->d_secsize
) {
printf("wd: transfer of partial sector\n");
if (wdio(func
, unit
, sector
, address
))
address
+= dd
->d_secsize
;
* Routine to do a one-sector I/O operation, and wait for it
wdio(func
, unit
, blknm
, addr
)
long cylin
, head
, sector
;
/* Calculate data for output. */
cylin
= blknm
/ dd
->d_secpercyl
;
head
= (blknm
% dd
->d_secpercyl
) / dd
->d_nsectors
;
sector
= blknm
% dd
->d_nsectors
;
* See if the current block is in the bad block list.
if (blknm
> BBSIZE
/DEV_BSIZE
) /* should be BBSIZE */
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.
printf("--- badblock code -> Old = %d; ",
blknm
= dd
->d_secperunit
- dd
->d_nsectors
- (bt_ptr
- dkbad
[unit
].bt_bad
) - 1;
cylin
= blknm
/ dd
->d_secpercyl
;
head
= (blknm
% dd
->d_secpercyl
) / dd
->d_nsectors
;
sector
= blknm
% dd
->d_nsectors
;
printf("new = %d\n", blknm
);
printf("sec %d sdh %x cylin %d ", sector
,
WDSD_IBM
| (unit
<<4) | (head
& 0xf), cylin
);
outb(wdc
+wd_precomp
, 0xff);
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_status
) & WDCS_READY
) == 0) ;
outb(wdc
+wd_command
, opcode
);
while (opcode
== WDCC_READ
&& (inb(wdc
+wd_status
) & WDCS_BUSY
))
/* Did we get an error? */
if (opcode
== WDCC_READ
&& (inb(wdc
+wd_status
) & WDCS_ERR
))
/* Ready to remove data? */
while ((inb(wdc
+wd_status
) & WDCS_DRQ
) == 0) ;
insw(wdc
+wd_data
,addr
,256);
else outsw(wdc
+wd_data
,addr
,256);
/* Check data request (should be done). */
if (inb(wdc
+wd_status
) & WDCS_DRQ
) goto error
;
while (opcode
== WDCC_WRITE
&& (inb(wdc
+wd_status
) & WDCS_BUSY
)) ;
if (inb(wdc
+wd_status
) & WDCS_ERR
) goto error
;
erro
= inb(wdc
+wd_error
);
printf("wd%d: hard error: sector %d status %x error %x\n", unit
,
blknm
, inb(wdc
+wd_status
), erro
);
printf("wd%d: hard %s error: sector %d status %b error %b\n", unit
,
opcode
== WDCC_READ
? "read" : "write", blknm
,
inb(wdc
+wd_status
), WDCS_BITS
, erro
, WDERR_BITS
);
if (open
[unit
]) return(0);
wdcport
= io
->i_ctlr
? IO_WD2
: IO_WD1
;
outb(wdc
+wd_ctlr
, 12); wait(10); outb(wdc
+wd_ctlr
, 8);
/* set SDH, step rate, do restore to recalibrate drive */
outb(wdc
+wd_sdh
, WDSD_IBM
| (unit
<< 4));
outb(wdc
+wd_command
, WDCC_RESTORE
| WD_STEP
);
if ((i
= inb(wdc
+wd_status
)) & WDCS_ERR
) {
printf("wd%d: recal status %x error %x\n",
unit, i, inb(wdc+wd_error));
printf("wd%d: recal status %b error %b\n",
unit
, i
, WDCS_BITS
, inb(wdc
+wd_error
), WDERR_BITS
);
* Some controllers require this (after a recal they
* revert to a logical translation mode to compensate for
* dos limitation on 10-bit cylinders -- *shudder* -wfj)
* note: cylinders *must* be fewer than or equal to 8 to
* compensate for some IDE drives that latch this for all time.
outb(wdc
+wd_sdh
, WDSD_IBM
| (unit
<< 4) + 8 -1);
outb(wdc
+wd_seccnt
, 35 );
outb(wdc
+wd_cyl_lo
, 1224);
outb(wdc
+wd_cyl_hi
, 1224/256);
outb(wdc
+wd_command
, 0x91);
while (inb(wdc
+wd_status
) & WDCS_BUSY
) ;
* Read in LABELSECTOR to get the pack label and geometry.
outb(wdc
+wd_precomp
, 0xff); /* sometimes this is head bit 3 */
outb(wdc
+wd_sector
, LABELSECTOR
+ 1);
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 ((i
= inb(wdc
+wd_status
)) & WDCS_ERR
) {
printf("wd%d: reading label, status %x error %x\n",
printf("wd%d: reading label, status %b error %b\n",
unit
, i
, WDCS_BITS
, err
, WDERR_BITS
);
/* Ready to remove data? */
while ((inb(wdc
+wd_status
) & WDCS_DRQ
) == 0) ;
i
= insw(wdc
+wd_data
, buf
, 256);
printf("magic %x,insw %x, %x\n",
((struct disklabel
*) (buf
+ LABELOFFSET
))->d_magic
, i
, buf
);
if (((struct disklabel
*) (buf
+ LABELOFFSET
))->d_magic
== DISKMAGIC
) {
*dd
= * (struct disklabel
*) (buf
+ LABELOFFSET
);
printf("wd%d: bad disk label\n", unit
);
if (io
->i_flgs
& F_FILE
) return(-1);
dkbad
[unit
].bt_bad
[0].bt_cyl
= -1;
dd
->d_secpercyl
= 1999999 ; dd
->d_nsectors
= 17 ;
outb(wdc
+wd_precomp
, 0xff); /* force head 3 bit off */
printf("magic %x sect %d\n", dd
->d_magic
, dd
->d_nsectors
);
/* now that we know the disk geometry, tell the controller */
outb(wdc
+wd_cyl_lo
, dd
->d_ncylinders
);
outb(wdc
+wd_cyl_hi
, (dd
->d_ncylinders
)>>8);
outb(wdc
+wd_sdh
, WDSD_IBM
| (unit
<< 4) + dd
->d_ntracks
-1);
outb(wdc
+wd_seccnt
, dd
->d_nsectors
);
outb(wdc
+wd_command
, 0x91);
while (inb(wdc
+wd_status
) & WDCS_BUSY
) ;
dkbad
[unit
].bt_bad
[0].bt_cyl
= -1;
outb(wdc
+wd_precomp
, dd
->d_precompcyl
/ 4);
* Read bad sector table into memory.
int blknm
= dd
->d_secperunit
- dd
->d_nsectors
+ i
;
errcnt
= wdio(F_READ
, unit
, blknm
, buf
);
} while (errcnt
&& (i
+= 2) < 10 && i
< dd
->d_nsectors
);
db
= (struct dkbad
*)(buf
);
#define DKBAD_MAGIC 0x4321
if (errcnt
== 0 && db
->bt_mbz
== 0 && db
->bt_flag
== DKBAD_MAGIC
)
printf("wd%d: error in bad-sector file\n", unit
);
dkbad
[unit
].bt_bad
[0].bt_cyl
= -1;
while (inb(wdc
+wd_status
) & WDCS_BUSY
)
while ((inb(wdc
+wd_status
) & WDCS_READY
) == 0)