* with ECC handling and bad block forwarding.
* Also supports header io operations and
* commands to write check header and data.
#include "../vaxmba/hpreg.h"
#include "../vaxmba/mbareg.h"
#define MASKREG(reg) ((reg)&0xffff)
#define SECTSIZ 512 /* sector size in bytes */
#define HDRSIZ 4 /* number of bytes in sector header */
#define MAXECC 5 /* max # bits allow in ecc error w/ F_ECCLM */
char hp_type
[MAXNMBA
*8] = { 0 };
MBDT_RM02
, /* actually something else */
#define RP06 (hptypes[hp_type[unit]] <= MBDT_RP06)
#define ML11 (hptypes[hp_type[unit]] == MBDT_ML11A)
#define RM80 (hptypes[hp_type[unit]] == MBDT_RM80)
HPOF_P400
, HPOF_M400
, HPOF_P400
, HPOF_M400
,
HPOF_P800
, HPOF_M800
, HPOF_P800
, HPOF_M800
,
HPOF_P1200
, HPOF_M1200
, HPOF_P1200
, HPOF_M1200
,
struct dkbad hpbad
[MAXNMBA
*8];
int ssect
[MAXNMBA
*8]; /* 1 when on track w/skip sector */
#define HPF_BSEDEBUG 01 /* debugging bad sector forwarding */
#define HPF_ECCDEBUG 02 /* debugging ecc correction */
* When awaiting command completion, don't
* hang on to the status register since
* this ties up some controllers.
while ((((addr)->hpds)&HPDS_DRY)==0) DELAY(500);
register unit
= io
->i_unit
;
struct hpdevice
*hpaddr
= (struct hpdevice
*)mbadrv(unit
);
mbainit(UNITTOMBA(unit
));
if (hp_type
[unit
] == 0) {
register i
, type
= hpaddr
->hpdt
& MBDT_TYPE
;
for (i
= 0; hptypes
[i
]; i
++)
_stop("unknown drive type");
hpaddr
->hpcs1
= HP_DCLR
|HP_GO
; /* init drive */
hpaddr
->hpcs1
= HP_PRESET
|HP_GO
;
hpaddr
->hpof
= HPOF_FMT22
;
hp_type
[unit
] = hpmaptype(hpaddr
, i
, unit
);
* Read in the bad sector table.
st
= &hpst
[hp_type
[unit
]];
tio
.i_bn
= st
->nspc
* st
->ncyl
- st
->nsect
;
tio
.i_ma
= (char *)&hpbad
[unit
];
tio
.i_cc
= sizeof (struct dkbad
);
for (i
= 0; i
< 5; i
++) {
if (hpstrategy(&tio
, READ
) == sizeof (struct dkbad
))
printf("Unable to read bad sector table\n");
for (i
= 0; i
< MAXBADDESC
; i
++) {
hpbad
[unit
].bt_bad
[i
].bt_cyl
= -1;
hpbad
[unit
].bt_bad
[i
].bt_trksec
= -1;
if (io
->i_boff
< 0 || io
->i_boff
> 7 ||
st
->off
[io
->i_boff
]== -1)
io
->i_boff
= st
->off
[io
->i_boff
] * st
->nspc
;
register unit
= io
->i_unit
;
struct mba_regs
*mba
= mbamba(unit
);
struct hpdevice
*hpaddr
= (struct hpdevice
*)mbadrv(unit
);
struct st
*st
= &hpst
[hp_type
[unit
]];
int cn
, tn
, sn
, bytecnt
, bytesleft
;
if ((io
->i_flgs
& (F_HDR
|F_HCHECK
)) != 0)
if ((hpaddr
->hpds
& HPDS_VV
) == 0) {
hpaddr
->hpcs1
= HP_DCLR
|HP_GO
;
hpaddr
->hpcs1
= HP_PRESET
|HP_GO
;
hpaddr
->hpof
= HPOF_FMT22
;
sn
= sn
%st
->nsect
+ ssect
[unit
];
hpaddr
->hpda
= (tn
<< 8) + sn
;
if (mbastart(io
, func
) != 0) /* start transfer */
* Successful data transfer, return.
if ((hpaddr
->hpds
&HPDS_ERR
) == 0 && (mba
->mba_sr
&MBSR_EBITS
) == 0)
* Error handling. Calculate location of error.
bytesleft
= MASKREG(mba
->mba_bcr
);
bytesleft
|= 0xffff0000; /* sxt */
bn
= io
->i_bn
+ (io
->i_cc
+ bytesleft
) / sectsiz
;
er1
= MASKREG(hpaddr
->hper1
);
er2
= MASKREG(hpaddr
->hper2
);
if (er1
& (HPER1_DCK
|HPER1_ECH
))
bn
--; /* Error is in Prev block */
if (hpdebug
[unit
] & (HPF_ECCDEBUG
|HPF_BSEDEBUG
)) {
printf("hp error: (cyl,trk,sec)=(%d,%d,%d) ds=%b\n",
cn
, tn
, sn
, MASKREG(hpaddr
->hpds
), HPDS_BITS
);
printf("er1=%b er2=%b\n", er1
, HPER1_BITS
, er2
, HPER2_BITS
);
printf("bytes left: %d, of 0x%x, da 0x%x\n",-bytesleft
,
hpaddr
->hpof
, hpaddr
->hpda
);
er1
&= ~(HPER1_HCE
|HPER1_FER
);
* Give up early if drive write locked.
printf("hp%d: write locked\n", unit
);
* Interpret format error bit as a bad block on RP06's.
if (MASKREG(er1
) == HPER1_FER
&& RP06
)
* If a hard error, or maximum retry count
* exceeded, clear controller state and
* pass back error to caller.
if (++io
->i_errcnt
> 27 || (er1
& HPER1_HARD
) ||
(!ML11
&& (er2
& HPER2_HARD
))) {
* The last ditch effort to bad sector forward
* below will probably fail since mba byte ctr
* (bcr) is different for BSE and ECC errors and
* the wrong block will be revectored to if one
* has 2 contiguous bad blocks and reads the second.
* For now, we can probably just let a header CRC
* error be handled like a BSE since no data will
* have been transferred and the bcr should the same
* as it would with a BSE error.
if ((io
->i_flgs
&F_NBSF
) == 0 && hpecc(io
, BSE
) == 0)
if (mba
->mba_sr
& (MBSR_WCKUP
|MBSR_WCKLWR
))
io
->i_errblk
= bn
+ ssect
[unit
];
printf("hp error: (cyl,trk,sec)=(%d,%d,%d) ds=%b \n",
cn
, tn
, sn
, MASKREG(hpaddr
->hpds
), HPDS_BITS
);
printf("er1=%b er2=%b", er1
, HPER1_BITS
, er2
, HPER2_BITS
);
printf(" mr1=%o", MASKREG(hpaddr
->hpmr
));
printf(" mr2=%o", MASKREG(hpaddr
->hpmr2
));
if (hpdebug
[unit
] & (HPF_BSEDEBUG
|HPF_ECCDEBUG
))
printf(" dc=%d, da=0x%x",MASKREG(hpaddr
->hpdc
),
hpaddr
->hpcs1
= HP_DCLR
|HP_GO
;
* Attempt to forward bad sectors on
* anything but an ML11. If drive
* supports skip sector handling, try to
* use it first; otherwise try the
if ((er2
& HPER2_BSE
) && !ML11
) {
if (!ssect
[unit
] && (er2
&HPER2_SSE
))
if (io
->i_flgs
& F_NBSF
) {
if (RM80
&& (er2
& HPER2_SSE
)) {
if ((er1
& (HPER1_DCK
|HPER1_ECH
)) == HPER1_DCK
) {
io
->i_errblk
= bn
+ ssect
[unit
];
if (io
->i_flgs
& F_SEVRE
)
if (ML11
&& (io
->i_errcnt
>= 16))
hpaddr
->hpcs1
= HP_DCLR
|HP_GO
;
* Every fourth retry recalibrate.
if (((io
->i_errcnt
& 07) == 4) ) {
hpaddr
->hpcs1
= HP_RECAL
|HP_GO
;
hpaddr
->hpcs1
= HP_SEEK
|HP_GO
;
if (io
->i_errcnt
>= 16 && (io
->i_flgs
& F_READ
)) {
hpaddr
->hpof
= hp_offset
[io
->i_errcnt
& 017]|HPOF_FMT22
;
hpaddr
->hpcs1
= HP_OFFSET
|HP_GO
;
if (hpdebug
[unit
] & (HPF_ECCDEBUG
|HPF_BSEDEBUG
))
printf("restart: bn=%d, cc=%d, ma=0x%x hprecal=%d\n",
io
->i_bn
, io
->i_cc
, io
->i_ma
, hprecal
);
goto restart
; /* retry whole transfer --ghg */
* On successful error recovery, bump
* block number to advance to next portion
if ((bn
-startblock
) * sectsiz
< bytecnt
) {
io
->i_ma
= membase
+ (io
->i_bn
- startblock
)*sectsiz
;
io
->i_cc
= bytecnt
- (io
->i_bn
- startblock
)*sectsiz
;
if (hpdebug
[unit
] & (HPF_ECCDEBUG
|HPF_BSEDEBUG
))
printf("restart: bn=%d, cc=%d, ma=0x%x hprecal=%d\n",
io
->i_bn
, io
->i_cc
, io
->i_ma
, hprecal
);
if (io
->i_errcnt
>= 16) {
hpaddr
->hpcs1
= HP_RTC
|HP_GO
;
while (hpaddr
->hpds
& HPDS_PIP
)
register unit
= io
->i_unit
;
register struct mba_regs
*mbp
= mbamba(unit
);
register struct hpdevice
*rp
= (struct hpdevice
*)mbadrv(unit
);
register struct st
*st
= &hpst
[hp_type
[unit
]];
int npf
, bn
, cn
, tn
, sn
, bcr
;
bcr
= MASKREG(mbp
->mba_bcr
);
bcr
|= 0xffff0000; /* sxt */
npf
= (bcr
+ io
->i_cc
) / sectsiz
; /* # sectors read */
npf
--; /* Error is in prev block --ghg */
bn
= io
->i_bn
+ npf
+ ssect
[unit
]; /* physical block #*/
if (hpdebug
[unit
]&HPF_ECCDEBUG
)
printf("bcr=%d npf=%d ssect=%d sectsiz=%d i_cc=%d\n",
bcr
, npf
, ssect
[unit
], sectsiz
, io
->i_cc
);
int bit
, o
, mask
, ecccnt
= 0;
printf("hp%d: soft ecc sn%d\n", unit
, bn
);
mask
= MASKREG(rp
->hpec2
);
i
= MASKREG(rp
->hpec1
) - 1; /* -1 makes 0 origin */
rp
->hpcs1
= HP_DCLR
| HP_GO
;
while (o
<sectsiz
&& npf
*sectsiz
+ o
< io
->i_cc
&& bit
> -11) {
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 (hpdebug
[unit
] & HPF_ECCDEBUG
)
printf("addr=%x old=%x ", addr
,
if (hpdebug
[unit
] & HPF_ECCDEBUG
)
printf("new=%x\n",(*addr
& 0xff));
if ((io
->i_flgs
& F_ECCLM
) && ecccnt
++ >= MAXECC
)
if (io
->i_flgs
& F_SEVRE
)
* Set skip-sector-inhibit and
rp
->hpcs1
= HP_DCLR
| HP_GO
;
rp
->hpcs1
= HP_DCLR
| HP_GO
;
if (hpdebug
[unit
] & HPF_BSEDEBUG
)
printf("hpecc: BSE @ bn %d\n", bn
);
if ((bbn
= isbad(&hpbad
[unit
], cn
, tn
, sn
)) < 0)
bbn
= st
->ncyl
*st
->nspc
- st
->nsect
- 1 - bbn
;
if (hpdebug
[unit
] & HPF_BSEDEBUG
)
printf("revector to cn %d tn %d sn %d\n", cn
, tn
, sn
);
return (rp
->hpds
&HPDS_ERR
);
printf("hpecc: flag=%d\n", flag
);
register unit
= io
->i_unit
;
struct st
*st
= &hpst
[hp_type
[unit
]], *tmp
;
struct mba_drv
*drv
= mbadrv(unit
);
if ((drv
->mbd_dt
&MBDT_TAP
) == 0) {
case SAIOSSI
: /* skip-sector-inhibit */
if (drv
->mbd_dt
&MBDT_TAP
)
if ((io
->i_flgs
&F_SSI
) == 0) {
/* make sure this is done once only */
case SAIONOSSI
: /* remove skip-sector-inhibit */
if (io
->i_flgs
& F_SSI
) {
drv
->mbd_of
&= ~HPOF_SSEI
;
case SAIOSSDEV
: /* drive have skip sector? */
return (RM80
? 0 : ECMD
);