* 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
* from: @(#)wd.c 7.2 (Berkeley) 5/9/91
* $Id: wd.c,v 1.13 1993/11/18 05:02:22 rgrimes Exp $
/* TODO:peel out buffer at low ipl, speed improvement */
#include "i386/isa/isa.h"
#include "i386/isa/isa_device.h"
#include "i386/isa/icu.h"
#include "i386/isa/wdreg.h"
#define _NWD (NWD - 1) /* One is for the controller XXX 31 Jul 92*/
#define WDCTIMEOUT 10000000 /* arbitrary timeout for drive ready waits */
#define RETRIES 5 /* number of retries before giving up */
#define MAXTRANSFER 32 /* max size of transfer in page clusters */
#define wdnoreloc(dev) (minor(dev) & 0x80) /* ignore partition table */
#define wddospart(dev) (minor(dev) & 0x40) /* use dos partitions */
#define wdunit(dev) ((minor(dev) & 0x38) >> 3)
#define wdpart(dev) (minor(dev) & 0x7)
#define makewddev(maj, unit, part) (makedev(maj,((unit<<3)+part)))
#define WDRAW 3 /* 'd' partition isn't a partition! */
#define b_cylin b_resid /* cylinder number for doing IO to */
/* shares an entry in the buf struct */
* Drive states. Used to initialize drive.
#define CLOSED 0 /* disk is closed. */
#define WANTOPEN 1 /* open requested, not started */
#define RECAL 2 /* doing restore */
#define OPEN 3 /* done with open */
* The structure of a disk drive.
long dk_bc
; /* byte count left */
short dk_skip
; /* blocks already transferred */
char dk_unit
; /* physical unit number */
char dk_state
; /* control state */
u_char dk_status
; /* copy of status reg. */
u_char dk_error
; /* copy of error reg. */
short dk_port
; /* i/o port base */
u_long dk_copenpart
; /* character units open on this drive */
u_long dk_bopenpart
; /* block units open on this drive */
u_long dk_openpart
; /* all units open on this drive */
short dk_wlabel
; /* label writable? */
short dk_flags
; /* drive characteistics found */
#define DKFL_DOSPART 0x00001 /* has DOS partition table */
#define DKFL_QUIET 0x00002 /* report errors back, but don't complain */
#define DKFL_SINGLE 0x00004 /* sector at a time mode */
#define DKFL_ERROR 0x00008 /* processing a disk error */
#define DKFL_BSDLABEL 0x00010 /* has a BSD disk label */
#define DKFL_BADSECT 0x00020 /* has a bad144 badsector table */
#define DKFL_WRITEPROT 0x00040 /* manual unit write protect */
struct wdparams dk_params
; /* ESDI/IDE drive/controller parameters */
struct disklabel dk_dd
; /* device configuration data */
dk_dospartitions
[NDOSPART
]; /* DOS view of disk */
struct dkbad dk_bad
; /* bad sector table */
struct disk
*wddrives
[_NWD
]; /* table of units */
struct buf wdutab
[_NWD
]; /* head of queue per drive */
struct buf rwdbuf
[_NWD
]; /* buffers for raw IO */
long wdxfer
[_NWD
]; /* count of transfers */
struct isa_driver wddriver
= {
static void wdustart(struct disk
*);
static int wdcommand(struct disk
*, int);
static int wdcontrol(struct buf
*);
static int wdsetctlr(dev_t
, struct disk
*);
static int wdgetctlr(int, struct disk
*);
wdprobe(struct isa_device
*dvp
)
if (unit
>= _NWD
) /* 31 Jul 92*/
if ((du
= wddrives
[unit
]) == 0) {
du
= wddrives
[unit
] = (struct disk
*)
malloc (sizeof(struct disk
), M_TEMP
, M_NOWAIT
);
bzero (du
, sizeof(struct disk
)); /* 31 Jul 92*/
wdc
= du
->dk_port
= dvp
->id_iobase
;
/* check if we have registers that work */
outb(wdc
+wd_cyl_lo
, 0xa5) ; /* wd_cyl_lo is read/write */
if(inb(wdc
+wd_cyl_lo
) != 0xa5)
outb(wdc
+wd_ctlr
, (WDCTL_RST
|WDCTL_IDS
));
outb(wdc
+wd_ctlr
, WDCTL_IDS
);
/* execute a controller only command */
if (wdcommand(du
, WDCC_DIAGNOSE
) < 0)
(void) inb(wdc
+wd_error
); /* XXX! */
outb(wdc
+wd_ctlr
, WDCTL_4BIT
);
* Attach each drive if possible.
wdattach(struct isa_device
*dvp
)
/* int unit = dvp->id_unit;*/
for (unit
=0; unit
< _NWD
; unit
++) {
if ((du
= wddrives
[unit
]) == 0) {
du
= wddrives
[unit
] = (struct disk
*)
malloc (sizeof(struct disk
), M_TEMP
, M_NOWAIT
);
bzero (du
, sizeof(struct disk
));
du
->dk_port
= dvp
->id_iobase
;
/* print out description of drive, suppressing multiple blanks*/
if(wdgetctlr(unit
, du
) == 0) {
printf("wd%d: unit %d type ", unit
, unit
);
for (i
= blank
= 0 ; i
< sizeof(du
->dk_params
.wdp_model
); i
++) {
char c
= du
->dk_params
.wdp_model
[i
];
if (blank
&& c
== ' ') continue;
/* 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.
wdstrategy(register struct buf
*bp
)
register struct partition
*p
;
struct disk
*du
; /* Disk unit to do the IO. */
int unit
= wdunit(bp
->b_dev
);
/* valid unit, controller, and request? */
if (unit
>= _NWD
|| bp
->b_blkno
< 0 || (du
= wddrives
[unit
]) == 0) {
/* "soft" write protect check */
if ((du
->dk_flags
& DKFL_WRITEPROT
) && (bp
->b_flags
& B_READ
) == 0) {
/* have partitions and want to use them? */
if ((du
->dk_flags
& DKFL_BSDLABEL
) != 0 && wdpart(bp
->b_dev
) != WDRAW
) {
* do bounds checking, adjust transfer. if error, process.
* if end of partition, just return
if (bounds_check_with_label(bp
, &du
->dk_dd
, du
->dk_wlabel
) <= 0)
/* otherwise, process transfer request */
/* queue transfer on drive, activate drive and controller if idle */
wdustart(du
); /* start drive */
wdstart(s
); /* start controller */
/* toss transfer, we're done early */
* Routine to queue a command to the controller. The unit's
* request is linked into the active list for the controller.
* If the controller is idle, the transfer is started.
wdustart(register struct disk
*du
)
register struct buf
*bp
, *dp
= &wdutab
[du
->dk_unit
];
/* unit already active? */
/* link onto controller queue */
if (wdtab
.b_actf
== NULL
)
wdtab
.b_actl
->b_forw
= dp
;
/* mark the drive unit 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 struct bt_bad
*bt_ptr
;
long blknum
, pagcnt
, cylin
, head
, sector
;
long secpertrk
, secpercyl
, addr
, i
, timeout
;
/* is there a drive for the controller to do a transfer with? */
/* is there a transfer to this drive ? if so, link it on
the controller's queue */
wdtab
.b_actf
= dp
->b_forw
;
/* obtain controller and drive information */
unit
= wdunit(bp
->b_dev
);
/* if not really a transfer, do control operations specially */
if (du
->dk_state
< OPEN
) {
/* calculate transfer details */
blknum
= bp
->b_blkno
+ du
->dk_skip
;
/*if(wddebug)printf("bn%d ", blknum);*/
printf("\nwdstart %d: %s %d@%d; map ", unit
,
(bp
->b_flags
& B_READ
) ? "read" : "write",
printf(" %d)%x", du
->dk_skip
, inb(wdc
+wd_altsts
));
addr
= (int) bp
->b_un
.b_addr
;
du
->dk_bc
= bp
->b_bcount
;
secpertrk
= lp
->d_nsectors
;
secpercyl
= lp
->d_secpercyl
;
if ((du
->dk_flags
& DKFL_BSDLABEL
) != 0 && wdpart(bp
->b_dev
) != WDRAW
)
blknum
+= lp
->d_partitions
[wdpart(bp
->b_dev
)].p_offset
;
cylin
= blknum
/ secpercyl
;
head
= (blknum
% secpercyl
) / secpertrk
;
sector
= blknum
% secpertrk
;
* See if the current block is in the bad block list.
* (If we have one, and not formatting.)
if ((du
->dk_flags
& (DKFL_SINGLE
|DKFL_BADSECT
)) /* 19 Aug 92*/
== (DKFL_SINGLE
|DKFL_BADSECT
))
* BAD144END was done to clean up some old bad code that was
* attempting to compare a u_short to -1. This makes the compilers
* happy and clearly shows what is going on.
#define BAD144END (u_short)(-1)
for (bt_ptr
= du
->dk_bad
.bt_bad
; bt_ptr
->bt_cyl
!= BAD144END
; 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; ",
blknum
= lp
->d_secperunit
- lp
->d_nsectors
- (bt_ptr
- du
->dk_bad
.bt_bad
) - 1;
cylin
= blknum
/ secpercyl
;
head
= (blknum
% secpercyl
) / secpertrk
;
sector
= blknum
% secpertrk
;
printf("new = %d\n", blknum
);
/*if(wddebug)pg("c%d h%d s%d ", cylin, head, sector);*/
sector
+= 1; /* sectors begin with 1, not 0 */
wdtab
.b_active
= 1; /* mark controller active */
/* if starting a multisector transfer, or doing single transfers */
if (du
->dk_skip
== 0 || (du
->dk_flags
& DKFL_SINGLE
)) {
if (wdtab
.b_errcnt
&& (bp
->b_flags
& B_READ
) == 0)
/* must delay 5us to conform to ATA spec */
while (inb(wdc
+wd_status
) & WDCS_BUSY
)
if (++timeout
> WDCTIMEOUT
)
printf("wd.c: Controller busy too long!\n");
outb(wdc
+wd_ctlr
, (WDCTL_RST
|WDCTL_IDS
));
outb(wdc
+wd_ctlr
, WDCTL_IDS
);
(void) inb(wdc
+wd_error
); /* XXX! */
outb(wdc
+wd_ctlr
, WDCTL_4BIT
);
/* stuff the task file */
outb(wdc
+wd_precomp
, lp
->d_precompcyl
/ 4);
if (bp
->b_flags
& B_FORMAT
) {
outb(wdc
+wd_sector
, lp
->d_gap3
);
outb(wdc
+wd_seccnt
, lp
->d_nsectors
);
if (du
->dk_flags
& DKFL_SINGLE
)
outb(wdc
+wd_seccnt
, howmany(du
->dk_bc
, DEV_BSIZE
));
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));
/* wait for drive to become ready */
while ((inb(wdc
+wd_status
) & WDCS_READY
) == 0)
if (++timeout
> WDCTIMEOUT
)
printf("wd.c: Drive busy too long!\n");
outb(wdc
+wd_ctlr
, (WDCTL_RST
|WDCTL_IDS
));
outb(wdc
+wd_ctlr
, WDCTL_IDS
);
(void) inb(wdc
+wd_error
); /* XXX! */
outb(wdc
+wd_ctlr
, WDCTL_4BIT
);
if (bp
->b_flags
& B_FORMAT
)
outb(wdc
+wd_command
, WDCC_FORMAT
);
(bp
->b_flags
& B_READ
)? WDCC_READ
: WDCC_WRITE
);
printf("sector %d cylin %d head %d addr %x sts %x\n",
sector
, cylin
, head
, addr
, inb(wdc
+wd_altsts
));
/* 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_status
) & WDCS_DRQ
) == 0)
if (++timeout
> WDCTIMEOUT
)
printf("wd.c: Drive not ready for too long!\n");
outb(wdc
+wd_ctlr
, (WDCTL_RST
|WDCTL_IDS
));
outb(wdc
+wd_ctlr
, WDCTL_IDS
);
(void) inb(wdc
+wd_error
); /* XXX! */
outb(wdc
+wd_ctlr
, WDCTL_4BIT
);
outsw (wdc
+wd_data
, addr
+du
->dk_skip
* DEV_BSIZE
,
DEV_BSIZE
/sizeof(short));
/* 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.
wdintr(struct intrframe wdif
)
register struct disk
*du
;
register struct buf
*bp
, *dp
;
printf("wd: extra interrupt\n");
du
= wddrives
[wdunit(bp
->b_dev
)];
/* must delay 5us to conform to ATA spec */
while ((status
= inb(wdc
+wd_status
)) & WDCS_BUSY
) ;
/* is it not a transfer, but a control operation? */
if (du
->dk_state
< OPEN
) {
if (status
& (WDCS_ERR
| WDCS_ECCCOR
)) {
du
->dk_error
= inb(wdc
+ wd_error
);
printf("status %x error %x\n", status
, du
->dk_error
);
if((du
->dk_flags
& DKFL_SINGLE
) == 0) {
du
->dk_flags
|= DKFL_ERROR
;
if (bp
->b_flags
& B_FORMAT
) {
bp
->b_error
= EIO
; /* 17 Sep 92*/
/* error or error correction? */
if (++wdtab
.b_errcnt
< RETRIES
) {
diskerr(bp
, "wd", "hard error", LOG_PRINTF
,
du
->dk_skip
, &du
->dk_dd
);
printf( "status %b error %b\n",
inb(wdc
+wd_error
), WDERR_BITS
);
bp
->b_error
= EIO
; /* 17 Sep 92*/
bp
->b_flags
|= B_ERROR
; /* flag the error */
diskerr(bp
, "wd", "soft ecc", 0,
du
->dk_skip
, &du
->dk_dd
);
* 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(DEV_BSIZE
/ sizeof(short), du
->dk_bc
/ sizeof(short));
/* ready to receive data? */
while ((inb(wdc
+wd_status
) & WDCS_DRQ
) == 0)
(int)bp
->b_un
.b_addr
+ du
->dk_skip
* DEV_BSIZE
, chk
);
du
->dk_bc
-= chk
* sizeof(short);
/* for obselete fractional sector reads */
while (chk
++ < 256) insw (wdc
+wd_data
, &dummy
, 1);
if ((bp
->b_flags
& B_ERROR
) == 0) {
du
->dk_skip
++; /* Add to successful sectors. */
diskerr(bp
, "wd", "soft error", 0,
du
->dk_skip
, &du
->dk_dd
);
/* see if more to transfer */
if (du
->dk_bc
> 0 && (du
->dk_flags
& DKFL_ERROR
) == 0) {
return; /* next chunk is started */
} else if ((du
->dk_flags
& (DKFL_SINGLE
|DKFL_ERROR
))
du
->dk_flags
&= ~DKFL_ERROR
;
du
->dk_flags
|= DKFL_SINGLE
;
return; /* redo xfer sector by sector */
/* done with this transfer, with or without error */
du
->dk_flags
&= ~DKFL_SINGLE
;
wdtab
.b_actf
= dp
->b_forw
;
dp
->b_actf
= bp
->av_forw
;
/* anything more on drive queue? */
/* anything more for controller to do? */
wdopen(dev_t dev
, int flags
, int fmt
, struct proc
*p
)
register unsigned int unit
;
register struct disk
*du
;
int part
= wdpart(dev
), mask
= 1 << part
;
if (unit
>= _NWD
) return (ENXIO
) ;
if (du
== 0) return (ENXIO
) ;
if ((du
->dk_flags
& DKFL_BSDLABEL
) == 0) {
du
->dk_flags
|= DKFL_WRITEPROT
;
wdutab
[unit
].b_actf
= NULL
;
* Use the default sizes until we've read the label,
* or longer if there isn't one there.
bzero(&du
->dk_dd
, sizeof(du
->dk_dd
));
#undef d_type /* fix goddamn segments.h! XXX */
du
->dk_dd
.d_type
= DTYPE_ST506
;
du
->dk_dd
.d_ncylinders
= 1024;
du
->dk_dd
.d_secsize
= DEV_BSIZE
;
du
->dk_dd
.d_nsectors
= 17;
du
->dk_dd
.d_secpercyl
= 17*8;
/* read label using "c" partition */
if (msg
= readdisklabel(makewddev(major(dev
), wdunit(dev
), WDRAW
),
wdstrategy
, &du
->dk_dd
, du
->dk_dospartitions
,
log(LOG_WARNING
, "wd%d: cannot find label (%s)\n",
error
= EINVAL
; /* XXX needs translation */
du
->dk_flags
|= DKFL_BSDLABEL
;
du
->dk_flags
&= ~DKFL_WRITEPROT
;
if (du
->dk_dd
.d_flags
& D_BADSECT
)
du
->dk_flags
|= DKFL_BADSECT
;
* Warn if a partion is opened
* that overlaps another partition which is open
* unless one is the "raw" partition (whole disk).
if ((du
->dk_openpart
& mask
) == 0 /*&& part != RAWPART*/ && part
!= WDRAW
) {
pp
= &du
->dk_dd
.d_partitions
[part
];
end
= pp
->p_offset
+ pp
->p_size
;
for (pp
= du
->dk_dd
.d_partitions
;
pp
< &du
->dk_dd
.d_partitions
[du
->dk_dd
.d_npartitions
];
if (pp
->p_offset
+ pp
->p_size
<= start
||
/*if (pp - du->dk_dd.d_partitions == RAWPART)
if (pp
- du
->dk_dd
.d_partitions
== WDRAW
)
if (du
->dk_openpart
& (1 << (pp
-
du
->dk_dd
.d_partitions
)))
"wd%d%c: overlaps open partition (%c)\n",
pp
- du
->dk_dd
.d_partitions
+ 'a');
if (part
>= du
->dk_dd
.d_npartitions
&& part
!= WDRAW
)
/* insure only one open at a time */
du
->dk_copenpart
|= mask
;
du
->dk_bopenpart
|= mask
;
* 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.
wdcontrol(register struct buf
*bp
)
register struct disk
*du
;
int cyl
, trk
, sec
, i
, wdc
;
du
= wddrives
[wdunit(bp
->b_dev
)];
case WANTOPEN
: /* set SDH, step rate, do restore */
printf("wd%d: recal ", unit
);
s
= splbio(); /* not called from intr level ... */
outb(wdc
+wd_sdh
, WDSD_IBM
| (unit
<< 4));
/* must delay 5us to conform to ATA spec */
/* wait for drive and controller to become ready */
for (i
= WDCTIMEOUT
; (inb(wdc
+wd_status
) & (WDCS_READY
|WDCS_BUSY
))
!= WDCS_READY
&& i
-- != 0; )
outb(wdc
+wd_command
, WDCC_RESTORE
| WD_STEP
);
if ((stat
= inb(wdc
+wd_status
)) & WDCS_ERR
) {
printf("wd%d: recal", du
->dk_unit
);
printf(": status %b error %b\n", stat
, WDCS_BITS
,
inb(wdc
+wd_error
), WDERR_BITS
);
if (++wdtab
.b_errcnt
< RETRIES
) {
bp
->b_error
= ENXIO
; /* XXX needs translation */
/* some controllers require this ... */
wdsetctlr(bp
->b_dev
, du
);
* The rest of the initialization can be done
printf(": status %b error %b\n", stat
, WDCS_BITS
,
inb(wdc
+ wd_error
), WDERR_BITS
);
* send a command and wait uninterruptibly until controller is finished.
* return -1 if controller busy for too long, otherwise
* return status. intended for brief controller commands at critical points.
* assumes interrupts are blocked.
wdcommand(struct disk
*du
, int cmd
) {
int timeout
= WDCTIMEOUT
, stat
, wdc
;
/* controller ready for command? */
/* must delay 5us to conform to ATA spec */
while (((stat
= inb(wdc
+ wd_status
)) & WDCS_BUSY
) && timeout
> 0)
/* send command, await results */
outb(wdc
+wd_command
, cmd
);
/* must delay 5us to conform to ATA spec */
while (((stat
= inb(wdc
+wd_status
)) & WDCS_BUSY
) && timeout
> 0)
/* is controller ready to return data? */
while (((stat
= inb(wdc
+wd_status
)) & (WDCS_ERR
|WDCS_DRQ
)) == 0 &&
* issue IDC to drive to tell it just what geometry it is to be.
wdsetctlr(dev_t dev
, struct disk
*du
) {
/*printf("C%dH%dS%d ", du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks,
outb(wdc
+wd_cyl_lo
, du
->dk_dd
.d_ncylinders
+1);
outb(wdc
+wd_cyl_hi
, (du
->dk_dd
.d_ncylinders
+1)>>8);
outb(wdc
+wd_sdh
, WDSD_IBM
| (wdunit(dev
) << 4) + du
->dk_dd
.d_ntracks
-1);
outb(wdc
+wd_seccnt
, du
->dk_dd
.d_nsectors
);
stat
= wdcommand(du
, WDCC_IDC
);
printf("wdsetctlr: status %b error %b\n",
stat
, WDCS_BITS
, inb(wdc
+wd_error
), WDERR_BITS
);
* issue READP to drive to ask it what it is.
wdgetctlr(int u
, struct disk
*du
) {
x
= splbio(); /* not called from intr level ... */
outb(wdc
+wd_sdh
, WDSD_IBM
| (u
<< 4));
stat
= wdcommand(du
, WDCC_READP
);
* If WDCC_READP fails then we might have an old ST506 type drive
* so we try a seek to 0; if that passes then the
* drive is there but it's OLD AND KRUSTY
stat
= wdcommand(du
, WDCC_RESTORE
| WD_STEP
);
stat
= inb(wdc
+wd_error
);
strncpy(du
->dk_dd
.d_typename
, "ST506",
sizeof du
->dk_dd
.d_typename
);
strncpy(du
->dk_params
.wdp_model
, "Unknown Type",
sizeof du
->dk_params
.wdp_model
);
du
->dk_dd
.d_type
= DTYPE_ST506
;
insw(wdc
+wd_data
, tb
, sizeof(tb
)/sizeof(short));
bcopy(tb
, wp
, sizeof(struct wdparams
));
/* shuffle string byte order */
for (i
=0; i
< sizeof(wp
->wdp_model
) ;i
+=2) {
p
= (u_short
*) (wp
->wdp_model
+ i
);
/*printf("gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", wp->wdp_config,
wp->wdp_fixedcyl+wp->wdp_removcyl, wp->wdp_heads, wp->wdp_sectors,
wp->wdp_cntype, wp->wdp_cnsbsz, wp->wdp_model);*/
/* update disklabel given drive information */
du
->dk_dd
.d_ncylinders
= wp
->wdp_fixedcyl
+ wp
->wdp_removcyl
/*+- 1*/;
du
->dk_dd
.d_ntracks
= wp
->wdp_heads
;
du
->dk_dd
.d_nsectors
= wp
->wdp_sectors
;
du
->dk_dd
.d_secpercyl
= du
->dk_dd
.d_ntracks
* du
->dk_dd
.d_nsectors
;
du
->dk_dd
.d_partitions
[1].p_size
= du
->dk_dd
.d_secpercyl
*
du
->dk_dd
.d_partitions
[1].p_offset
= 0;
bcopy("ESDI/IDE", du
->dk_dd
.d_typename
, 9);
bcopy(wp
->wdp_model
+20, du
->dk_dd
.d_packname
, 14-1);
du
->dk_dd
.d_type
= DTYPE_ESDI
;
du
->dk_dd
.d_subtype
|= DSTYPE_GEOMETRY
;
/* XXX sometimes possibly needed */
(void) inb(wdc
+wd_status
);
wdclose(dev_t dev
, int flags
, int fmt
)
register struct disk
*du
;
int part
= wdpart(dev
), mask
= 1 << part
;
du
= wddrives
[wdunit(dev
)];
/* insure only one open at a time */
du
->dk_openpart
&= ~mask
;
du
->dk_copenpart
&= ~mask
;
du
->dk_bopenpart
&= ~mask
;
wdioctl(dev_t dev
, int cmd
, caddr_t addr
, int flag
)
register struct disk
*du
;
if ((flag
& FWRITE
) == 0)
du
->dk_bad
= *(struct dkbad
*)addr
;
*(struct disklabel
*)addr
= du
->dk_dd
;
((struct partinfo
*)addr
)->disklab
= &du
->dk_dd
;
((struct partinfo
*)addr
)->part
=
&du
->dk_dd
.d_partitions
[wdpart(dev
)];
if ((flag
& FWRITE
) == 0)
error
= setdisklabel(&du
->dk_dd
,
(struct disklabel
*)addr
,
/*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart : */0,
du
->dk_flags
|= DKFL_BSDLABEL
;
du
->dk_flags
&= ~DKFL_WRITEPROT
;
if ((flag
& FWRITE
) == 0)
du
->dk_wlabel
= *(int *)addr
;
du
->dk_flags
&= ~DKFL_WRITEPROT
;
if ((flag
& FWRITE
) == 0)
else if ((error
= setdisklabel(&du
->dk_dd
, (struct disklabel
*)addr
,
/*(du->dk_flags & DKFL_BSDLABEL) ? du->dk_openpart :*/ 0,
du
->dk_dospartitions
)) == 0) {
du
->dk_flags
|= DKFL_BSDLABEL
;
/* simulate opening partition 0 so write succeeds */
du
->dk_openpart
|= (1 << 0); /* XXX */
error
= writedisklabel(dev
, wdstrategy
,
&du
->dk_dd
, du
->dk_dospartitions
);
du
->dk_openpart
= du
->dk_copenpart
| du
->dk_bopenpart
;
*(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
.d_secsize
;
error
= physio(wdformat
, &rwdbuf
[unit
], 0, dev
, B_WRITE
,
fop
->df_count
-= auio
.uio_resid
;
fop
->df_reg
[0] = du
->dk_status
;
fop
->df_reg
[1] = du
->dk_error
;
int unit
= wdunit(dev
), part
= wdpart(dev
), val
= 0;
if (unit
>= _NWD
) /* 31 Jul 92*/
if (du
== 0 || du
->dk_state
== 0)
val
= wdopen (makewddev(major(dev
), unit
, WDRAW
), FREAD
, S_IFBLK
, 0);
if (du
== 0 || val
!= 0 || du
->dk_flags
& DKFL_WRITEPROT
)
return((int)du
->dk_dd
.d_partitions
[part
].p_size
);
extern char *vmmap
; /* poor name! */
wddump(dev_t dev
) /* dump core after a system crash */
register struct disk
*du
; /* disk unit to do the IO */
register struct bt_bad
*bt_ptr
;
long num
; /* number of sectors to write */
long blkoff
, blknum
, blkcnt
;
long cylin
, head
, sector
, stat
;
long secpertrk
, secpercyl
, nblocks
, i
;
static wddoingadump
= 0 ;
addr
= (char *) 0; /* starting address */
/* toss any characters present prior to dump */
/* size of memory to dump */
unit
= wdunit(dev
); /* eventually support floppies? */
part
= wdpart(dev
); /* file system */
/* check for acceptable drive number */
if (unit
>= _NWD
) return(ENXIO
); /* 31 Jul 92*/
if (du
== 0) return(ENXIO
);
/* was it ever initialized ? */
if (du
->dk_state
< OPEN
) return (ENXIO
) ;
if (du
->dk_flags
& DKFL_WRITEPROT
) return(ENXIO
);
/* Convert to disk sectors */
num
= (u_long
) num
* NBPG
/ du
->dk_dd
.d_secsize
;
/* check if controller active */
/*if (wdtab.b_active) return(EFAULT); */
if (wddoingadump
) return(EFAULT
);
secpertrk
= du
->dk_dd
.d_nsectors
;
secpercyl
= du
->dk_dd
.d_secpercyl
;
nblocks
= du
->dk_dd
.d_partitions
[part
].p_size
;
blkoff
= du
->dk_dd
.d_partitions
[part
].p_offset
;
/*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/
/* 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 ;
/* must delay 5us to conform to ATA spec */
while ((inb(wdc
+wd_status
) & WDCS_BUSY
) && (i
-- > 0)) ;
outb(wdc
+wd_sdh
, WDSD_IBM
| (unit
<< 4));
outb(wdc
+wd_command
, WDCC_RESTORE
| WD_STEP
);
/* must delay 5us to conform to ATA spec */
while (inb(wdc
+wd_status
) & WDCS_BUSY
) ;
/* some compaq controllers require this ... */
blknum
= dumplo
+ blkoff
;
if (blkcnt
> MAXTRANSFER
) blkcnt
= MAXTRANSFER
;
if ((blknum
+ blkcnt
- 1) / secpercyl
!= blknum
/ secpercyl
)
blkcnt
= secpercyl
- (blknum
% secpercyl
);
/* keep transfer within current cylinder */
pmap_enter(kernel_pmap
, CADDR1
, trunc_page(addr
), VM_PROT_READ
, TRUE
);
/* 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
= du
->dk_bad
.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
.d_secperunit
)
- (bt_ptr
- du
->dk_bad
.bt_bad
) - 1;
cylin
= blknum
/ secpercyl
;
head
= (blknum
% secpercyl
) / secpertrk
;
sector
= blknum
% secpertrk
;
outb(wdc
+wd_sdh
, WDSD_IBM
| (unit
<<4) | (head
& 0xf));
while ((inb(wdc
+wd_status
) & WDCS_READY
) == 0) ;
/* transfer some blocks */
outb(wdc
+wd_sector
, sector
);
outb(wdc
+wd_cyl_lo
, cylin
);
outb(wdc
+wd_cyl_hi
, cylin
>> 8);
/* lets just talk about this first...*/
pg ("sdh 0%o sector %d cyl %d addr 0x%x",
inb(wdc
+wd_sdh
), inb(wdc
+wd_sector
),
inb(wdc
+wd_cyl_hi
)*256+inb(wdc
+wd_cyl_lo
), addr
) ;
outb(wdc
+wd_command
, WDCC_WRITE
);
/* Ready to send data? */
while ((inb(wdc
+wd_status
) & WDCS_DRQ
) == 0) ;
if (inb(wdc
+wd_status
) & WDCS_ERR
) return(EIO
) ;
outsw (wdc
+wd_data
, CADDR1
+((int)addr
&(NBPG
-1)), 256);
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 */
/* must delay 5us to conform to ATA spec */
for ( i
= WDCTIMEOUT
; 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 ((unsigned)addr
% (1024*1024) == 0) printf("%d ", num
/2048) ;
/* operator aborting dump? */