* Copyright (c) 1992 Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* %sccs.include.redist.c%
* @(#)tz.c 7.2 (Berkeley) %G%
* 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.
void tzstart(), tzdone();
struct driver tzdriver
= {
"tz", tzprobe
, tzstart
, tzdone
,
struct scsi_device
*sc_sd
; /* physical unit info */
int sc_flags
; /* see below */
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 */
#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 */
/* bits in minor device */
#define tzunit(x) (minor(x) >> 1) /* tz%d unit number */
#define TZ_NOREWIND 0x1 /* don't rewind on close */
#define INF (daddr_t)1000000L /* a block number that won't exist */
* Test to see if device is present.
* Return true if found and initialized ok.
register struct tz_softc
*sc
= &tz_softc
[sd
->sd_unit
];
printf("tzprobe()\n"); /* XXX */
/* init some parameters that don't change */
sc
->sc_cmd
.unit
= sd
->sd_unit
;
sc
->sc_rwcmd
.unitNumber
= sd
->sd_slave
;
/* sc->sc_rwcmd.highAddr = 1; /* count in blocks */
/* 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
.av_forw
= (struct buf
*)0;
sc
->sc_tab
.b_actf
= sc
->sc_tab
.b_actl
= &sc
->sc_buf
;
if (biowait(&sc
->sc_buf
) ||
(i
= sizeof(inqbuf
) - sc
->sc_buf
.b_resid
) < 5)
if (inqbuf
.type
!= SCSI_TAPE_TYPE
)
/* 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
.av_forw
= (struct buf
*)0;
sc
->sc_tab
.b_actf
= sc
->sc_tab
.b_actl
= &sc
->sc_buf
;
(void) biowait(&sc
->sc_buf
);
sc
->sc_flags
= TZF_ALIVE
;
printf("tz%d at %s%d drive %d slave %d\n", sd
->sd_unit
,
sd
->sd_cdriver
->d_name
, sd
->sd_ctlr
, sd
->sd_drive
,
/* doesn't exist or not a CCS device */
* Perform a special tape command on a SCSI Tape drive.
tzcommand(dev
, command
, code
, count
)
register struct tz_softc
*sc
= &tz_softc
[tzunit(dev
)];
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
);
scsiGroup0Cmd(command
, sc
->sc_sd
->sd_slave
,
(code
<< 16) | ((count
>> 8) & 0xFFFF), count
& 0xFF,
(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
.av_forw
= (struct buf
*)0;
sc
->sc_tab
.b_actf
= sc
->sc_tab
.b_actl
= &sc
->sc_buf
;
tzstart(sc
->sc_sd
->sd_unit
);
error
= biowait(&sc
->sc_buf
);
sc
->sc_flags
&= ~TZF_ALTCMD
; /* force use of sc_cdb */
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
)) {
sc
->sc_cmd
.flags
= !(bp
->b_flags
& B_READ
) ?
SCSICMD_DATA_TO_DEVICE
: 0;
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_cmd
.flags
= SCSICMD_DATA_TO_DEVICE
;
sc
->sc_rwcmd
.command
= SCSI_WRITE
;
sc
->sc_cmd
.cmd
= (u_char
*)&sc
->sc_rwcmd
;
sc
->sc_cmd
.cmdlen
= sizeof(sc
->sc_rwcmd
);
n
= howmany(bp
->b_bcount
, 512);
sc
->sc_rwcmd
.midAddr
= n
>> 16;
sc
->sc_rwcmd
.lowAddr
= n
>> 8;
sc
->sc_rwcmd
.blockCount
= n
;
if ((bp
->b_bcount
& (512 - 1)) != 0)
printf("tz%d: partial block xfer -- %x bytes\n",
printf("tzstart(%d) flags %x, addr %x sz %d\n", unit
,
sc
->sc_flags
, sc
->sc_cmd
.buf
, sc
->sc_cmd
.buflen
); /* XXX */
/* tell controller to start this command */
if (sc
->sc_cmd
.cmd
[0] == SCSI_READ
)
(*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("tzdone(%d, %d, %d, %x) %x flags %x\n", unit
, error
, resid
,
status
, sc
, sc
->sc_flags
); /* XXX */
printf("tz%d: bp == NULL\n", unit
);
if (sc
->sc_flags
& TZF_SENSEINPROGRESS
) {
sc
->sc_flags
&= ~TZF_SENSEINPROGRESS
;
sc
->sc_tab
.b_actf
= bp
= bp
->b_actf
; /* remove sc_errbuf */
panic("tzdone"); /* XXX */
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
;
scsiPrintSense((ScsiClass7Sense
*)sc
->sc_sense
.sense
,
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_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
.av_forw
= bp
;
sc
->sc_tab
.b_actf
= &sc
->sc_errbuf
;
sc
->sc_sense
.status
= status
;
sc
->sc_tab
.b_actf
= bp
->b_actf
;
if (sc
->sc_flags
& TZF_WAIT
) {
sc
->sc_flags
&= ~TZF_WAIT
;
register int unit
= tzunit(dev
);
register struct tz_softc
*sc
= &tz_softc
[unit
];
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);
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
)
if ((flag
&FWRITE
) && (sc
->sc_dsreg
&HTDS_WRL
)) {
uprintf("tz%d: no write ring\n", unit
);
sc
->sc_ctty
= (caddr_t
)(u
.u_procp
->p_flag
& SCTTY
?
u
.u_procp
->p_session
->s_ttyp
: 0);
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
))) {
(void) tzcommand(dev
, SCSI_WRITE_EOF
, 0, 1);
if ((minor(dev
) & TZ_NOREWIND
) == 0)
(void) tzcommand(dev
, SCSI_REWIND
, 0, 0);
sc
->sc_flags
&= ~(TZF_OPEN
| TZF_WRITTEN
);
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
));
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
];
dp
->b_actl
->av_forw
= bp
;