* Copyright (c) 1982, 1990 The Regents of the University of California.
* 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
* @(#)ct.c 7.3 (Berkeley) 5/4/91
* CS80 cartridge tape driver (9144, 88140, 9145)
* C_CC bit (character count option) when used in the CS/80 command
* 'set options' will cause the tape not to stream.
* make filesystem compatible
* make block mode work according to mtio(4) spec. (if possible)
* merge with cs80 disk driver
/* number of eof marks to remember */
int ctinit(), ctstart(), ctgo(), ctintr();
struct driver ctdriver
= {
ctinit
, "ct", ctstart
, ctgo
, ctintr
,
struct ct_ssmcmd sc_ssmc
;
struct ct_soptcmd sc_soptc
;
struct ct_clearcmd sc_clear
;
#define CTF_STATWAIT 0x100
#define CTF_CANSTREAM 0x200
int nctinfo
= sizeof(ctinfo
) / sizeof(ctinfo
[0]);
#define UNIT(x) (minor(x) & 3)
#define ctpunit(x) ((x) & 7)
register struct hp_device
*hd
;
register struct ct_softc
*sc
= &ct_softc
[hd
->hp_unit
];
sc
->sc_punit
= ctpunit(hd
->hp_flags
);
sc
->sc_dq
.dq_ctlr
= hd
->hp_ctlr
;
sc
->sc_dq
.dq_unit
= hd
->hp_unit
;
sc
->sc_dq
.dq_slave
= hd
->hp_slave
;
sc
->sc_dq
.dq_driver
= &ctdriver
;
sc
->sc_flags
|= CTF_ALIVE
;
register struct ct_softc
*sc
;
register struct hp_device
*hd
;
* Read device id and verify that:
* 2. It is one of our recognized tape devices
* 3. It has the proper physical unit number
id
= hpibid(hd
->hp_ctlr
, hd
->hp_slave
);
for (i
= 0; i
< nctinfo
; i
++)
if (id
== ctinfo
[i
].hwid
)
if (i
== nctinfo
|| sc
->sc_punit
!= ctinfo
[i
].punit
)
* Collect device description.
* Right now we only need this to differentiate 7945 from 7946.
* Note that we always issue the describe command to unit 0.
hpibsend(hd
->hp_ctlr
, hd
->hp_slave
, C_CMD
, cmd
, sizeof(cmd
));
hpibrecv(hd
->hp_ctlr
, hd
->hp_slave
, C_EXEC
, &desc
, 37);
hpibrecv(hd
->hp_ctlr
, hd
->hp_slave
, C_QSTAT
, &stat
, sizeof(stat
));
bzero(name
, sizeof(name
));
register int n
= desc
.d_name
;
for (i
= 5; i
>= 0; i
--) {
name
[i
] = (n
& 0xf) + '0';
switch (ctinfo
[id
].hwid
) {
if (bcmp(name
, "079450", 6) == 0)
return(-1); /* not really a 7946 */
sc
->sc_flags
|= CTF_CANSTREAM
;
printf("ct%d: %s %stape\n", hd
->hp_unit
, ctinfo
[id
].desc
,
(sc
->sc_flags
& CTF_CANSTREAM
) ? "streaming " : " ");
register struct ct_softc
*sc
;
register struct hp_device
*hd
;
sc
->sc_clear
.unit
= C_SUNIT(sc
->sc_punit
);
sc
->sc_clear
.cmd
= C_CLEAR
;
hpibsend(hd
->hp_ctlr
, hd
->hp_slave
, C_TCMD
, &sc
->sc_clear
,
hpibswait(hd
->hp_ctlr
, hd
->hp_slave
);
hpibrecv(hd
->hp_ctlr
, hd
->hp_slave
, C_QSTAT
, &stat
, sizeof(stat
));
sc
->sc_src
.unit
= C_SUNIT(CTCTLR
);
sc
->sc_src
.param
= C_REL
;
hpibsend(hd
->hp_ctlr
, hd
->hp_slave
, C_CMD
, &sc
->sc_src
,
hpibswait(hd
->hp_ctlr
, hd
->hp_slave
);
hpibrecv(hd
->hp_ctlr
, hd
->hp_slave
, C_QSTAT
, &stat
, sizeof(stat
));
sc
->sc_ssmc
.unit
= C_SUNIT(sc
->sc_punit
);
sc
->sc_ssmc
.refm
= REF_MASK
;
sc
->sc_ssmc
.fefm
= FEF_MASK
;
sc
->sc_ssmc
.aefm
= AEF_MASK
;
sc
->sc_ssmc
.iefm
= IEF_MASK
;
hpibsend(hd
->hp_ctlr
, hd
->hp_slave
, C_CMD
, &sc
->sc_ssmc
,
hpibswait(hd
->hp_ctlr
, hd
->hp_slave
);
hpibrecv(hd
->hp_ctlr
, hd
->hp_slave
, C_QSTAT
, &stat
, sizeof(stat
));
sc
->sc_soptc
.unit
= C_SUNIT(sc
->sc_punit
);
sc
->sc_soptc
.nop
= C_NOP
;
sc
->sc_soptc
.cmd
= C_SOPT
;
sc
->sc_soptc
.opt
= C_SPAR
;
hpibsend(hd
->hp_ctlr
, hd
->hp_slave
, C_CMD
, &sc
->sc_soptc
,
hpibswait(hd
->hp_ctlr
, hd
->hp_slave
);
hpibrecv(hd
->hp_ctlr
, hd
->hp_slave
, C_QSTAT
, &stat
, sizeof(stat
));
ctopen(dev
, flag
, type
, p
)
register struct ct_softc
*sc
= &ct_softc
[UNIT(dev
)];
if (UNIT(dev
) >= NCT
|| (sc
->sc_flags
& CTF_ALIVE
) == 0)
if (sc
->sc_flags
& CTF_OPEN
)
sc
->sc_soptc
.unit
= C_SUNIT(sc
->sc_punit
);
sc
->sc_soptc
.nop
= C_NOP
;
sc
->sc_soptc
.cmd
= C_SOPT
;
if ((dev
& CT_STREAM
) && (sc
->sc_flags
& CTF_CANSTREAM
))
sc
->sc_soptc
.opt
= C_SPAR
| C_IMRPT
;
sc
->sc_soptc
.opt
= C_SPAR
;
* Check the return of hpibsend() and hpibswait().
* Drive could be loading/unloading a tape. If not checked,
cc
= hpibsend(sc
->sc_hd
->hp_ctlr
, sc
->sc_hd
->hp_slave
,
C_CMD
, &sc
->sc_soptc
, sizeof(sc
->sc_soptc
));
if (cc
!= sizeof(sc
->sc_soptc
))
hpibswait(sc
->sc_hd
->hp_ctlr
, sc
->sc_hd
->hp_slave
);
cc
= hpibrecv(sc
->sc_hd
->hp_ctlr
, sc
->sc_hd
->hp_slave
, C_QSTAT
,
sc
->sc_tpr
= tprintf_open(p
);
sc
->sc_flags
|= CTF_OPEN
;
register struct ct_softc
*sc
= &ct_softc
[UNIT(dev
)];
if ((sc
->sc_flags
& (CTF_WRT
|CTF_WRTTN
)) == (CTF_WRT
|CTF_WRTTN
) &&
(sc
->sc_flags
& CTF_EOT
) == 0 ) { /* XXX return error if EOT ?? */
ctcommand(dev
, MTWEOF
, 2);
ctcommand(dev
, MTBSR
, 1);
if (sc
->sc_eofp
== EOFS
- 1)
printf("ct%d: ctclose backup eofs prt %d blk %d\n",
UNIT(dev
), sc
->sc_eofp
, sc
->sc_eofs
[sc
->sc_eofp
]);
if ((minor(dev
) & CT_NOREW
) == 0)
ctcommand(dev
, MTREW
, 1);
sc
->sc_flags
&= ~(CTF_OPEN
| CTF_WRT
| CTF_WRTTN
);
tprintf_close(sc
->sc_tpr
);
printf("ctclose: flags %x\n", sc
->sc_flags
);
register struct ct_softc
*sc
= &ct_softc
[UNIT(dev
)];
register struct buf
*bp
= &ctbuf
[UNIT(dev
)];
register struct buf
*nbp
= 0;
if (cmd
== MTBSF
&& sc
->sc_eofp
== EOFS
- 1) {
cnt
= sc
->sc_eofs
[EOFS
- 1] - cnt
;
ctcommand(dev
, MTREW
, 1);
ctcommand(dev
, MTFSF
, cnt
);
if (cmd
== MTBSF
&& sc
->sc_eofp
- cnt
< 0) {
nbp
= (struct buf
*)geteblk(MAXBSIZE
);
bp
->b_un
.b_addr
= nbp
->b_un
.b_addr
;
sc
->sc_blkno
= sc
->sc_eofs
[sc
->sc_eofp
];
printf("ct%d: backup eof pos %d blk %d\n",
sc
->sc_eofs
[sc
->sc_eofp
]);
sc
->sc_flags
&= ~CTF_CMD
;
dp
->b_actl
->av_forw
= bp
;
register struct ct_softc
*sc
= &ct_softc
[unit
];
sc
->sc_addr
= bp
->b_un
.b_addr
;
sc
->sc_resid
= bp
->b_bcount
;
register struct ct_softc
*sc
= &ct_softc
[unit
];
if ((sc
->sc_flags
& CTF_CMD
) && sc
->sc_bp
== bp
) {
sc
->sc_ul
.unit
= C_SUNIT(sc
->sc_punit
);
sc
->sc_ul
.cmd
= C_UNLOAD
;
hpibsend(sc
->sc_hd
->hp_ctlr
, sc
->sc_hd
->hp_slave
,
C_CMD
, &sc
->sc_ul
, sizeof(sc
->sc_ul
));
sc
->sc_wfm
.unit
= C_SUNIT(sc
->sc_punit
);
hpibsend(sc
->sc_hd
->hp_ctlr
, sc
->sc_hd
->hp_slave
,
C_CMD
, &sc
->sc_wfm
, sizeof(sc
->sc_wfm
));
printf("ct%d: clearing eofs\n", unit
);
sc
->sc_ioc
.saddr
= C_SADDR
;
sc
->sc_ioc
.addr
= sc
->sc_blkno
;
sc
->sc_ioc
.unit
= C_SUNIT(sc
->sc_punit
);
sc
->sc_ioc
.slen
= C_SLEN
;
hpibsend(sc
->sc_hd
->hp_ctlr
, sc
->sc_hd
->hp_slave
,
C_CMD
, &sc
->sc_ioc
, sizeof(sc
->sc_ioc
));
if ((bp
->b_flags
& B_READ
) &&
sc
->sc_flags
& (CTF_BEOF
|CTF_EOT
)) {
printf("ctstart: before flags %x\n", sc
->sc_flags
);
if (sc
->sc_flags
& CTF_BEOF
) {
sc
->sc_flags
&= ~CTF_BEOF
;
sc
->sc_flags
|= CTF_AEOF
;
printf("ctstart: after flags %x\n", sc
->sc_flags
);
bp
->b_resid
= bp
->b_bcount
;
cttab
[unit
].b_actf
= bp
= bp
->av_forw
;
cttab
[unit
].b_active
= 0;
sc
->sc_addr
= bp
->b_un
.b_addr
;
sc
->sc_resid
= bp
->b_bcount
;
sc
->sc_ioc
.unit
= C_SUNIT(sc
->sc_punit
);
sc
->sc_ioc
.saddr
= C_SADDR
;
sc
->sc_ioc
.addr
= sc
->sc_blkno
;
sc
->sc_ioc
.slen
= C_SLEN
;
sc
->sc_ioc
.len
= sc
->sc_resid
;
if (bp
->b_flags
& B_READ
)
sc
->sc_ioc
.cmd
= C_WRITE
;
sc
->sc_flags
|= (CTF_WRT
| CTF_WRTTN
);
hpibsend(sc
->sc_hd
->hp_ctlr
, sc
->sc_hd
->hp_slave
, C_CMD
,
&sc
->sc_ioc
, sizeof(sc
->sc_ioc
));
hpibawait(sc
->sc_hd
->hp_ctlr
);
register struct ct_softc
*sc
= &ct_softc
[unit
];
hpibgo(sc
->sc_hd
->hp_ctlr
, sc
->sc_hd
->hp_slave
, C_EXEC
,
sc
->sc_addr
, sc
->sc_resid
, bp
->b_flags
& B_READ
);
* Hideous grue to handle EOF/EOT (mostly for reads)
register struct ct_softc
*sc
;
* EOT on a write is an error.
if ((bp
->b_flags
& B_READ
) == 0) {
bp
->b_resid
= bp
->b_bcount
;
* Use returned block position to determine how many blocks
* we really read and update b_resid.
blks
= sc
->sc_stat
.c_blk
- sc
->sc_blkno
- 1;
printf("cteof: bc %d oblk %d nblk %d read %d, resid %d\n",
bp
->b_bcount
, sc
->sc_blkno
, sc
->sc_stat
.c_blk
,
blks
, bp
->b_bcount
- CTKTOB(blks
));
if (blks
== -1) { /* 9145 on EOF does not change sc_stat.c_blk */
sc
->sc_blkno
= sc
->sc_stat
.c_blk
;
bp
->b_resid
= bp
->b_bcount
- CTKTOB(blks
);
* If we are at physical EOV or were after an EOF,
* we are now at logical EOT.
if ((sc
->sc_stat
.c_aef
& AEF_EOV
) ||
(sc
->sc_flags
& CTF_AEOF
)) {
sc
->sc_flags
&= ~(CTF_AEOF
|CTF_BEOF
);
* If we were before an EOF or we have just completed a FSF,
else if ((sc
->sc_flags
& CTF_BEOF
) ||
(sc
->sc_flags
& CTF_CMD
) && sc
->sc_cmd
== MTFSF
) {
sc
->sc_flags
|= CTF_AEOF
;
sc
->sc_flags
&= ~CTF_BEOF
;
* Otherwise if we read something we are now before EOF
* (and no longer after EOF).
sc
->sc_flags
|= CTF_BEOF
;
sc
->sc_flags
&= ~CTF_AEOF
;
* Finally, if we didn't read anything we just passed an EOF
sc
->sc_flags
|= CTF_AEOF
;
printf("cteof: leaving flags %x\n", sc
->sc_flags
);
register struct ct_softc
*sc
= &ct_softc
[unit
];
printf("ct%d: bp == NULL\n", unit
);
if (sc
->sc_flags
& CTF_IO
) {
if (hpibustart(sc
->sc_hd
->hp_ctlr
))
if ((sc
->sc_flags
& CTF_STATWAIT
) == 0) {
if (hpibpptest(sc
->sc_hd
->hp_ctlr
, sc
->sc_hd
->hp_slave
) == 0) {
sc
->sc_flags
|= CTF_STATWAIT
;
hpibawait(sc
->sc_hd
->hp_ctlr
);
sc
->sc_flags
&= ~CTF_STATWAIT
;
hpibrecv(sc
->sc_hd
->hp_ctlr
, sc
->sc_hd
->hp_slave
, C_QSTAT
, &stat
, 1);
printf("ctintr: before flags %x\n", sc
->sc_flags
);
sc
->sc_rsc
.unit
= C_SUNIT(sc
->sc_punit
);
sc
->sc_rsc
.cmd
= C_STATUS
;
hpibsend(sc
->sc_hd
->hp_ctlr
, sc
->sc_hd
->hp_slave
, C_CMD
,
&sc
->sc_rsc
, sizeof(sc
->sc_rsc
));
hpibrecv(sc
->sc_hd
->hp_ctlr
, sc
->sc_hd
->hp_slave
, C_EXEC
,
&sc
->sc_stat
, sizeof(sc
->sc_stat
));
hpibrecv(sc
->sc_hd
->hp_ctlr
, sc
->sc_hd
->hp_slave
, C_QSTAT
,
printf("ctintr: return stat 0x%x, A%x F%x blk %d\n",
sc
->sc_stat
.c_fef
, sc
->sc_stat
.c_blk
);
if (sc
->sc_stat
.c_aef
& (AEF_EOF
| AEF_EOV
)) {
if (sc
->sc_stat
.c_fef
& FEF_PF
) {
if (sc
->sc_stat
.c_fef
& FEF_REXMT
) {
if (sc
->sc_stat
.c_aef
& 0x5800) {
if (sc
->sc_stat
.c_aef
& 0x4000)
"ct%d: uninitialized media\n",
if (sc
->sc_stat
.c_aef
& 0x1000)
"ct%d: not ready\n", unit
);
if (sc
->sc_stat
.c_aef
& 0x0800)
"ct%d: write protect\n", unit
);
printf("ct%d err: v%d u%d ru%d bn%d, ",
(sc
->sc_stat
.c_vu
>>4)&0xF,
printf("R0x%x F0x%x A0x%x I0x%x\n",
printf("ct%d: request status failed\n", unit
);
if (sc
->sc_flags
& CTF_CMD
) {
sc
->sc_flags
&= ~(CTF_BEOF
|CTF_AEOF
);
sc
->sc_blkno
+= CTBTOK(sc
->sc_resid
);
sc
->sc_flags
&= ~(CTF_AEOF
|CTF_BEOF
|CTF_EOT
);
sc
->sc_flags
&= ~CTF_BEOF
;
if (sc
->sc_flags
& CTF_EOT
) {
sc
->sc_flags
|= CTF_AEOF
;
sc
->sc_flags
&= ~CTF_EOT
;
} else if (sc
->sc_flags
& CTF_AEOF
) {
sc
->sc_flags
|= CTF_BEOF
;
sc
->sc_flags
&= ~CTF_AEOF
;
sc
->sc_flags
&= ~CTF_BEOF
;
if (sc
->sc_flags
& (CTF_AEOF
|CTF_EOT
)) {
sc
->sc_flags
&= ~CTF_AEOF
;
sc
->sc_flags
|= CTF_AEOF
;
sc
->sc_flags
&= ~(CTF_BEOF
|CTF_AEOF
|CTF_EOT
);
sc
->sc_flags
&= ~CTF_AEOF
;
sc
->sc_blkno
+= CTBTOK(sc
->sc_resid
);
printf("ctintr: after flags %x\n", sc
->sc_flags
);
cttab
[unit
].b_actf
= bp
->av_forw
;
if (cttab
[unit
].b_actf
== NULL
) {
cttab
[unit
].b_active
= 0;
register int unit
= UNIT(dev
);
return(physio(ctstrategy
, &ctbuf
[unit
], dev
, B_READ
, minphys
, uio
));
register int unit
= UNIT(dev
);
return(physio(ctstrategy
, &ctbuf
[unit
], dev
, B_WRITE
, minphys
, uio
));
ctioctl(dev
, cmd
, data
, flag
)
register struct mtop
*op
;
op
= (struct mtop
*)data
;
ctcommand(dev
, op
->mt_op
, cnt
);
register struct ct_softc
*sc
= &ct_softc
[unit
];
if (sc
->sc_eofp
== EOFS
- 1)
if (sc
->sc_eofp
== EOFS
- 1)
sc
->sc_eofs
[EOFS
- 1] = EOFS
;
sc
->sc_eofs
[sc
->sc_eofp
] = sc
->sc_blkno
- 1;
printf("ct%d: add eof pos %d blk %d\n",
sc
->sc_eofs
[sc
->sc_eofp
]);