* Copyright (c) 1982, 1988 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
* @(#)up.c 7.8 (Berkeley) %G%
* UNIBUS peripheral standalone driver with ECC correction and bad
* block forwarding. Also supports header operation and write check
* for data and/or header.
#include "../vaxuba/upreg.h"
#include "../vaxuba/ubareg.h"
#define MAXBADDESC 126 /* max number of bad sectors recorded */
#define SECTSIZ 512 /* sector size in bytes */
#define HDRSIZ 4 /* number of bytes in sector header */
#define MAXCTLR 1 /* all addresses must be specified */
static u_short upstd
[MAXCTLR
] = { 0776700 };
static struct disklabel uplabel
[MAXNUBA
][MAXCTLR
][MAXUNIT
];
static char lbuf
[SECTSIZ
];
struct dkbad upbad
[MAXNUBA
][MAXCTLR
][MAXUNIT
]; /* bad sector table */
int sectsiz
; /* real sector size */
# define UPF_BSEDEBUG 01 /* debugging bad sector forwarding */
# define UPF_ECCDEBUG 02 /* debugging ecc correction */
} up_softc
[MAXNUBA
][MAXCTLR
][MAXUNIT
];
UPOF_P400
, UPOF_M400
, UPOF_P400
, UPOF_M400
,
UPOF_P800
, UPOF_M800
, UPOF_P800
, UPOF_M800
,
UPOF_P1200
, UPOF_M1200
, UPOF_P1200
, UPOF_M1200
,
register struct updevice
*upaddr
;
register struct up_softc
*sc
;
register struct disklabel
*lp
;
int error
= 0, uba
, ctlr
;
if ((u_int
)(uba
= io
->i_adapt
) >= nuba
)
if ((u_int
)(ctlr
= io
->i_ctlr
) >= MAXCTLR
)
if ((u_int
)unit
>= MAXUNIT
)
upaddr
= (struct updevice
*)ubamem(uba
, upstd
[ctlr
]);
while ((upaddr
->upcs1
& UP_DVA
) == 0);
sc
= &up_softc
[uba
][ctlr
][unit
];
lp
= &uplabel
[uba
][ctlr
][unit
];
/* Read in the pack label. */
if (upstrategy(&tio
, READ
) != SECTSIZ
)
dlp
= (struct disklabel
*)(lbuf
+ LABELOFFSET
);
if (error
== 0 && (dlp
->d_magic
!= DISKMAGIC
||
dlp
->d_magic2
!= DISKMAGIC
))
if (upmaptype(unit
, upaddr
, lp
) == 0)
/* Read in the bad sector table. */
tio
.i_bn
= lp
->d_secpercyl
* lp
->d_ncylinders
- lp
->d_nsectors
;
tio
.i_ma
= (char *)&upbad
[uba
][ctlr
][unit
];
tio
.i_cc
= sizeof(struct dkbad
);
for (i
= 0; i
< 5; i
++) {
if (upstrategy(&tio
, READ
) == sizeof(struct dkbad
))
printf("up: can't read bad sector table\n");
for (i
= 0; i
< MAXBADDESC
; i
++) {
upbad
[uba
][ctlr
][unit
].bt_bad
[i
].bt_cyl
= -1;
upbad
[uba
][ctlr
][unit
].bt_bad
[i
].bt_trksec
= -1;
if (io
->i_part
>= lp
->d_npartitions
||
lp
->d_partitions
[io
->i_part
].p_size
== 0)
io
->i_boff
= lp
->d_partitions
[io
->i_part
].p_offset
;
io
->i_flgs
&= ~F_TYPEMASK
;
register unit
= io
->i_unit
;
int recal
, info
, waitdry
;
register struct updevice
*upaddr
;
register struct disklabel
*lp
;
int error
, rv
= io
->i_cc
;
upaddr
= (struct updevice
*)ubamem(io
->i_adapt
, upstd
[io
->i_ctlr
]);
sc
= &up_softc
[io
->i_adapt
][io
->i_ctlr
][unit
];
lp
= &uplabel
[io
->i_adapt
][io
->i_ctlr
][unit
];
if (io
->i_flgs
& (F_HDR
|F_HCHECK
))
if ((upaddr
->upds
& UPDS_VV
) == 0) {
upaddr
->upcs1
= UP_DCLR
|UP_GO
;
upaddr
->upcs1
= UP_PRESET
|UP_GO
;
upaddr
->upof
= UPOF_FMT22
;
if ((upaddr
->upds
& UPDS_DREADY
) == 0) {
printf("up%d not ready\n", unit
);
upaddr
->upwc
= -io
->i_cc
/ sizeof (short);
o
= io
->i_cc
+ (upaddr
->upwc
* sizeof (short));
bn
= io
->i_bn
+ o
/ sectsiz
;
if (doprintf
&& sc
->debug
& (UPF_ECCDEBUG
|UPF_BSEDEBUG
))
printf("wc=%d o=%d i_bn=%d bn=%d\n",
upaddr
->upwc
, o
, io
->i_bn
, bn
);
if (upstart(io
, bn
, lp
) != 0) {
* If transfer has completed, free UNIBUS
* resources and return transfer size.
if ((upaddr
->upds
&UPDS_ERR
) == 0 && (upaddr
->upcs1
&UP_TRE
) == 0)
(io
->i_cc
+ upaddr
->upwc
* sizeof (short)) / sectsiz
;
if (upaddr
->uper1
& (UPER1_DCK
|UPER1_ECH
))
cn
= bn
/ lp
->d_secpercyl
;
sn
= bn
% lp
->d_secpercyl
;
tn
= sn
/ lp
->d_nsectors
;
sn
= sn
% lp
->d_nsectors
;
if (sc
->debug
& (UPF_ECCDEBUG
|UPF_BSEDEBUG
)) {
printf("up error: sn%d (cyl,trk,sec)=(%d,%d,%d) ",
printf("cs2=%b er1=%b er2=%b wc=%d\n",
upaddr
->upcs2
, UPCS2_BITS
, upaddr
->uper1
,
UPER1_BITS
, upaddr
->uper2
, UPER2_BITS
, upaddr
->upwc
);
while ((upaddr
->upds
&UPDS_DRY
) == 0 && ++waitdry
< sectsiz
)
if (upaddr
->uper1
&UPER1_WLE
) {
* Give up on write locked devices immediately.
printf("up%d: write locked\n", unit
);
if (upaddr
->uper2
& UPER2_BSE
) {
if ((io
->i_flgs
&F_NBSF
) == 0 && upecc(io
, BSE
) == 0)
* ECC error. If a soft error, correct it;
* if correction is too large, no more retries.
if ((upaddr
->uper1
& (UPER1_DCK
|UPER1_ECH
|UPER1_HCRC
)) == UPER1_DCK
) {
* If the error is a header CRC, check if a replacement sector
* exists in the bad sector table.
if ((upaddr
->uper1
&UPER1_HCRC
) && (io
->i_flgs
&F_NBSF
) == 0 &&
if (++io
->i_errcnt
> sc
->retries
) {
* After 28 retries (16 without offset, and
* 12 with offset positioning) give up.
if (upaddr
->upcs2
& UPCS2_WCE
)
printf("up error: sn%d (cyl,trk,sec)=(%d,%d,%d) ",
printf("cs2=%b er1=%b er2=%b\n",
upaddr
->upcs2
, UPCS2_BITS
, upaddr
->uper1
,
UPER1_BITS
, upaddr
->uper2
, UPER2_BITS
);
upaddr
->upcs1
= UP_TRE
|UP_DCLR
|UP_GO
;
if (io
->i_errcnt
>= 16) {
upaddr
->upof
= UPOF_FMT22
;
upaddr
->upcs1
= UP_RTC
|UP_GO
;
* Clear drive error and, every eight attempts, (starting with the
* fourth) recalibrate to clear the slate.
upaddr
->upcs1
= UP_TRE
|UP_DCLR
|UP_GO
;
if ((io
->i_errcnt
&07) == 4 ) {
upaddr
->upcs1
= UP_RECAL
|UP_GO
;
upaddr
->upcs1
= UP_SEEK
|UP_GO
;
if (io
->i_errcnt
>= 16 && (func
& READ
)) {
upaddr
->upof
= up_offset
[io
->i_errcnt
& 017] | UPOF_FMT22
;
upaddr
->upcs1
= UP_OFFSET
|UP_GO
;
#define rounddown(x, y) (((x) / (y)) * (y))
upaddr
->upwc
= rounddown(upaddr
->upwc
, sectsiz
/ sizeof (short));
* If we were offset positioning,
if (io
->i_errcnt
>= 16) {
upaddr
->upof
= UPOF_FMT22
;
upaddr
->upcs1
= UP_RTC
|UP_GO
;
register struct updevice
*upaddr
;
} while ((upaddr
->upcs1
& UP_RDY
) == 0);
register struct updevice
*upaddr
;
while ((upaddr
->upds
&UPDS_DRY
) == 0)
* Correct an ECC error, and restart the i/o to complete the transfer (if
* necessary). This is quite complicated because the transfer may be going
* to an odd memory address base and/or across a page boundary.
register struct up_softc
*sc
;
register struct updevice
*up
;
register struct disklabel
*lp
;
int bn
, twc
, npf
, mask
, cn
, tn
, sn
;
* Npf is the number of sectors transferred
* before the sector containing the ECC error;
* bn is the current block number.
sc
= &up_softc
[io
->i_adapt
][io
->i_ctlr
][unit
];
lp
= &uplabel
[io
->i_adapt
][io
->i_ctlr
][unit
];
up
= (struct updevice
*)ubamem(io
->i_adapt
, upstd
[io
->i_ctlr
]);
npf
= ((twc
* sizeof(short)) + io
->i_cc
) / sectsiz
;
if (sc
->debug
& UPF_ECCDEBUG
)
printf("npf=%d mask=0x%x ec1=%d wc=%d\n",
npf
, up
->upec2
, up
->upec1
, twc
);
cn
= bn
/ lp
->d_secpercyl
;
sn
= bn
% lp
->d_secpercyl
;
tn
= sn
/ lp
->d_nsectors
;
sn
= sn
% lp
->d_nsectors
;
printf("up%d: soft ecc sn%d\n", unit
, bn
);
for (i
= mask
, bit
= 0; i
; i
>>= 1)
printf("%d-bit error\n", bit
);
* Compute the byte and bit position of
* the error. o is the byte offset in
* the transfer at which the correction
i
= up
->upec1
- 1; /* -1 makes 0 origin */
up
->upcs1
= UP_TRE
|UP_DCLR
|UP_GO
;
* Correct while possible bits remain of mask.
* Since mask contains 11 bits, we continue while
* the bit offset is > -11. Also watch out for
* end of this block and the end of the transfer.
while (o
< sectsiz
&& (npf
*sectsiz
)+o
< io
->i_cc
&& bit
> -11) {
* (base address of transfer) +
* (# sectors transferred before the error) *
* (byte offset to incorrect data)
addr
= io
->i_ma
+ (npf
* sectsiz
) + o
;
* No data transfer occurs with a write check,
* so don't correct the resident copy of data.
if ((io
->i_flgs
& (F_CHECK
|F_HCHECK
)) == 0) {
if (sc
->debug
& UPF_ECCDEBUG
)
printf("addr=0x%x old=0x%x ", addr
,
if (sc
->debug
& UPF_ECCDEBUG
)
printf("new=0x%x\n", (*addr
&0xff));
* If not in bad sector table,
* indicate a hard error to caller.
up
->upcs1
= UP_TRE
|UP_DCLR
|UP_GO
;
if ((bbn
= isbad(&upbad
[io
->i_adapt
][io
->i_ctlr
][unit
], cn
, tn
, sn
)) < 0)
bbn
= (lp
->d_ncylinders
* lp
->d_secpercyl
) -
lp
->d_nsectors
- 1 - bbn
;
twc
= up
->upwc
+ sectsiz
;
up
->upwc
= - (sectsiz
/ sizeof (short));
if (sc
->debug
& UPF_BSEDEBUG
)
printf("revector sn %d to %d\n", sn
, bbn
);
* Clear the drive & read the replacement
* sector. If this is in the middle of a
* transfer, then set up the controller
* registers in a normal fashion.
* The UNIBUS address need not be changed.
if (upstart(io
, bbn
, lp
))
io
->i_errcnt
= 0; /* success */
if ((up
->upds
& UPDS_ERR
) || (up
->upcs1
& UP_TRE
)) {
up
->upwc
= twc
- sectsiz
;
register struct disklabel
*lp
;
register struct updevice
*upaddr
;
register struct up_softc
*sc
;
upaddr
= (struct updevice
*)ubamem(io
->i_adapt
, upstd
[io
->i_ctlr
]);
sc
= &up_softc
[io
->i_adapt
][io
->i_ctlr
][io
->i_unit
];
sn
= bn
% lp
->d_secpercyl
;
tn
= sn
/ lp
->d_nsectors
;
sn
= sn
% lp
->d_nsectors
;
upaddr
->updc
= bn
/ lp
->d_secpercyl
;
upaddr
->upda
= (tn
<< 8) + sn
;
switch (io
->i_flgs
& F_TYPEMASK
) {
upaddr
->upcs1
= UP_RCOM
|UP_GO
;
upaddr
->upcs1
= UP_WCOM
|UP_GO
;
upaddr
->upcs1
= UP_RHDR
|UP_GO
;
upaddr
->upcs1
= UP_WHDR
|UP_GO
;
upaddr
->upcs1
= UP_WCDATA
|UP_GO
;
upaddr
->upcs1
= UP_WCHDR
|UP_GO
;
io
->i_flgs
&= ~F_TYPEMASK
;
register struct up_softc
*sc
;
sc
= &up_softc
[io
->i_adapt
][io
->i_ctlr
][io
->i_unit
];
*(struct disklabel
*)arg
=
uplabel
[io
->i_adapt
][io
->i_ctlr
][io
->i_unit
];
upbad
[io
->i_adapt
][io
->i_ctlr
][io
->i_unit
];