* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
* 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
* @(#)tz.c 8.4 (Berkeley) 1/11/94
* from: $Header: /sprite/src/kernel/dev/RCS/devSCSITape.c,
* v 8.14 89/07/31 17:26:13 mendel Exp $ SPRITE (Berkeley)
* SCSI CCS (Command Command Set) tape driver.
#include <pmax/dev/device.h>
#include <pmax/dev/scsi.h>
void tzstart(), tzdone();
struct driver tzdriver
= {
"tz", tzprobe
, tzstart
, tzdone
,
struct scsi_device
*sc_sd
; /* physical unit info */
int sc_flags
; /* see below */
int sc_tapeid
; /* tape drive id */
int sc_blklen
; /* 0 = variable len records */
long sc_numblks
; /* number of blocks on tape */
tpr_t sc_ctty
; /* terminal for error messages */
struct buf sc_tab
; /* queue of pending operations */
struct buf sc_buf
; /* buf for doing I/O */
struct buf sc_errbuf
; /* buf for doing REQUEST_SENSE */
struct ScsiCmd sc_cmd
; /* command for controller */
ScsiGroup0Cmd sc_rwcmd
; /* SCSI cmd for read/write */
struct scsi_fmt_cdb sc_cdb
; /* SCSI cmd if not read/write */
struct scsi_fmt_sense sc_sense
; /* sense data from last cmd */
struct ScsiTapeModeSelectHdr sc_mode
; /* SCSI_MODE_SENSE data */
char sc_modelen
; /* SCSI_MODE_SENSE data length */
#define TZF_ALIVE 0x01 /* drive found and ready */
#define TZF_SENSEINPROGRESS 0x02 /* REQUEST_SENSE command in progress */
#define TZF_ALTCMD 0x04 /* alternate command in progress */
#define TZF_WRITTEN 0x08 /* tape has been written to */
#define TZF_OPEN 0x10 /* device is open */
#define TZF_WAIT 0x20 /* waiting for sc_tab to drain */
#define TZF_SEENEOF 0x40 /* seen file mark on read */
/* bits in minor device */
#define tzunit(x) (minor(x) >> 4) /* tz%d unit number */
#define TZ_NOREWIND 0x01 /* don't rewind on close */
#define TZ_HIDENSITY 0x02
* Test to see if device is present.
* Return true if found and initialized ok.
register struct tz_softc
*sc
= &tz_softc
[sd
->sd_unit
];
/* init some parameters that don't change */
sc
->sc_cmd
.unit
= sd
->sd_unit
;
sc
->sc_rwcmd
.unitNumber
= sd
->sd_slave
;
/* try to find out what type of device this is */
sc
->sc_flags
= TZF_ALTCMD
; /* force use of sc_cdb */
sc
->sc_cdb
.len
= sizeof(ScsiGroup0Cmd
);
scsiGroup0Cmd(SCSI_INQUIRY
, sd
->sd_slave
, 0, sizeof(inqbuf
),
(ScsiGroup0Cmd
*)sc
->sc_cdb
.cdb
);
sc
->sc_buf
.b_flags
= B_BUSY
| B_READ
;
sc
->sc_buf
.b_bcount
= sizeof(inqbuf
);
sc
->sc_buf
.b_un
.b_addr
= (caddr_t
)&inqbuf
;
sc
->sc_buf
.b_actf
= (struct buf
*)0;
sc
->sc_buf
.b_actb
= &sc
->sc_tab
.b_actf
;
sc
->sc_tab
.b_actf
= &sc
->sc_buf
;
sc
->sc_tab
.b_actb
= &sc
->sc_buf
.b_actf
;
if (biowait(&sc
->sc_buf
) ||
(i
= sizeof(inqbuf
) - sc
->sc_buf
.b_resid
) < 5)
if (inqbuf
.type
!= SCSI_TAPE_TYPE
|| !inqbuf
.rmb
)
/* check for device ready to clear UNIT_ATTN */
sc
->sc_cdb
.len
= sizeof(ScsiGroup0Cmd
);
scsiGroup0Cmd(SCSI_TEST_UNIT_READY
, sd
->sd_slave
, 0, 0,
(ScsiGroup0Cmd
*)sc
->sc_cdb
.cdb
);
sc
->sc_buf
.b_flags
= B_BUSY
| B_READ
;
sc
->sc_buf
.b_un
.b_addr
= (caddr_t
)0;
sc
->sc_buf
.b_actf
= (struct buf
*)0;
sc
->sc_buf
.b_actb
= &sc
->sc_tab
.b_actf
;
sc
->sc_tab
.b_actf
= &sc
->sc_buf
;
sc
->sc_tab
.b_actb
= &sc
->sc_buf
.b_actf
;
(void) biowait(&sc
->sc_buf
);
sc
->sc_flags
= TZF_ALIVE
;
printf("tz%d at %s%d drive %d slave %d", sd
->sd_unit
,
sd
->sd_cdriver
->d_name
, sd
->sd_ctlr
, sd
->sd_drive
,
if (i
== 5 && inqbuf
.version
== 1 && inqbuf
.qualifier
== 0x50) {
sc
->sc_tapeid
= MT_ISTK50
;
} else if (i
>= 5 && inqbuf
.version
== 1 && inqbuf
.qualifier
== 0 &&
/* assume Emultex MT02 controller */
sc
->sc_tapeid
= MT_ISMT02
;
} else if (inqbuf
.version
> 2 || i
< 36) {
printf(" GENERIC SCSI tape device: qual 0x%x, ver %d\n",
inqbuf
.qualifier
, inqbuf
.version
);
char vid
[9], pid
[17], revl
[5];
bcopy((caddr_t
)inqbuf
.vendorID
, (caddr_t
)vid
, 8);
bcopy((caddr_t
)inqbuf
.productID
, (caddr_t
)pid
, 16);
bcopy((caddr_t
)inqbuf
.revLevel
, (caddr_t
)revl
, 4);
printf(" %s %s rev %s\n", vid
, pid
, revl
);
if (bcmp("EXB-8200", pid
, 8) == 0) {
sc
->sc_tapeid
= MT_ISEXABYTE
;
} else if (bcmp("VIPER 150", pid
, 9) == 0) {
sc
->sc_tapeid
= MT_ISVIPER1
;
} else if (bcmp("Python 25501", pid
, 12) == 0) {
sc
->sc_tapeid
= MT_ISPYTHON
;
} else if (bcmp("HP35450A", pid
, 8) == 0) {
/* XXX "extra" stat makes the HP drive happy at boot time */
stat
= scsi_test_unit_rdy(ctlr
, slave
, unit
);
sc
->sc_tapeid
= MT_ISHPDAT
;
} else if (bcmp("123107 SCSI", pid
, 11) == 0) {
sc
->sc_tapeid
= MT_ISMFOUR
;
printf("tz%d: assuming GENERIC SCSI tape device\n",
inqbuf
.type
, inqbuf
.qualifier
, inqbuf
.version
);
/* doesn't exist or not a CCS device */
* Perform a special tape command on a SCSI Tape drive.
tzcommand(dev
, command
, code
, count
, data
)
register struct tz_softc
*sc
= &tz_softc
[tzunit(dev
)];
register ScsiGroup0Cmd
*c
;
/* wait for pending operations to finish */
while (sc
->sc_tab
.b_actf
) {
sc
->sc_flags
|= TZF_WAIT
;
sleep(&sc
->sc_flags
, PZERO
);
sc
->sc_flags
|= TZF_ALTCMD
; /* force use of sc_cdb */
sc
->sc_cdb
.len
= sizeof(ScsiGroup0Cmd
);
c
= (ScsiGroup0Cmd
*)sc
->sc_cdb
.cdb
;
c
->unitNumber
= sc
->sc_sd
->sd_slave
;
c
->midAddr
= count
>> 16;
if (command
== SCSI_MODE_SELECT
)
sc
->sc_buf
.b_flags
= B_BUSY
;
sc
->sc_buf
.b_flags
= B_BUSY
| B_READ
;
/* this seems to work but doesn't give us a speed advantage */
if (command
== SCSI_TEST_UNIT_READY
)
sc
->sc_cmd
.flags
|= SCSICMD_USE_SYNC
;
sc
->sc_buf
.b_bcount
= data
? count
: 0;
sc
->sc_buf
.b_un
.b_addr
= data
;
sc
->sc_buf
.b_actf
= (struct buf
*)0;
sc
->sc_buf
.b_actb
= &sc
->sc_tab
.b_actf
;
sc
->sc_tab
.b_actf
= &sc
->sc_buf
;
sc
->sc_tab
.b_actb
= &sc
->sc_buf
.b_actf
;
tzstart(sc
->sc_sd
->sd_unit
);
error
= biowait(&sc
->sc_buf
);
sc
->sc_flags
&= ~TZF_ALTCMD
; /* force use of sc_cdb */
printf("tzcommand: resid %d\n", sc
->sc_buf
.b_resid
); /* XXX */
sc
->sc_flags
&= ~TZF_SEENEOF
;
register struct tz_softc
*sc
= &tz_softc
[unit
];
register struct buf
*bp
= sc
->sc_tab
.b_actf
;
sc
->sc_cmd
.buf
= bp
->b_un
.b_addr
;
sc
->sc_cmd
.buflen
= bp
->b_bcount
;
if (sc
->sc_flags
& (TZF_SENSEINPROGRESS
| TZF_ALTCMD
)) {
if (bp
->b_flags
& B_READ
)
sc
->sc_cmd
.flags
&= ~SCSICMD_DATA_TO_DEVICE
;
sc
->sc_cmd
.flags
|= SCSICMD_DATA_TO_DEVICE
;
sc
->sc_cmd
.cmd
= sc
->sc_cdb
.cdb
;
sc
->sc_cmd
.cmdlen
= sc
->sc_cdb
.len
;
if (bp
->b_flags
& B_READ
) {
sc
->sc_rwcmd
.command
= SCSI_READ
;
sc
->sc_flags
&= ~TZF_WRITTEN
;
sc
->sc_cmd
.flags
= SCSICMD_DATA_TO_DEVICE
;
sc
->sc_rwcmd
.command
= SCSI_WRITE
;
sc
->sc_flags
|= TZF_WRITTEN
;
sc
->sc_cmd
.cmd
= (u_char
*)&sc
->sc_rwcmd
;
sc
->sc_cmd
.cmdlen
= sizeof(sc
->sc_rwcmd
);
/* fixed sized records */
n
= bp
->b_bcount
/ sc
->sc_blklen
;
if (bp
->b_bcount
% sc
->sc_blklen
) {
"tz%d: I/O not block aligned %d/%ld\n",
unit
, sc
->sc_blklen
, bp
->b_bcount
);
tzdone(unit
, EIO
, bp
->b_bcount
, 0);
sc
->sc_rwcmd
.highAddr
= 1;
/* variable sized records */
sc
->sc_rwcmd
.highAddr
= 0;
sc
->sc_rwcmd
.midAddr
= n
>> 16;
sc
->sc_rwcmd
.lowAddr
= n
>> 8;
sc
->sc_rwcmd
.blockCount
= n
;
/* tell controller to start this command */
(*sc
->sc_sd
->sd_cdriver
->d_start
)(&sc
->sc_cmd
);
* This is called by the controller driver when the command is done.
tzdone(unit
, error
, resid
, status
)
int error
; /* error number from errno.h */
int resid
; /* amount not transfered */
int status
; /* SCSI status byte */
register struct tz_softc
*sc
= &tz_softc
[unit
];
register struct buf
*bp
= sc
->sc_tab
.b_actf
;
printf("tz%d: bp == NULL\n", unit
);
if (sc
->sc_flags
& TZF_SENSEINPROGRESS
) {
sc
->sc_flags
&= ~TZF_SENSEINPROGRESS
;
*bp
->b_actb
= dp
= bp
->b_actf
; /* remove sc_errbuf */
if (error
|| (status
& SCSI_STATUS_CHECKCOND
)) {
printf("tz%d: error reading sense data: error %d scsi status 0x%x\n",
* We got an error during the REQUEST_SENSE,
* fill in no sense for data.
sc
->sc_sense
.sense
[0] = 0x70;
sc
->sc_sense
.sense
[2] = SCSI_CLASS7_NO_SENSE
;
sp
= (ScsiClass7Sense
*)sc
->sc_sense
.sense
;
resid
= (sp
->info1
<< 24) | (sp
->info2
<< 16) |
(sp
->info3
<< 8) | sp
->info4
;
case SCSI_CLASS7_NO_SENSE
:
* Hit a filemark, end of media, or
* Fixed length blocks, an error.
if (sc
->sc_blklen
&& sp
->badBlockLen
) {
"tz%d: Incorrect Block Length, expected %d got %d\n",
unit
, sc
->sc_blklen
, resid
);
* Variable length records but
* attempted to read less than a
"tz%d: Partial Read of Variable Length Tape Block, expected %d read %d\n",
unit
, bp
->b_bcount
- resid
,
sc
->sc_flags
|= TZF_SEENEOF
;
* Attempting to read more than a record is
* OK. Just record how much was actually read.
case SCSI_CLASS7_UNIT_ATTN
:
if (!(sc
->sc_flags
& TZF_OPEN
))
scsiPrintSense((ScsiClass7Sense
*)
sizeof(sc
->sc_sense
.sense
) - resid
);
} else if (error
|| (status
& SCSI_STATUS_CHECKCOND
)) {
printf("tz%d: error %d scsi status 0x%x\n",
sc
->sc_sense
.status
= status
;
if (status
& SCSI_STATUS_CHECKCOND
) {
* Start a REQUEST_SENSE command.
* Since we are called at interrupt time, we can't
* wait for the command to finish; that's why we use
sc
->sc_flags
|= TZF_SENSEINPROGRESS
;
sc
->sc_cdb
.len
= sizeof(ScsiGroup0Cmd
);
scsiGroup0Cmd(SCSI_REQUEST_SENSE
, sc
->sc_sd
->sd_slave
,
0, sizeof(sc
->sc_sense
.sense
),
(ScsiGroup0Cmd
*)sc
->sc_cdb
.cdb
);
sc
->sc_errbuf
.b_flags
= B_BUSY
| B_PHYS
| B_READ
;
sc
->sc_errbuf
.b_bcount
= sizeof(sc
->sc_sense
.sense
);
sc
->sc_errbuf
.b_un
.b_addr
= (caddr_t
)sc
->sc_sense
.sense
;
sc
->sc_errbuf
.b_actf
= bp
;
sc
->sc_errbuf
.b_actb
= bp
->b_actb
;
*bp
->b_actb
= &sc
->sc_errbuf
;
bp
->b_actb
= &sc
->sc_errbuf
.b_actf
;
sc
->sc_sense
.status
= status
;
sc
->sc_tab
.b_actb
= bp
->b_actb
;
if (sc
->sc_flags
& TZF_WAIT
) {
sc
->sc_flags
&= ~TZF_WAIT
;
tzopen(dev
, flags
, type
, p
)
register int unit
= tzunit(dev
);
register struct tz_softc
*sc
= &tz_softc
[unit
];
if (unit
>= NTZ
|| sc
->sc_sd
== NULL
)
if (!(sc
->sc_flags
& TZF_ALIVE
)) {
/* check again, tape may have been turned off at boot time */
if (sc
->sc_flags
& TZF_OPEN
)
/* clear UNIT_ATTENTION */
error
= tzcommand(dev
, SCSI_TEST_UNIT_READY
, 0, 0, 0);
ScsiClass7Sense
*sp
= (ScsiClass7Sense
*)sc
->sc_sense
.sense
;
/* return error if last error was not UNIT_ATTENTION */
if (!(sc
->sc_sense
.status
& SCSI_STATUS_CHECKCOND
) ||
sp
->error7
!= 0x70 || sp
->key
!= SCSI_CLASS7_UNIT_ATTN
)
* Try it again just to be sure and
* try to negotiate synchonous transfers.
error
= tzcommand(dev
, SCSI_TEST_UNIT_READY
, 0, 0, 0);
/* get the current mode settings */
error
= tzcommand(dev
, SCSI_MODE_SENSE
, 0,
sc
->sc_modelen
, (caddr_t
)&sc
->sc_mode
);
/* check for write protected tape */
if ((flags
& FWRITE
) && sc
->sc_mode
.writeprot
) {
uprintf("tz%d: write protected\n", unit
);
if (minor(dev
) & TZ_FIXEDBLK
)
sc
->sc_blklen
= st_exblklen
;
sc
->sc_blklen
= (sc
->sc_mode
.block_size2
<< 16) |
(sc
->sc_mode
.block_size1
<< 8) | sc
->sc_mode
.block_size0
;
/* save total number of blocks on tape */
sc
->sc_numblks
= (sc
->sc_mode
.blocks_2
<< 16) |
(sc
->sc_mode
.blocks_1
<< 8) | sc
->sc_mode
.blocks_0
;
/* setup for mode select */
sc
->sc_mode
.bufferedMode
= 1;
sc
->sc_mode
.blocks_0
= 0;
sc
->sc_mode
.blocks_1
= 0;
sc
->sc_mode
.blocks_2
= 0;
sc
->sc_mode
.block_size0
= sc
->sc_blklen
>> 16;
sc
->sc_mode
.block_size1
= sc
->sc_blklen
>> 8;
sc
->sc_mode
.block_size2
= sc
->sc_blklen
;
/* check for tape density changes */
if (minor(dev
) & TZ_HIDENSITY
)
sc
->sc_mode
.density
= 0x5;
uprintf("Can only write QIC-24\n");
sc
->sc_mode
.density
= 0x4;
* The tape density is set automatically when the tape
* is loaded. We only need to change it if we are writing.
if (minor(dev
) & TZ_HIDENSITY
)
sc
->sc_mode
.density
= 0x4;
if (minor(dev
) & TZ_HIDENSITY
)
uprintf("EXB-8200 density support only\n");
sc
->sc_mode
.vupb
= (u_char
)st_exvup
;
sc
->sc_mode
.motionthres
= (u_char
)st_exmotthr
;
sc
->sc_mode
.reconthres
= (u_char
)st_exreconthr
;
sc
->sc_mode
.gapthres
= (u_char
)st_exgapthr
;
if (minor(dev
) & TZ_HIDENSITY
)
uprintf("tz%d: Only one density supported\n", unit
);
break; /* XXX could do density select? */
/* set the current mode settings */
error
= tzcommand(dev
, SCSI_MODE_SELECT
, 0,
sc
->sc_modelen
, (caddr_t
)&sc
->sc_mode
);
sc
->sc_ctty
= tprintf_open(p
);
sc
->sc_flags
= TZF_ALIVE
| TZF_OPEN
;
register struct tz_softc
*sc
= &tz_softc
[tzunit(dev
)];
if (!(sc
->sc_flags
& TZF_OPEN
))
((flag
& FWRITE
) && (sc
->sc_flags
& TZF_WRITTEN
))) {
error
= tzcommand(dev
, SCSI_WRITE_EOF
, 0, 1, 0);
* Cartridge tapes don't do double EOFs on EOT.
error
= tzcommand(dev
, SCSI_WRITE_EOF
, 0, 1, 0);
if (minor(dev
) & TZ_NOREWIND
)
(void) tzcommand(dev
, SCSI_SPACE
, 0, -1, 0);
if ((minor(dev
) & TZ_NOREWIND
) == 0)
(void) tzcommand(dev
, SCSI_REWIND
, 0, 0, 0);
sc
->sc_flags
&= ~(TZF_OPEN
| TZF_WRITTEN
);
tprintf_close(sc
->sc_ctty
);
tzioctl(dev
, cmd
, data
, flag
)
register struct tz_softc
*sc
= &tz_softc
[tzunit(dev
)];
register struct buf
*bp
= &sc
->sc_buf
;
SCSI_WRITE_EOF
, SCSI_SPACE
, SCSI_SPACE
, SCSI_SPACE
, SCSI_SPACE
,
SCSI_REWIND
, SCSI_REWIND
, SCSI_TEST_UNIT_READY
case MTIOCTOP
: /* tape operation */
mtop
= (struct mtop
*)data
;
if ((unsigned)mtop
->mt_op
< MTREW
&& mtop
->mt_count
<= 0)
return (tzcommand(dev
, tzops
[mtop
->mt_op
], code
, count
, 0));
mtget
= (struct mtget
*)data
;
mtget
->mt_erreg
= sc
->sc_sense
.status
;
register int unit
= tzunit(bp
->b_dev
);
register struct tz_softc
*sc
= &tz_softc
[unit
];
if (sc
->sc_flags
& TZF_SEENEOF
) {
bp
->b_resid
= bp
->b_bcount
;
dp
->b_actb
= &bp
->b_actf
;