* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
* @(#)hp.c 7.11 (Berkeley) %G%
* RP??/RM?? disk driver with ECC handling and bad block forwarding.
* Also supports header io operations and commands to write check
#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 RP06(type) ((type) == MBDT_RP06 || (type) == MBDT_RP05 \
#define ML11(type) ((type) == MBDT_ML11A)
#define RM80(type) ((type) == 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 disklabel hplabel
[MAXNMBA
][MAXUNIT
];
struct dkbad hpbad
[MAXNMBA
][MAXUNIT
];
char ssect
; /* 1 when on track w/skip sector */
# define HPF_BSEDEBUG 01 /* debugging bad sector forwarding */
# define HPF_ECCDEBUG 02 /* debugging ecc correction */
} hp_softc
[MAXNMBA
][MAXUNIT
];
* When awaiting command completion, don't hang on to the status register
* since this ties up some controllers.
while ((((addr)->hpds)&HPDS_DRY) == 0) \
register unit
= io
->i_unit
;
register struct hp_softc
*sc
;
register struct disklabel
*lp
;
* Accept adaptor number as either controller or adaptor,
io
->i_adapt
= io
->i_ctlr
;
if ((u_int
)io
->i_adapt
>= MAXNMBA
|| !mbainit(io
->i_adapt
))
if ((u_int
)unit
>= MAXUNIT
)
hpaddr
= (struct hpdevice
*)mbadrv(io
->i_adapt
, unit
);
sc
= &hp_softc
[io
->i_adapt
][unit
];
lp
= &hplabel
[io
->i_adapt
][unit
];
hpaddr
->hpcs1
= HP_DCLR
|HP_GO
; /* init drive */
hpaddr
->hpcs1
= HP_PRESET
|HP_GO
;
if ((hpaddr
->hpds
& HPDS_DPR
) == 0)
sc
->type
= hpaddr
->hpdt
& MBDT_TYPE
;
if (sc
->type
== MBDT_ML11B
)
hpaddr
->hpof
= HPOF_FMT22
;
* Read in the pack label.
if (hpstrategy(&tio
, READ
) != SECTSIZ
)
dlp
= (struct disklabel
*)(lbuf
+ LABELOFFSET
);
if (error
== 0 && (dlp
->d_magic
!= DISKMAGIC
||
dlp
->d_magic2
!= DISKMAGIC
))
if (hpmaptype(hpaddr
, hpaddr
->hpdt
& MBDT_TYPE
, unit
, lp
) == 0)
* Read in the bad sector table.
tio
.i_bn
= lp
->d_secpercyl
* lp
->d_ncylinders
- lp
->d_nsectors
;
tio
.i_ma
= (char *)&hpbad
[io
->i_adapt
][unit
];
tio
.i_cc
= sizeof(struct dkbad
);
for (i
= 0; i
< 5; i
++) {
if (hpstrategy(&tio
, READ
) == sizeof(struct dkbad
))
printf("hp: can't read bad sector table\n");
for (i
= 0; i
< MAXBADDESC
; i
++) {
hpbad
[io
->i_adapt
][unit
].bt_bad
[i
].bt_cyl
= -1;
hpbad
[io
->i_adapt
][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
;
register int unit
= io
->i_unit
;
register struct hp_softc
*sc
;
register struct disklabel
*lp
;
int cn
, tn
, sn
, bytecnt
, bytesleft
, rv
;
mba
= mbamba(io
->i_adapt
);
hpaddr
= (struct hpdevice
*)mbadrv(io
->i_adapt
, unit
);
sc
= &hp_softc
[io
->i_adapt
][unit
];
lp
= &hplabel
[io
->i_adapt
][unit
];
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
;
cn
= bn
/ lp
->d_secpercyl
;
sn
= bn
% lp
->d_secpercyl
;
tn
= sn
/ lp
->d_nsectors
;
sn
= sn
% lp
->d_nsectors
+ sc
->ssect
;
hpaddr
->hpda
= (tn
<< 8) + sn
;
mbastart(io
, io
->i_unit
, func
); /* start transfer */
if (hpaddr
->hpds
& HPDS_ERR
) {
printf("hp error: sn [%d-%d) ds=%b er1=%b\n",
bn
, bn
+ io
->i_cc
/SECTSIZ
, MASKREG(hpaddr
->hpds
), HPDS_BITS
,
MASKREG(hpaddr
->hper1
), HPER1_BITS
);
if (mbastart(io
, io
->i_unit
, 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 (sc
->debug
& (HPF_ECCDEBUG
|HPF_BSEDEBUG
)) {
printf("hp error: sn%d (cyl,trk,sec)=(%d,%d,%d) ds=%b\n",
bn
, 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
);
if ((io
->i_flgs
&F_NBSF
) == 0 && hpecc(io
, BSE
) == 0)
* Give up early if drive write locked.
printf("hp%d: write locked\n", unit
);
if (RM80(sc
->type
) && (er2
& HPER2_SSE
)) {
* Attempt to forward bad sectors on anything but an ML11.
* Interpret format error bit as a bad block on RP06's.
if (((er2
& HPER2_BSE
) && !ML11(sc
->type
)) ||
(MASKREG(er1
) == HPER1_FER
&& RP06(sc
->type
))) {
if (io
->i_flgs
& F_NBSF
) {
if ((er1
& (HPER1_DCK
|HPER1_ECH
)) == HPER1_DCK
) {
* If a hard error, or maximum retry count
* exceeded, clear controller state and
* pass back error to caller.
if (++io
->i_errcnt
> sc
->retries
|| (er1
& HPER1_HARD
) ||
(!ML11(sc
->type
) && (er2
& HPER2_HARD
)) ||
(ML11(sc
->type
) && (io
->i_errcnt
>= 16))) {
if (mba
->mba_sr
& (MBSR_WCKUP
|MBSR_WCKLWR
))
io
->i_errblk
= bn
+ sc
->ssect
;
if (sc
->debug
& (HPF_BSEDEBUG
|HPF_ECCDEBUG
))
printf(" dc=%d, da=0x%x",MASKREG(hpaddr
->hpdc
),
printf("hp error: sn%d (cyl,trk,sec)=(%d,%d,%d) ds=%b \n",
bn
, cn
, tn
, sn
, MASKREG(hpaddr
->hpds
), HPDS_BITS
);
printf("er1=%b er2=%b", er1
, HPER1_BITS
, er2
, HPER2_BITS
);
hpaddr
->hpcs1
= HP_DCLR
|HP_GO
;
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 (sc
->debug
& (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
);
* 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 (sc
->debug
& (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
)
io
->i_bn
= startblock
; /*reset i_bn to original */
io
->i_cc
= bytecnt
; /*reset i_cc to total count xfered*/
io
->i_ma
= membase
; /*reset i_ma to original */
register int unit
= io
->i_unit
;
register struct mba_regs
*mbp
;
register struct hpdevice
*rp
;
register struct hp_softc
*sc
;
register struct disklabel
*lp
;
int npf
, bn
, cn
, tn
, sn
, bcr
;
mbp
= mbamba(io
->i_adapt
);
rp
= (struct hpdevice
*)mbadrv(io
->i_adapt
, unit
);
sc
= &hp_softc
[io
->i_adapt
][unit
];
lp
= &hplabel
[io
->i_adapt
][unit
];
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
+ sc
->ssect
; /* physical block #*/
if (sc
->debug
& HPF_ECCDEBUG
)
printf("bcr=%d npf=%d ssect=%d sectsiz=%d i_cc=%d\n",
bcr
, npf
, sc
->ssect
, sectsiz
, io
->i_cc
);
printf("hp%d: soft ecc sn%d\n", unit
, bn
);
mask
= MASKREG(rp
->hpec2
);
for (i
= mask
, bit
= 0; i
; i
>>= 1)
printf("%d-bit error\n", bit
);
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 (sc
->debug
& HPF_ECCDEBUG
)
printf("addr=%x old=%x ", addr
,
if (sc
->debug
& HPF_ECCDEBUG
)
printf("new=%x\n",(*addr
& 0xff));
* Set skip-sector-inhibit and
rp
->hpcs1
= HP_DCLR
| HP_GO
;
rp
->hpcs1
= HP_DCLR
| HP_GO
;
if (sc
->debug
& HPF_BSEDEBUG
)
printf("hpecc: BSE @ bn %d\n", bn
);
cn
= bn
/ lp
->d_secpercyl
;
sn
= bn
% lp
->d_secpercyl
;
tn
= sn
/ lp
->d_nsectors
;
sn
= sn
% lp
->d_nsectors
;
if ((bbn
= isbad(&hpbad
[io
->i_adapt
][unit
], cn
, tn
, sn
)) < 0)
bbn
= lp
->d_ncylinders
* lp
->d_secpercyl
- lp
->d_nsectors
- 1
cn
= bbn
/ lp
->d_secpercyl
;
sn
= bbn
% lp
->d_secpercyl
;
tn
= sn
/ lp
->d_nsectors
;
sn
= sn
% lp
->d_nsectors
;
io
->i_ma
+= npf
* sectsiz
;
if (sc
->debug
& HPF_BSEDEBUG
)
printf("revector to cn %d tn %d sn %d\n", cn
, tn
, sn
);
mbastart(io
, io
->i_unit
, io
->i_flgs
);
return (rp
->hpds
&HPDS_ERR
);
printf("hpecc: flag=%d\n", flag
);
register unit
= io
->i_unit
;
register struct hp_softc
*sc
= &hp_softc
[io
->i_adapt
][unit
];
register struct disklabel
*lp
= &hplabel
[io
->i_adapt
][unit
];
struct mba_drv
*drv
= mbadrv(io
->i_adapt
, unit
);
if (drv
->mbd_dt
&MBDT_TAP
)
*(struct disklabel
*)arg
= *lp
;
if (drv
->mbd_dt
&MBDT_TAP
)
*(struct dkbad
*)arg
= hpbad
[io
->i_adapt
][unit
];
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 */
lp
->d_secpercyl
+= lp
->d_ntracks
;
case SAIONOSSI
: /* remove skip-sector-inhibit */
if (io
->i_flgs
& F_SSI
) {
drv
->mbd_of
&= ~HPOF_SSEI
;
lp
->d_secpercyl
-= lp
->d_ntracks
;
case SAIOSSDEV
: /* drive have skip sector? */
return (RM80(sc
->type
) ? 0 : ECMD
);