* 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: @(#)wx.c 7.2 (Berkeley) 5/9/91
* $Id: wx.c,v 1.11 1993/10/16 13:46:31 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/wxreg.h"
#define _NWD (NWX - 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 RECOVERYTIME 500000 /* usec for controller to recover after err */
#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_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 */
#define DKFL_LABELLING 0x00080 /* readdisklabel() in progress */
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 */
static struct disk
*wddrives
[_NWD
]; /* table of units */
static struct buf wdutab
[_NWD
]; /* head of queue per drive */
static struct buf rwdbuf
[_NWD
]; /* buffers for raw IO */
static long wdxfer
[_NWD
]; /* count of transfers */
static int wdprobe(struct isa_device
*dvp
);
static int wdattach(struct isa_device
*dvp
);
static void wdustart(struct disk
*du
);
static void wdstart(void);
static int wdcontrol(struct buf
*bp
);
static int wdcommand(struct disk
*du
, u_int cylinder
, u_int head
,
u_int sector
, u_int count
, u_int command
);
static int wdsetctlr(struct disk
*du
);
static int wdwsetctlr(struct disk
*du
);
static int wdgetctlr(struct disk
*du
);
static void wderror(struct buf
*bp
, struct disk
*du
, char *mesg
);
static int wdreset(struct disk
*du
);
static void wdsleep(char *wmesg
);
static int wdunwedge(struct disk
*du
);
static int wdwait(struct disk
*du
, u_char bits_wanted
);
struct isa_driver wxdriver
= {
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
;
* Skip probing and resetting the controller if it has already
* been successfully done.
* XXX - should finish probing for drives (using wdgetctlr) or
* only probe for controllers.
for (u
= 0; u
< _NWD
; u
++)
if (u
!= unit
&& wddrives
[u
] != NULL
&& wddrives
[u
]->dk_port
== wdc
)
/* 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)
if (wdreset(du
) != 0 && (DELAY(RECOVERYTIME
), wdreset(du
)) != 0)
/* execute a controller only command */
if (wdcommand(du
, 0, 0, 0, 0, WDCC_DIAGNOSE
) != 0
* Attach each drive if possible.
wdattach(struct isa_device
*dvp
)
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(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
)
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 */
/* Pick up changes made by readdisklabel(). */
if (du
->dk_flags
& DKFL_LABELLING
&& du
->dk_state
> RECAL
) {
wdstart(); /* 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
, cylin
, head
, sector
;
long secpertrk
, secpercyl
, addr
;
/* 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
) {
if (du
->dk_state
!= WANTOPEN
)
printf("wd%d: wdstart: weird dk_state %d\n",
du
->dk_unit
, du
->dk_state
);
printf("wd%d: wdstart: wdcontrol returned nonzero, state = %d\n",
du
->dk_unit
, du
->dk_state
);
/* calculate transfer details */
blknum
= bp
->b_blkno
+ du
->dk_skip
;
printf("wd%d: wdstart: %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
))
for (bt_ptr
= du
->dk_bad
.bt_bad
; bt_ptr
->bt_cyl
!= 0xffff;
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; ",
* XXX the offset of the bad sector table ought
* to be stored in the in-core copy of the table.
#define BAD144_PART 2 /* XXX scattered magic numbers */
#define BSD_PART 0 /* XXX should be 2 but bad144.c uses 0 */
if (lp
->d_partitions
[BSD_PART
].p_offset
!= 0)
blknum
= lp
->d_partitions
[BAD144_PART
].p_offset
+ lp
->d_partitions
[BAD144_PART
].p_size
;
blknum
= lp
->d_secperunit
;
blknum
-= lp
->d_nsectors
+ (bt_ptr
- du
->dk_bad
.bt_bad
)
cylin
= blknum
/ secpercyl
;
head
= (blknum
% secpercyl
) / secpertrk
;
sector
= blknum
% secpertrk
;
printf("new = %d\n", blknum
);
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)
if (bp
->b_flags
& B_FORMAT
) {
sector
= lp
->d_gap3
- 1; /* + 1 later */
if (du
->dk_flags
& DKFL_SINGLE
)
count
= howmany(du
->dk_bc
, DEV_BSIZE
);
command
= (bp
->b_flags
& B_READ
) ? WDCC_READ
: WDCC_WRITE
;
* XXX this loop may never terminate. The code to handle
* counting down of retries and eventually failing the i/o is
* in wdintr() and we can't get there from here.
printf("dummy wdunwedge\n");
while (wdcommand(du
, cylin
, head
, sector
, count
, command
) != 0)
"wdstart: timeout waiting to send command");
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? */
if (wdwait(du
, WDCS_READY
| WDCS_SEEKCMPLT
| WDCS_DRQ
) != 0) {
wderror(bp
, du
, "wdstart: timeout waiting for DRQ");
* XXX what do we do now? If we've just issued the command,
* then we can treat this failure the same as a command
* failure. But if we are continuing a multi-sector write,
* the command was issued ages ago, so we can't simply
* XXX we waste a lot of time unnecessarily translating
* block numbers to cylin/head/sector for continued i/o's.
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.
register struct disk
*du
;
register struct buf
*bp
, *dp
;
printf("wd%d: extra interrupt\n", unit
);
du
= wddrives
[wdunit(bp
->b_dev
)];
wderror(bp
, du
, "wdintr: timeout waiting for status");
du
->dk_status
|= WDCS_ERR
; /* XXX */
/* is it not a transfer, but a control operation? */
if (du
->dk_state
< OPEN
) {
if (du
->dk_status
& (WDCS_ERR
| WDCS_ECCCOR
)) {
wderror(bp
, du
, "wdintr");
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 (du
->dk_status
& WDCS_ERR
) {
if (++wdtab
.b_errcnt
< RETRIES
) {
wderror(bp
, du
, "hard error");
bp
->b_error
= EIO
; /* 17 Sep 92*/
bp
->b_flags
|= B_ERROR
; /* flag the error */
wderror(bp
, du
, "soft ecc");
* 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? */
if ((du
->dk_status
& (WDCS_READY
| WDCS_SEEKCMPLT
| WDCS_DRQ
))
!= (WDCS_READY
| WDCS_SEEKCMPLT
| WDCS_DRQ
))
wderror(bp
, du
, "wdintr: read intr arrived early");
if (wdwait(du
, WDCS_READY
| WDCS_SEEKCMPLT
| WDCS_DRQ
) != 0) {
wderror(bp
, du
, "wdintr: read error detected late");
(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. */
wderror(bp
, du
, "soft error");
/* 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
;
struct disklabel save_label
;
if (unit
>= _NWD
) return (ENXIO
) ;
if (du
== 0) return (ENXIO
) ;
while (du
->dk_flags
& DKFL_LABELLING
)
tsleep((caddr_t
)&du
->dk_flags
, PZERO
- 1, "wdopen", 1);
if ((du
->dk_flags
& DKFL_BSDLABEL
) == 0) {
* wdtab.b_active != 0 implies wdutab[unit].b_actf == NULL (?)
* so the following guards most things (until the next i/o).
* It doesn't guard against a new i/o starting and being
* affected by the label being changed. Sigh.
du
->dk_flags
|= DKFL_LABELLING
| DKFL_WRITEPROT
;
wdutab
[unit
].b_actf
= NULL
;
* Read label using WDRAW partition.
* If the drive has an MBR, then the current geometry (from
* wdgetctlr()) is used to read it; then the BIOS/DOS
* geometry is inferred and used to read the label off the
* 'c' partition. Otherwise the label is read using the
* current geometry. The label gives the final geometry.
* If bad sector handling is enabled, then this geometry
* is used to read the bad sector table. The geometry
* changes occur inside readdisklabel() and are propagated
* to the driver by resetting the state machine.
#define WDSTRATEGY ((int (*)(struct buf *)) wdstrategy) /* XXX */
msg
= readdisklabel(makewddev(major(dev
), unit
, WDRAW
),
du
->dk_dospartitions
, &du
->dk_bad
,
du
->dk_flags
&= ~DKFL_LABELLING
;
log(LOG_WARNING
, "wd%d: cannot find label (%s)\n",
return (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
;
/* Pick up changes made by readdisklabel(). */
* 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
== 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
;
du
= wddrives
[wdunit(bp
->b_dev
)];
if (wdcommand(du
, 0, 0, 0, 0, WDCC_RESTORE
| WD_STEP
) != 0) {
wderror(bp
, du
, "wdcontrol: wdcommand failed");
if (du
->dk_status
& WDCS_ERR
|| wdsetctlr(du
) != 0) {
wderror(bp
, du
, "wdcontrol: recal failed");
if (du
->dk_status
& WDCS_ERR
)
if (++wdtab
.b_errcnt
< RETRIES
) {
bp
->b_error
= ENXIO
; /* XXX needs translation */
* The rest of the initialization can be done
* Wait uninterruptibly until controller is not busy, then send it a command.
* The wait usually terminates immediately because we waited for the previous
wdcommand(struct disk
*du
, u_int cylinder
, u_int head
, u_int sector
,
u_int count
, u_int command
)
outb(wdc
+ wd_precomp
, du
->dk_dd
.d_precompcyl
/ 4);
outb(wdc
+ wd_cyl_lo
, cylinder
);
outb(wdc
+ wd_cyl_hi
, cylinder
>> 8);
outb(wdc
+ wd_sdh
, WDSD_IBM
| (du
->dk_unit
<< 4) | head
);
outb(wdc
+ wd_sector
, sector
+ 1);
outb(wdc
+ wd_seccnt
, count
);
outb(du
->dk_port
+ wd_command
, command
);
* issue IDC to drive to tell it just what geometry it is to be.
wdsetctlr(struct disk
*du
)
printf("wd%d: wdsetctlr C %lu H %lu S %lu\n", du
->dk_unit
,
du
->dk_dd
.d_ncylinders
, du
->dk_dd
.d_ntracks
,
if (wdcommand(du
, du
->dk_dd
.d_ncylinders
, du
->dk_dd
.d_ntracks
- 1, 0,
du
->dk_dd
.d_nsectors
, WDCC_IDC
) != 0
|| wdwait(du
, WDCS_READY
| WDCS_SEEKCMPLT
) != 0) {
wderror((struct buf
*)NULL
, du
, "wdsetctlr failed");
* Wait until driver is inactive, then set up controller.
wdwsetctlr(struct disk
*du
)
* issue READP to drive to ask it what it is.
wdgetctlr(struct disk
*du
) {
if (wdcommand(du
, 0, 0, 0, 0, WDCC_READP
) != 0
|| wdwait(du
, WDCS_READY
| WDCS_SEEKCMPLT
| WDCS_DRQ
) != 0) {
/* Old drives don't support WDCC_READP. Try a seek to 0. */
if (wdcommand(du
, 0, 0, 0, 0, WDCC_RESTORE
| WD_STEP
) != 0
|| wdwait(du
, WDCS_READY
| WDCS_SEEKCMPLT
) != 0)
/* Fake minimal drive geometry for reading the MBR or label. */
du
->dk_dd
.d_secsize
= DEV_BSIZE
;
du
->dk_dd
.d_nsectors
= 17;
du
->dk_dd
.d_ncylinders
= 1;
du
->dk_dd
.d_secpercyl
= 17;
* Fake some more of the label for printing by disklabel(1)
* in case there is no real label.
du
->dk_dd
.d_type
= DTYPE_ST506
;
du
->dk_dd
.d_subtype
|= DSTYPE_GEOMETRY
;
strncpy(du
->dk_dd
.d_typename
, "Fake geometry",
sizeof du
->dk_dd
.d_typename
);
/* Fake the model name for printing by wdattach(). */
strncpy(du
->dk_params
.wdp_model
, "Unknown Type",
sizeof du
->dk_params
.wdp_model
);
insw(du
->dk_port
+ 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
);
"\nwdgetctlr wd%d: gc %x cyl %d trk %d sec %d type %d sz %d model %s\n",
du
->dk_unit
, wp
->wdp_config
,
wp
->wdp_fixedcyl
+ wp
->wdp_removcyl
, wp
->wdp_heads
,
wp
->wdp_sectors
, wp
->wdp_cntype
, wp
->wdp_cnsbsz
,
/* update disklabel given drive information */
du
->dk_dd
.d_secsize
= DEV_BSIZE
;
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
;
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
;
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
;
wdwsetctlr(du
); /* XXX - check */
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
;
wdwsetctlr(du
); /* XXX - check */
/* 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
], 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
;
if (unit
>= _NWD
) /* 31 Jul 92*/
if (du
== 0 || du
->dk_state
== CLOSED
)
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 cylin
, head
, sector
;
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 ;
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
);
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.
if (lp
->d_partitions
[BSD_PART
].p_offset
!= 0)
blknum
= lp
->d_partitions
[BAD144_PART
]
+ lp
->d_partitions
[BAD144_PART
]
blknum
= lp
->d_secperunit
;
blknum
-= du
->dk_dd
.d_nsectors
+ (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 */
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("%ld ", num
/2048);
/* operator aborting dump? */
wderror(struct buf
*bp
, struct disk
*du
, char *mesg
)
printf("wd%d: %s:", du
->dk_unit
, mesg
);
diskerr(bp
, "wd", mesg
, LOG_PRINTF
, du
->dk_skip
, &du
->dk_dd
);
printf(" status %b error %b\n",
du
->dk_status
, WDCS_BITS
, du
->dk_error
, WDERR_BITS
);
outb(wdc
+ wd_ctlr
, WDCTL_IDS
| WDCTL_RST
);
outb(wdc
+ wd_ctlr
, WDCTL_IDS
);
if (wdwait(du
, WDCS_READY
| WDCS_SEEKCMPLT
) != 0
|| (du
->dk_error
= inb(wdc
+ wd_error
)) != 0x01)
outb(wdc
+ wd_ctlr
, WDCTL_4BIT
);
* Sleep until driver is inactive.
* This is used only for avoiding rare race conditions, so it is unimportant
* that the sleep may be far too short or too long.
tsleep((caddr_t
)&wdtab
.b_active
, PZERO
- 1, wmesg
, 1);
* Reset the controller after it has become wedged. This is different from
* wdreset() so that wdreset() can be used in the probe and so that this
* can restore the geometry .
wdunwedge(struct disk
*du
)
/* Schedule other drives for recalibration. */
for (unit
= 0; unit
< _NWD
; unit
++)
if ((du1
= wddrives
[unit
]) != NULL
&& du1
!= du
&& du1
->dk_state
> WANTOPEN
)
du1
->dk_state
= WANTOPEN
;
* XXX - recalibrate current drive now because some callers
* aren't prepared to have its state change.
if (wdcommand(du
, 0, 0, 0, 0, WDCC_RESTORE
| WD_STEP
) == 0
&& wdwait(du
, WDCS_READY
| WDCS_SEEKCMPLT
) == 0
wderror((struct buf
*)NULL
, du
, "wdunwedge failed");
* Wait uninterruptibly until controller is not busy and either certain
* status bits are set or an error has occurred.
* The wait is usually short unless it is for the controller to process
* an entire critical command.
* Return 1 for (possibly stale) controller errors, -1 for timeout errors,
* Return controller status in du->dk_status and, if there was a controller
* error, return the error code in du->dk_error.
wdwait(struct disk
*du
, u_char bits_wanted
)
#define TIMEOUT 1000 /* WDCC_DIAGNOSE can take > 300 msec */
retries
= POLLING
+ TIMEOUT
;
if (min_retries
> retries
|| min_retries
== 0)
du
->dk_status
= status
= inb(wdc
+ wd_status
);
if (!(status
& WDCS_BUSY
)) {
du
->dk_error
= inb(wdc
+ wd_error
);
if ((status
& bits_wanted
) == bits_wanted
)
* Switch to a polling rate of about 1 KHz so that
* the timeout is almost machine-independent. The
* controller is taking a long time to respond, so
* an extra msec won't matter.
} while (--retries
!= 0);