mirror of
https://github.com/phabrics/Run-Sun3-SunOS-4.1.1.git
synced 2026-04-29 11:02:59 -04:00
1357 lines
38 KiB
C
1357 lines
38 KiB
C
/* $Id: scsi-tape.c,v 1.8 2007/09/06 23:19:06 fredette Exp $ */
|
|
|
|
/* scsi/scsi-tape.c - implementation of SCSI tape emulation: */
|
|
|
|
/*
|
|
* Copyright (c) 2003 Matt Fredette
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 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 Matt Fredette.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <tme/common.h>
|
|
_TME_RCSID("$Id: scsi-tape.c,v 1.8 2007/09/06 23:19:06 fredette Exp $");
|
|
|
|
/* includes: */
|
|
#include <tme/scsi/scsi-tape.h>
|
|
#ifdef HAVE_STDARG_H
|
|
#include <stdarg.h>
|
|
#else /* HAVE_STDARG_H */
|
|
#include <varargs.h>
|
|
#endif /* HAVE_STDARG_H */
|
|
|
|
/* macros: */
|
|
|
|
/* globals: */
|
|
|
|
/* the list of tapes that we emulate: */
|
|
const struct {
|
|
|
|
/* the type name: */
|
|
const char *_tme_scsi_tape_list_type;
|
|
|
|
/* the initialization function: */
|
|
int (*_tme_scsi_tape_list_init) _TME_P((struct tme_scsi_tape *));
|
|
} _tme_scsi_tape_list[] = {
|
|
|
|
/* the generic TME SCSI-1 tape: */
|
|
{ "tme-scsi-1", tme_scsi_tape_tme_init },
|
|
|
|
/* the Emulex MT02 emulation: */
|
|
{ "emulex-mt02", tme_scsi_tape_emulexmt02_init },
|
|
};
|
|
|
|
/* this is the LUN addresser for LUN-aware tape devices: */
|
|
int
|
|
tme_scsi_tape_address_lun_aware(struct tme_scsi_device *scsi_device)
|
|
{
|
|
struct tme_scsi_tape *scsi_tape;
|
|
struct tme_scsi_tape_connection *conn_scsi_tape;
|
|
struct tme_scsi_device_sense *sense;
|
|
int lun;
|
|
|
|
/* recover our data structure: */
|
|
scsi_tape = (struct tme_scsi_tape *) scsi_device;
|
|
|
|
/* if an IDENTIFY message was sent, use that LUN: */
|
|
lun = scsi_device->tme_scsi_device_addressed_lun;
|
|
|
|
/* otherwise, get the LUN from bits 5-7 of the second
|
|
CDB byte: */
|
|
if (lun < 0) {
|
|
lun = (scsi_device->tme_scsi_device_cdb[1] >> 5);
|
|
scsi_device->tme_scsi_device_addressed_lun = lun;
|
|
}
|
|
|
|
/* get this LUN's tape connection: */
|
|
conn_scsi_tape = scsi_tape->tme_scsi_tape_connections[lun];
|
|
|
|
/* if this LUN is not defined, and this isn't a REQUEST SENSE
|
|
command: */
|
|
if (!(scsi_device->tme_scsi_device_luns
|
|
& TME_BIT(lun))
|
|
&& (scsi_device->tme_scsi_device_cdb[0]
|
|
!= TME_SCSI_CDB_REQUEST_SENSE)) {
|
|
|
|
/* form the ILLEGAL REQUEST sense: */
|
|
sense = &scsi_device->tme_scsi_device_sense[lun];
|
|
sense->tme_scsi_device_sense_data[2] = 0x05;
|
|
}
|
|
|
|
/* otherwise, this LUN is defined. an INQUIRY or REQUEST SENSE
|
|
command is always allowed for a defined LUN: */
|
|
else if ((scsi_device->tme_scsi_device_cdb[0]
|
|
== TME_SCSI_CDB_INQUIRY)
|
|
|| (scsi_device->tme_scsi_device_cdb[0]
|
|
== TME_SCSI_CDB_REQUEST_SENSE)) {
|
|
sense = NULL;
|
|
}
|
|
|
|
/* otherwise, if the tape at this LUN has an attention condition: */
|
|
else if (conn_scsi_tape->tme_scsi_tape_connection_flags
|
|
& TME_SCSI_TAPE_FLAG_ATTENTION) {
|
|
|
|
/* clear the attention condition: */
|
|
conn_scsi_tape->tme_scsi_tape_connection_flags
|
|
&= ~TME_SCSI_TAPE_FLAG_ATTENTION;
|
|
|
|
/* form the UNIT ATTENTION sense: */
|
|
sense = &scsi_device->tme_scsi_device_sense[lun];
|
|
sense->tme_scsi_device_sense_data[2] = 0x06;
|
|
}
|
|
|
|
/* otherwise, if the tape at this LUN is not loaded: */
|
|
else if (!(conn_scsi_tape->tme_scsi_tape_connection_flags
|
|
& TME_SCSI_TAPE_FLAG_LOADED)) {
|
|
|
|
/* form the NOT READY sense: */
|
|
sense = &scsi_device->tme_scsi_device_sense[lun];
|
|
sense->tme_scsi_device_sense_data[2] = 0x02;
|
|
}
|
|
|
|
/* otherwise, this command is okay: */
|
|
else {
|
|
sense = NULL;
|
|
}
|
|
|
|
/* if addressing this LUN caused some sense: */
|
|
if (sense != NULL) {
|
|
|
|
/* this target must support extended sense: */
|
|
assert (!scsi_device->tme_scsi_device_sense_no_extended);
|
|
|
|
/* the error class and error code: */
|
|
sense->tme_scsi_device_sense_data[0]
|
|
= 0x70;
|
|
|
|
/* the additional sense length: */
|
|
sense->tme_scsi_device_sense_data[7]
|
|
= 0x00;
|
|
|
|
sense->tme_scsi_device_sense_valid
|
|
= TRUE;
|
|
|
|
/* return the CHECK CONDITION status: */
|
|
tme_scsi_device_target_do_smf(scsi_device,
|
|
TME_SCSI_STATUS_CHECK_CONDITION,
|
|
TME_SCSI_MSG_CMD_COMPLETE);
|
|
return (EINVAL);
|
|
}
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this is the LUN addresser for LUN-unaware devices: */
|
|
int
|
|
tme_scsi_tape_address_lun_unaware(struct tme_scsi_device *scsi_device)
|
|
{
|
|
|
|
/* we always force a LUN of zero: */
|
|
scsi_device->tme_scsi_device_addressed_lun = 0;
|
|
|
|
return (tme_scsi_tape_address_lun_aware(scsi_device));
|
|
}
|
|
|
|
/* this determines the status of a tape READ or WRITE command: */
|
|
tme_uint8_t
|
|
tme_scsi_tape_xfer_status(struct tme_scsi_tape *scsi_tape,
|
|
int flags,
|
|
unsigned long count_xfer_got)
|
|
{
|
|
int lun;
|
|
tme_uint8_t *cdb;
|
|
tme_uint8_t status;
|
|
unsigned long count_xfer_wanted;
|
|
struct tme_scsi_device_sense *sense;
|
|
|
|
/* assume that this command completed successfully: */
|
|
status = TME_SCSI_STATUS_GOOD;
|
|
|
|
/* if there are some tape check condition flags: */
|
|
if (flags & ~TME_TAPE_FLAG_FIXED) {
|
|
|
|
/* there is a condition to check: */
|
|
status = TME_SCSI_STATUS_CHECK_CONDITION;
|
|
|
|
/* get the addressed LUN: */
|
|
lun = scsi_tape->tme_scsi_tape_device.tme_scsi_device_addressed_lun;
|
|
|
|
/* get the original transfer length: */
|
|
cdb = &scsi_tape->tme_scsi_tape_device.tme_scsi_device_cdb[0];
|
|
count_xfer_wanted = cdb[2];
|
|
count_xfer_wanted = (count_xfer_wanted << 8) | cdb[3];
|
|
count_xfer_wanted = (count_xfer_wanted << 8) | cdb[4];
|
|
|
|
/* set the sense: */
|
|
sense
|
|
= &scsi_tape->tme_scsi_tape_device.tme_scsi_device_sense[lun];
|
|
|
|
/* the Valid, Error Class, and Error Code values: */
|
|
sense->tme_scsi_device_sense_data[0]
|
|
= (0x80 | 0x70);
|
|
|
|
/* the Filemark, EOM, ILI, and Sense Key (NO SENSE) values: */
|
|
sense->tme_scsi_device_sense_data[2]
|
|
= (((flags & TME_TAPE_FLAG_MARK)
|
|
? 0x80
|
|
: 0x00)
|
|
| ((flags & TME_TAPE_FLAG_EOM)
|
|
? 0x40
|
|
: 0x00)
|
|
| ((flags & TME_TAPE_FLAG_ILI)
|
|
? 0x20
|
|
: 0x00)
|
|
| 0x00);
|
|
|
|
/* set the Information Bytes (for a tape, the residue): */
|
|
count_xfer_wanted -= count_xfer_got;
|
|
sense->tme_scsi_device_sense_data[3]
|
|
= (count_xfer_wanted >> 24) & 0xff;
|
|
sense->tme_scsi_device_sense_data[4]
|
|
= (count_xfer_wanted >> 16) & 0xff;
|
|
sense->tme_scsi_device_sense_data[5]
|
|
= (count_xfer_wanted >> 8) & 0xff;
|
|
sense->tme_scsi_device_sense_data[6]
|
|
= (count_xfer_wanted >> 0) & 0xff;
|
|
|
|
/* there are no additional bytes: */
|
|
sense->tme_scsi_device_sense_data[7]
|
|
= 0x00;
|
|
|
|
/* this sense is valid: */
|
|
sense->tme_scsi_device_sense_valid
|
|
= TRUE;
|
|
}
|
|
|
|
/* done: */
|
|
return (status);
|
|
}
|
|
|
|
/* this finishes a WRITE command: */
|
|
_TME_SCSI_DEVICE_PHASE_DECL(tme_scsi_tape_target_do_write)
|
|
{
|
|
struct tme_scsi_tape *scsi_tape;
|
|
struct tme_scsi_tape_connection *conn_scsi_tape;
|
|
struct tme_tape_connection *conn_tape;
|
|
int lun;
|
|
unsigned long count;
|
|
int flags;
|
|
tme_uint8_t status;
|
|
int rc;
|
|
|
|
/* recover our tape: */
|
|
scsi_tape = (struct tme_scsi_tape *) scsi_device;
|
|
|
|
/* get the addressed LUN: */
|
|
lun = scsi_device->tme_scsi_device_addressed_lun;
|
|
|
|
/* get the tape connection: */
|
|
conn_scsi_tape
|
|
= scsi_tape->tme_scsi_tape_connections[lun];
|
|
conn_tape
|
|
= ((struct tme_tape_connection *)
|
|
conn_scsi_tape->tme_scsi_tape_connection.tme_tape_connection.tme_connection_other);
|
|
|
|
/* release the buffer: */
|
|
rc
|
|
= ((*conn_tape->tme_tape_connection_release)
|
|
(conn_tape,
|
|
&flags,
|
|
&count));
|
|
assert (rc == TME_OK);
|
|
|
|
/* get the status: */
|
|
status
|
|
= ((*scsi_tape->tme_scsi_tape_xfer_status)
|
|
(scsi_tape,
|
|
flags,
|
|
count));;
|
|
|
|
/* finish the command: */
|
|
tme_scsi_device_target_do_smf(scsi_device,
|
|
status,
|
|
TME_SCSI_MSG_CMD_COMPLETE);
|
|
}
|
|
|
|
/* this implements the tape Group 0 READ and WRITE commands: */
|
|
void
|
|
tme_scsi_tape_cdb_xfer0(struct tme_scsi_device *scsi_device,
|
|
int read)
|
|
{
|
|
struct tme_scsi_tape *scsi_tape;
|
|
struct tme_scsi_tape_connection *conn_scsi_tape;
|
|
struct tme_tape_connection *conn_tape;
|
|
int lun;
|
|
tme_uint8_t *cdb;
|
|
unsigned long count_xfer;
|
|
unsigned int bytes_xfer;
|
|
int flags;
|
|
tme_uint8_t status;
|
|
int rc;
|
|
|
|
/* recover our tape: */
|
|
scsi_tape = (struct tme_scsi_tape *) scsi_device;
|
|
|
|
/* get the addressed LUN: */
|
|
lun = scsi_device->tme_scsi_device_addressed_lun;
|
|
|
|
/* get the tape connection: */
|
|
conn_scsi_tape
|
|
= scsi_tape->tme_scsi_tape_connections[lun];
|
|
conn_tape
|
|
= ((struct tme_tape_connection *)
|
|
conn_scsi_tape->tme_scsi_tape_connection.tme_tape_connection.tme_connection_other);
|
|
|
|
cdb = &scsi_device->tme_scsi_device_cdb[0];
|
|
|
|
/* get the fixed bit: */
|
|
flags = (cdb[1] & 0x01) * TME_TAPE_FLAG_FIXED;
|
|
|
|
/* get the transfer length: */
|
|
count_xfer = cdb[2];
|
|
count_xfer = (count_xfer << 8) | cdb[3];
|
|
count_xfer = (count_xfer << 8) | cdb[4];
|
|
bytes_xfer = count_xfer;
|
|
if (flags & TME_TAPE_FLAG_FIXED) {
|
|
bytes_xfer *= scsi_tape->tme_scsi_tape_block_size_current;
|
|
}
|
|
|
|
/* if this is a read: */
|
|
if (read) {
|
|
|
|
/* get the tape buffer: */
|
|
rc
|
|
= ((*conn_tape->tme_tape_connection_read)
|
|
(conn_tape,
|
|
&flags,
|
|
&count_xfer,
|
|
&scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid,
|
|
&scsi_device->tme_scsi_device_dma.tme_scsi_dma_out));
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_in = NULL;
|
|
|
|
/* XXX FIXME - this is a big hack. the tme_tape_connection_read
|
|
function always reads one or more whole tape blocks, but we
|
|
call it for every READ CDB, even when the initiator hasn't set
|
|
a block size with MODE SELECT (which allows the initiator to
|
|
issue reads without regard to block size - reads smaller than
|
|
the tape block size don't discard the remainder of a block and
|
|
advance to the next tape block, but instead just return
|
|
successive parts of the same tape block).
|
|
|
|
this entire function needs to be rewritten, to handle this mode
|
|
by only issuing tme_tape_connection_read calls when the block
|
|
buffer has been exhausted, and also honor the SILI bit. as it
|
|
is, this code definitely has no chance of working with a real
|
|
tape drive.
|
|
|
|
but to fix the immediate problem, if the read has transferred
|
|
some bytes with an underrun, we just pad the read out with
|
|
zeroes. this fixes NetBSD PR pkg/34536: */
|
|
if (scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid > 0
|
|
&& scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid < bytes_xfer
|
|
&& (flags
|
|
& ~(TME_TAPE_FLAG_ILI
|
|
| TME_TAPE_FLAG_MARK)) == 0) {
|
|
/* XXX this breaks const: */
|
|
/* XXX writing into the tape buffer breaks the tape abstraction,
|
|
and only works because we know that posix-tape.c has
|
|
allocated this buffer to be at least as big as the read we
|
|
requested: */
|
|
memset(((tme_uint8_t *)
|
|
(scsi_device->tme_scsi_device_dma.tme_scsi_dma_out
|
|
+ scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid)),
|
|
0,
|
|
(bytes_xfer
|
|
- scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid));
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid = bytes_xfer;
|
|
flags &= ~TME_TAPE_FLAG_ILI;
|
|
}
|
|
|
|
/* get the status: */
|
|
status
|
|
= ((*scsi_tape->tme_scsi_tape_xfer_status)
|
|
(scsi_tape,
|
|
flags,
|
|
count_xfer));
|
|
|
|
/* finish the command: */
|
|
tme_scsi_device_target_do_dsmf(scsi_device,
|
|
status,
|
|
TME_SCSI_MSG_CMD_COMPLETE);
|
|
}
|
|
else {
|
|
|
|
/* get the tape buffer: */
|
|
rc
|
|
= ((*conn_tape->tme_tape_connection_write)
|
|
(conn_tape,
|
|
flags,
|
|
count_xfer,
|
|
&scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid,
|
|
&scsi_device->tme_scsi_device_dma.tme_scsi_dma_in));
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_out = NULL;
|
|
|
|
/* enter the DATA OUT phase to transfer all of the data to be
|
|
written: */
|
|
/* XXX when in fixed-block mode, we should go a block at a time,
|
|
so we can report errors as soon as they happen: */
|
|
tme_scsi_device_target_phase(scsi_device,
|
|
TME_SCSI_SIGNAL_BSY
|
|
| TME_SCSI_PHASE_DATA_OUT);
|
|
|
|
/* when the DATA OUT phase is done, we'll write the data end
|
|
enter the STATUS phase: */
|
|
scsi_device->tme_scsi_device_phase
|
|
= tme_scsi_tape_target_do_write;
|
|
}
|
|
|
|
/* if we couldn't get the tape buffer: */
|
|
if (rc != TME_OK) {
|
|
|
|
/* XXX we should return MEDIUM ERROR or HARDWARE ERROR sense here: */
|
|
abort();
|
|
}
|
|
}
|
|
|
|
/* this implements the tape REWIND command: */
|
|
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_rewind)
|
|
{
|
|
struct tme_scsi_tape *scsi_tape;
|
|
struct tme_scsi_tape_connection *conn_scsi_tape;
|
|
struct tme_tape_connection *conn_tape;
|
|
int lun;
|
|
int rc;
|
|
|
|
/* recover our tape: */
|
|
scsi_tape = (struct tme_scsi_tape *) scsi_device;
|
|
|
|
/* get the addressed LUN: */
|
|
lun = scsi_device->tme_scsi_device_addressed_lun;
|
|
|
|
/* get the tape connection: */
|
|
conn_scsi_tape
|
|
= scsi_tape->tme_scsi_tape_connections[lun];
|
|
conn_tape
|
|
= ((struct tme_tape_connection *)
|
|
conn_scsi_tape->tme_scsi_tape_connection.tme_tape_connection.tme_connection_other);
|
|
|
|
/* call out a REWIND control: */
|
|
rc =
|
|
((*conn_tape->tme_tape_connection_control)
|
|
(conn_tape,
|
|
TME_TAPE_CONTROL_REWIND));
|
|
assert (rc == TME_OK);
|
|
|
|
/* finish the command: */
|
|
tme_scsi_device_target_do_smf(scsi_device,
|
|
TME_SCSI_STATUS_GOOD,
|
|
TME_SCSI_MSG_CMD_COMPLETE);
|
|
}
|
|
|
|
/* this implements the tape READ BLOCK LIMITS command: */
|
|
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_block_limits)
|
|
{
|
|
struct tme_scsi_tape *scsi_tape;
|
|
tme_uint8_t *data;
|
|
int lun;
|
|
tme_uint32_t block_size;
|
|
|
|
/* recover our tape: */
|
|
scsi_tape = (struct tme_scsi_tape *) scsi_device;
|
|
|
|
/* get the addressed LUN: */
|
|
lun = scsi_device->tme_scsi_device_addressed_lun;
|
|
|
|
data = &scsi_device->tme_scsi_device_data[0];
|
|
|
|
/* a reserved byte: */
|
|
data++;
|
|
|
|
/* the Maximum Block Length: */
|
|
block_size = scsi_tape->tme_scsi_tape_block_size_max;
|
|
*(data++) = (block_size >> 16) & 0xff;
|
|
*(data++) = (block_size >> 8) & 0xff;
|
|
*(data++) = (block_size >> 0) & 0xff;
|
|
|
|
/* the Minimum Block Length: */
|
|
block_size = scsi_tape->tme_scsi_tape_block_size_min;
|
|
assert (block_size > 0);
|
|
*(data++) = (block_size >> 8) & 0xff;
|
|
*(data++) = (block_size >> 0) & 0xff;
|
|
|
|
/* set the DMA pointer and length: */
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
|
|
= (data
|
|
- &scsi_device->tme_scsi_device_data[0]);
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_out
|
|
= &scsi_device->tme_scsi_device_data[0];
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_in
|
|
= NULL;
|
|
|
|
/* finish the command: */
|
|
tme_scsi_device_target_do_dsmf(scsi_device,
|
|
TME_SCSI_STATUS_GOOD,
|
|
TME_SCSI_MSG_CMD_COMPLETE);
|
|
}
|
|
|
|
/* this implements the tape Group 0 READ command: */
|
|
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_read0)
|
|
{
|
|
tme_scsi_tape_cdb_xfer0(scsi_device, TRUE);
|
|
}
|
|
|
|
/* this implements the tape Group 0 WRITE command: */
|
|
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_write0)
|
|
{
|
|
tme_scsi_tape_cdb_xfer0(scsi_device, FALSE);
|
|
}
|
|
|
|
/* this implements the tape INQUIRY command: */
|
|
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_inquiry)
|
|
{
|
|
int lun;
|
|
struct tme_scsi_device_inquiry inquiry;
|
|
tme_uint8_t *data;
|
|
|
|
/* get the active LUN: */
|
|
lun = scsi_device->tme_scsi_device_addressed_lun;
|
|
|
|
/* this is a sequential-access device: */
|
|
inquiry.tme_scsi_device_inquiry_type = TME_SCSI_TYPE_TAPE;
|
|
|
|
/* if this LUN is defined: */
|
|
inquiry.tme_scsi_device_inquiry_lun_state
|
|
= ((scsi_device->tme_scsi_device_luns
|
|
& TME_BIT(lun))
|
|
? TME_SCSI_LUN_PRESENT
|
|
: TME_SCSI_LUN_UNSUPPORTED);
|
|
|
|
/* the device type qualifier: */
|
|
inquiry.tme_scsi_device_inquiry_type_qualifier = 0x00;
|
|
|
|
/* nonzero iff the LUN is removable: */
|
|
inquiry.tme_scsi_device_inquiry_lun_removable = TRUE;
|
|
|
|
/* the various standards versions: */
|
|
inquiry.tme_scsi_device_inquiry_std_ansi = 1;
|
|
inquiry.tme_scsi_device_inquiry_std_ecma = 1;
|
|
inquiry.tme_scsi_device_inquiry_std_iso = 1;
|
|
|
|
/* the response format: */
|
|
inquiry.tme_scsi_device_response_format = TME_SCSI_FORMAT_CCS;
|
|
|
|
/* make the inquiry data: */
|
|
data
|
|
= tme_scsi_device_make_inquiry_data(scsi_device,
|
|
&inquiry);
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
|
|
= TME_MIN((data
|
|
- scsi_device->tme_scsi_device_dma.tme_scsi_dma_out),
|
|
scsi_device->tme_scsi_device_cdb[4]);
|
|
|
|
/* finish the command: */
|
|
tme_scsi_device_target_do_dsmf(scsi_device,
|
|
TME_SCSI_STATUS_GOOD,
|
|
TME_SCSI_MSG_CMD_COMPLETE);
|
|
}
|
|
|
|
/* this implements the tape WRITE MARKS command: */
|
|
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_write_marks)
|
|
{
|
|
abort();
|
|
}
|
|
|
|
/* this implements the tape SPACE command: */
|
|
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_space)
|
|
{
|
|
struct tme_scsi_tape *scsi_tape;
|
|
struct tme_scsi_tape_connection *conn_scsi_tape;
|
|
struct tme_tape_connection *conn_tape;
|
|
int lun;
|
|
tme_uint8_t *cdb;
|
|
tme_int32_t count;
|
|
int rc;
|
|
|
|
/* recover our tape: */
|
|
scsi_tape = (struct tme_scsi_tape *) scsi_device;
|
|
|
|
/* get the addressed LUN: */
|
|
lun = scsi_device->tme_scsi_device_addressed_lun;
|
|
|
|
/* get the tape connection: */
|
|
conn_scsi_tape
|
|
= scsi_tape->tme_scsi_tape_connections[lun];
|
|
conn_tape
|
|
= ((struct tme_tape_connection *)
|
|
conn_scsi_tape->tme_scsi_tape_connection.tme_tape_connection.tme_connection_other);
|
|
|
|
cdb = &scsi_device->tme_scsi_device_cdb[0];
|
|
|
|
/* get the signed count: */
|
|
count = ((tme_int8_t *) cdb)[2];
|
|
count = (count << 8) | cdb[3];
|
|
count = (count << 8) | cdb[4];
|
|
|
|
/* dispatch on the SPACE code: */
|
|
switch (cdb[1] & 0x03) {
|
|
|
|
/* blocks: */
|
|
case 0x00:
|
|
abort();
|
|
|
|
/* filemarks: */
|
|
case 0x01:
|
|
|
|
/* call out a MARK_SKIPF or MARK_SKIPR control: */
|
|
rc =
|
|
(count < 0
|
|
? ((*conn_tape->tme_tape_connection_control)
|
|
(conn_tape,
|
|
TME_TAPE_CONTROL_MARK_SKIPR,
|
|
(unsigned int) (-count)))
|
|
: ((*conn_tape->tme_tape_connection_control)
|
|
(conn_tape,
|
|
TME_TAPE_CONTROL_MARK_SKIPF,
|
|
(unsigned int) count)));
|
|
assert (rc == TME_OK);
|
|
break;
|
|
|
|
/* sequential filemarks: */
|
|
case 0x02:
|
|
abort();
|
|
|
|
/* physical end-of-data: */
|
|
case 0x03:
|
|
abort();
|
|
}
|
|
|
|
/* finish the command: */
|
|
tme_scsi_device_target_do_smf(scsi_device,
|
|
TME_SCSI_STATUS_GOOD,
|
|
TME_SCSI_MSG_CMD_COMPLETE);
|
|
}
|
|
|
|
/* this processes the parameter list from a tape MODE SELECT command: */
|
|
_TME_SCSI_DEVICE_PHASE_DECL(_tme_scsi_tape_mode_select_data)
|
|
{
|
|
struct tme_scsi_tape *scsi_tape;
|
|
int lun;
|
|
struct tme_scsi_tape_connection *conn_scsi_tape;
|
|
struct tme_tape_connection *conn_tape;
|
|
const tme_uint8_t *data;
|
|
const tme_uint8_t *data_end;
|
|
tme_uint8_t status;
|
|
unsigned int block_descriptors;
|
|
tme_uint32_t blocks;
|
|
tme_uint32_t block_size;
|
|
tme_uint32_t length;
|
|
unsigned long sizes[3];
|
|
int rc;
|
|
|
|
/* recover our tape: */
|
|
scsi_tape = (struct tme_scsi_tape *) scsi_device;
|
|
|
|
/* get the addressed LUN: */
|
|
lun = scsi_device->tme_scsi_device_addressed_lun;
|
|
|
|
/* get a pointer to the first byte of data, and a pointer past the
|
|
last byte of data: */
|
|
data = &scsi_device->tme_scsi_device_data[0];
|
|
length = scsi_device->tme_scsi_device_cdb[4];
|
|
data_end = (data
|
|
+ TME_MIN(sizeof(scsi_device->tme_scsi_device_data),
|
|
length));
|
|
|
|
/* assume that this command will succeed: */
|
|
status = TME_SCSI_STATUS_GOOD;
|
|
|
|
/* skip the two reserved bytes: */
|
|
data += (data < data_end);
|
|
data += (data < data_end);
|
|
|
|
/* we ignore the buffered mode and speed byte: */
|
|
data += (data < data_end);
|
|
|
|
/* get the count of bytes in block descriptors: */
|
|
block_descriptors
|
|
= (data < data_end
|
|
? *(data++)
|
|
: 0);
|
|
|
|
/* check the block descriptors: */
|
|
block_size = 0;
|
|
for (;
|
|
block_descriptors >= 8;
|
|
block_descriptors -= 8) {
|
|
|
|
/* if this block descriptor is short: */
|
|
if ((data_end - data) < 8) {
|
|
|
|
/* XXX FIXME - we need to assemble a sense and return a CHECK
|
|
CONDITION here: */
|
|
abort();
|
|
}
|
|
|
|
/* we ignore the density code: */
|
|
data++;
|
|
|
|
/* get the block count: */
|
|
blocks = *(data++);
|
|
blocks = (blocks << 8) + *(data++);
|
|
blocks = (blocks << 8) + *(data++);
|
|
|
|
/* skip the reserved byte: */
|
|
data++;
|
|
|
|
/* get the block length: */
|
|
block_size = *(data++);
|
|
block_size = (block_size << 8) + *(data++);
|
|
block_size = (block_size << 8) + *(data++);
|
|
|
|
/* if this block descriptor doesn't describe the entire tape: */
|
|
if (blocks != 0) {
|
|
|
|
/* XXX FIXME - we need to assemble a sense and return a CHECK
|
|
CONDITION here: */
|
|
abort();
|
|
}
|
|
|
|
/* set the new current block size: */
|
|
scsi_tape->tme_scsi_tape_block_size_current = block_size;
|
|
}
|
|
|
|
/* if the parameter list was good: */
|
|
if (status == TME_SCSI_STATUS_GOOD) {
|
|
|
|
/* get the tape connection: */
|
|
conn_scsi_tape
|
|
= scsi_tape->tme_scsi_tape_connections[lun];
|
|
conn_tape
|
|
= ((struct tme_tape_connection *)
|
|
conn_scsi_tape->tme_scsi_tape_connection.tme_tape_connection.tme_connection_other);
|
|
|
|
/* set the block size: */
|
|
if (block_size != 0) {
|
|
sizes[0] = block_size;
|
|
sizes[1] = block_size;
|
|
sizes[2] = block_size;
|
|
}
|
|
else {
|
|
sizes[0] = scsi_tape->tme_scsi_tape_block_size_min;
|
|
sizes[1] = scsi_tape->tme_scsi_tape_block_size_max;
|
|
sizes[2] = 0;
|
|
}
|
|
rc
|
|
= ((*conn_tape->tme_tape_connection_control)
|
|
(conn_tape,
|
|
TME_TAPE_CONTROL_BLOCK_SIZE_SET,
|
|
sizes));
|
|
assert (rc == TME_OK);
|
|
}
|
|
|
|
tme_scsi_device_target_do_smf(scsi_device,
|
|
status,
|
|
TME_SCSI_MSG_CMD_COMPLETE);
|
|
}
|
|
|
|
/* this implements the tape MODE SELECT command: */
|
|
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_mode_select)
|
|
{
|
|
|
|
/* read in the parameter list: */
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
|
|
= scsi_device->tme_scsi_device_cdb[4];
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
|
|
= TME_MIN(scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid,
|
|
sizeof(scsi_device->tme_scsi_device_data));
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_in
|
|
= &scsi_device->tme_scsi_device_data[0];
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_out
|
|
= NULL;
|
|
|
|
/* transfer the parameter list: */
|
|
tme_scsi_device_target_phase(scsi_device,
|
|
(TME_SCSI_SIGNAL_BSY
|
|
| TME_SCSI_PHASE_DATA_OUT));
|
|
scsi_device->tme_scsi_device_phase
|
|
= _tme_scsi_tape_mode_select_data;
|
|
}
|
|
|
|
/* this implements the tape MODE SENSE command: */
|
|
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_mode_sense)
|
|
{
|
|
struct tme_scsi_tape *scsi_tape;
|
|
tme_uint8_t *data;
|
|
tme_uint32_t blocks, block_size;
|
|
int lun;
|
|
|
|
/* recover our tape: */
|
|
scsi_tape = (struct tme_scsi_tape *) scsi_device;
|
|
|
|
/* get the addressed LUN: */
|
|
lun = scsi_device->tme_scsi_device_addressed_lun;
|
|
|
|
/* get the current block size: */
|
|
block_size = scsi_tape->tme_scsi_tape_block_size_current;
|
|
|
|
data = &scsi_device->tme_scsi_device_data[0];
|
|
|
|
/* the sense data length. we will fill this in later: */
|
|
data++;
|
|
|
|
/* byte 1 is the medium type: */
|
|
*(data++) = 0x00; /* default (only one medium type supported) */
|
|
|
|
/* byte 2 is the WP (Write Protect), Buffered Mode, and Speed: */
|
|
*(data++) = 0x80; /* write protected, unbuffered, default speed */
|
|
|
|
/* byte 3 is the Block Descriptor Length. we will fill this in
|
|
later: */
|
|
data++;
|
|
|
|
/* the first Block Descriptor: */
|
|
|
|
/* the Block Descriptor density code: */
|
|
*(data++) = 0x05; /* QIC-24 */
|
|
|
|
/* the Number of Blocks: */
|
|
/* XXX FIXME - we assume a 60MB tape: */
|
|
blocks = (60 * 1024 * 1024) / block_size;
|
|
*(data++) = (blocks >> 16) & 0xff;
|
|
*(data++) = (blocks >> 8) & 0xff;
|
|
*(data++) = (blocks >> 0) & 0xff;
|
|
|
|
/* a reserved byte: */
|
|
data++;
|
|
|
|
/* the Block Length: */
|
|
*(data++) = (block_size >> 16) & 0xff;
|
|
*(data++) = (block_size >> 8) & 0xff;
|
|
*(data++) = (block_size >> 0) & 0xff;
|
|
|
|
/* fill in the Block Descriptor Length: */
|
|
scsi_device->tme_scsi_device_data[3]
|
|
= (data - &scsi_device->tme_scsi_device_data[4]);
|
|
|
|
/* there are no vendor-unique bytes or mode pages: */
|
|
|
|
/* fill in the sense data length: */
|
|
scsi_device->tme_scsi_device_data[0]
|
|
= (data - &scsi_device->tme_scsi_device_data[1]);
|
|
|
|
/* set the DMA pointer and length: */
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
|
|
= TME_MIN((data
|
|
- &scsi_device->tme_scsi_device_data[0]),
|
|
scsi_device->tme_scsi_device_cdb[4]);
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_out
|
|
= &scsi_device->tme_scsi_device_data[0];
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_in
|
|
= NULL;
|
|
|
|
/* finish the command: */
|
|
tme_scsi_device_target_do_dsmf(scsi_device,
|
|
TME_SCSI_STATUS_GOOD,
|
|
TME_SCSI_MSG_CMD_COMPLETE);
|
|
}
|
|
|
|
/* this implements the tape LOAD/UNLOAD command: */
|
|
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_load_unload)
|
|
{
|
|
/* XXX TBD */
|
|
|
|
/* finish the command: */
|
|
tme_scsi_device_target_do_smf(scsi_device,
|
|
TME_SCSI_STATUS_GOOD,
|
|
TME_SCSI_MSG_CMD_COMPLETE);
|
|
}
|
|
|
|
/* this implements the tape PREVENT/ALLOW command: */
|
|
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_prevent_allow)
|
|
{
|
|
/* XXX TBD */
|
|
|
|
/* finish the command: */
|
|
tme_scsi_device_target_do_smf(scsi_device,
|
|
TME_SCSI_STATUS_GOOD,
|
|
TME_SCSI_MSG_CMD_COMPLETE);
|
|
}
|
|
|
|
/* the tape control handler: */
|
|
#ifdef HAVE_STDARG_H
|
|
static int _tme_scsi_tape_control(struct tme_tape_connection *conn_tape,
|
|
unsigned int control,
|
|
...)
|
|
#else /* HAVE_STDARG_H */
|
|
static int _tme_scsi_tape_control(conn_tape, control, va_alist)
|
|
struct tme_tape_connection *conn_tape;
|
|
unsigned int control;
|
|
va_dcl
|
|
#endif /* HAVE_STDARG_H */
|
|
{
|
|
struct tme_scsi_tape *scsi_tape;
|
|
struct tme_scsi_tape_connection *conn_scsi_tape;
|
|
va_list control_args;
|
|
|
|
/* recover our data structures: */
|
|
conn_scsi_tape = (struct tme_scsi_tape_connection *) conn_tape;
|
|
scsi_tape = (struct tme_scsi_tape *) conn_tape->tme_tape_connection.tme_connection_element->tme_element_private;
|
|
|
|
/* lock the mutex: */
|
|
tme_mutex_lock(&scsi_tape->tme_scsi_tape_mutex);
|
|
|
|
/* start the variable arguments: */
|
|
#ifdef HAVE_STDARG_H
|
|
va_start(control_args, control);
|
|
#else /* HAVE_STDARG_H */
|
|
va_start(control_args);
|
|
#endif /* HAVE_STDARG_H */
|
|
|
|
/* dispatch on the sequence type: */
|
|
switch (control) {
|
|
|
|
case TME_TAPE_CONTROL_LOAD:
|
|
/* a tape has been loaded: */
|
|
conn_scsi_tape->tme_scsi_tape_connection_flags
|
|
= (conn_scsi_tape->tme_scsi_tape_connection_flags
|
|
| (TME_SCSI_TAPE_FLAG_LOADED
|
|
| TME_SCSI_TAPE_FLAG_ATTENTION));
|
|
break;
|
|
|
|
case TME_TAPE_CONTROL_UNLOAD:
|
|
/* a tape has been unloaded: */
|
|
conn_scsi_tape->tme_scsi_tape_connection_flags
|
|
= ((conn_scsi_tape->tme_scsi_tape_connection_flags
|
|
& ~TME_SCSI_TAPE_FLAG_LOADED)
|
|
| TME_SCSI_TAPE_FLAG_ATTENTION);
|
|
break;
|
|
|
|
abort();
|
|
|
|
case TME_TAPE_CONTROL_DENSITY_GET:
|
|
abort();
|
|
|
|
case TME_TAPE_CONTROL_DENSITY_SET:
|
|
abort();
|
|
|
|
case TME_TAPE_CONTROL_BLOCK_SIZE_GET:
|
|
abort();
|
|
|
|
case TME_TAPE_CONTROL_BLOCK_SIZE_SET:
|
|
abort();
|
|
|
|
case TME_TAPE_CONTROL_REWIND:
|
|
case TME_TAPE_CONTROL_MARK_WRITE:
|
|
case TME_TAPE_CONTROL_MARK_SKIPF:
|
|
case TME_TAPE_CONTROL_MARK_SKIPR:
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
/* end the variable arguments: */
|
|
va_end(control_args);
|
|
|
|
/* unlock the mutex: */
|
|
tme_mutex_unlock(&scsi_tape->tme_scsi_tape_mutex);
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this breaks a connection: */
|
|
static int
|
|
_tme_scsi_tape_connection_break(struct tme_connection *conn,
|
|
unsigned int state)
|
|
{
|
|
abort();
|
|
}
|
|
|
|
/* this makes a new tape connection: */
|
|
static int
|
|
_tme_scsi_tape_connection_make(struct tme_connection *conn,
|
|
unsigned int state)
|
|
{
|
|
struct tme_scsi_tape *scsi_tape;
|
|
struct tme_scsi_tape_connection *conn_scsi_tape;
|
|
struct tme_tape_connection *conn_tape;
|
|
int lun;
|
|
int loaded;
|
|
int rc;
|
|
|
|
/* both sides must be tape connections: */
|
|
assert (conn->tme_connection_type == TME_CONNECTION_TAPE);
|
|
assert (conn->tme_connection_other->tme_connection_type == TME_CONNECTION_TAPE);
|
|
|
|
/* recover our data structures: */
|
|
scsi_tape = conn->tme_connection_element->tme_element_private;
|
|
conn_scsi_tape = (struct tme_scsi_tape_connection *) conn;
|
|
|
|
/* we're always set up to answer calls across the connection,
|
|
so we only have to do work when the connection has gone full,
|
|
namely taking the other side of the connection: */
|
|
if (state == TME_CONNECTION_FULL) {
|
|
|
|
/* lock the mutex: */
|
|
tme_mutex_lock(&scsi_tape->tme_scsi_tape_mutex);
|
|
|
|
/* make this tape connection: */
|
|
lun = conn_scsi_tape->tme_scsi_tape_connection_lun;
|
|
assert (scsi_tape->tme_scsi_tape_connections[lun]
|
|
== NULL);
|
|
scsi_tape->tme_scsi_tape_connections[lun]
|
|
= conn_scsi_tape;
|
|
scsi_tape->tme_scsi_tape_device.tme_scsi_device_luns
|
|
|= (1 << lun);
|
|
|
|
/* call any type-specific connection function: */
|
|
if (scsi_tape->tme_scsi_tape_connected != NULL) {
|
|
(*scsi_tape->tme_scsi_tape_connected)(scsi_tape, lun);
|
|
}
|
|
|
|
/* call out a LOAD control to see if the tape is currently loaded: */
|
|
conn_tape
|
|
= ((struct tme_tape_connection *)
|
|
conn_scsi_tape->tme_scsi_tape_connection.tme_tape_connection.tme_connection_other);
|
|
rc =
|
|
((*conn_tape->tme_tape_connection_control)
|
|
(conn_tape,
|
|
TME_TAPE_CONTROL_LOAD,
|
|
&loaded));
|
|
assert (rc == TME_OK);
|
|
conn_scsi_tape->tme_scsi_tape_connection_flags
|
|
= (loaded
|
|
? (TME_SCSI_TAPE_FLAG_LOADED
|
|
| TME_SCSI_TAPE_FLAG_ATTENTION)
|
|
: 0);
|
|
|
|
/* unlock the mutex: */
|
|
tme_mutex_unlock(&scsi_tape->tme_scsi_tape_mutex);
|
|
}
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this returns the new connections possible: */
|
|
static int
|
|
_tme_scsi_tape_connections_new(struct tme_element *element,
|
|
const char * const *args,
|
|
struct tme_connection **_conns,
|
|
char **_output)
|
|
{
|
|
struct tme_scsi_tape *scsi_tape;
|
|
struct tme_scsi_tape_connection *conn_scsi_tape;
|
|
struct tme_tape_connection *conn_tape;
|
|
struct tme_connection *conn;
|
|
int lun;
|
|
int arg_i;
|
|
int usage;
|
|
int rc;
|
|
|
|
/* recover our device: */
|
|
scsi_tape = (struct tme_scsi_tape *) element->tme_element_private;
|
|
|
|
/* check our arguments: */
|
|
lun = -1;
|
|
arg_i = 1;
|
|
usage = FALSE;
|
|
|
|
/* loop reading our arguments: */
|
|
for (;;) {
|
|
|
|
/* the LUN to attach to: */
|
|
if (TME_ARG_IS(args[arg_i + 0], "lun")
|
|
&& lun < 0
|
|
&& (lun = tme_scsi_lun_parse(args[arg_i + 1])) >= 0
|
|
&& lun < TME_SCSI_DEVICE_LUN_COUNT
|
|
&& scsi_tape->tme_scsi_tape_connections[lun] == NULL) {
|
|
arg_i += 2;
|
|
}
|
|
|
|
/* if we've run out of arguments: */
|
|
else if (args[arg_i + 0] == NULL) {
|
|
break;
|
|
}
|
|
|
|
/* this is a bad argument: */
|
|
else {
|
|
tme_output_append_error(_output,
|
|
"%s %s, ",
|
|
args[arg_i],
|
|
_("unexpected"));
|
|
usage = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (usage) {
|
|
tme_output_append_error(_output,
|
|
"%s %s [ lun %s ]",
|
|
_("usage:"),
|
|
args[0],
|
|
_("LOGICAL-UNIT"));
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* return any SCSI device SCSI connection: */
|
|
rc = tme_scsi_device_connections_new(element,
|
|
args,
|
|
_conns,
|
|
_output);
|
|
if (rc != TME_OK) {
|
|
return (rc);
|
|
}
|
|
|
|
/* if we don't have a particular lun, see if there is a free lun.
|
|
if there isn't a free lun, return now: */
|
|
if (lun < 0) {
|
|
for (lun = 0;
|
|
lun < TME_SCSI_DEVICE_LUN_COUNT;
|
|
lun++) {
|
|
if (scsi_tape->tme_scsi_tape_connections[lun] == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
if (lun == TME_SCSI_DEVICE_LUN_COUNT) {
|
|
return (TME_OK);
|
|
}
|
|
}
|
|
|
|
/* create our side of a tape connection: */
|
|
conn_scsi_tape = tme_new0(struct tme_scsi_tape_connection, 1);
|
|
conn_tape = &conn_scsi_tape->tme_scsi_tape_connection;
|
|
conn = &conn_tape->tme_tape_connection;
|
|
|
|
/* fill in the generic connection: */
|
|
conn->tme_connection_next = *_conns;
|
|
conn->tme_connection_type = TME_CONNECTION_TAPE;
|
|
conn->tme_connection_score = tme_tape_connection_score;
|
|
conn->tme_connection_make = _tme_scsi_tape_connection_make;
|
|
conn->tme_connection_break = _tme_scsi_tape_connection_break;
|
|
|
|
/* fill in the tape connection: */
|
|
conn_tape->tme_tape_connection_control = _tme_scsi_tape_control;
|
|
|
|
/* fill in the internal tape connection: */
|
|
conn_scsi_tape->tme_scsi_tape_connection_lun = lun;
|
|
|
|
/* return the connection side possibility: */
|
|
*_conns = conn;
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* the new SCSI tape function: */
|
|
TME_ELEMENT_SUB_NEW_DECL(tme_scsi,tape) {
|
|
int id;
|
|
const char *tape_type;
|
|
const char *vendor;
|
|
const char *product;
|
|
const char *revision;
|
|
struct tme_scsi_tape *scsi_tape;
|
|
struct tme_scsi_device *scsi_device;
|
|
int arg_i;
|
|
int usage;
|
|
unsigned int tape_list_i;
|
|
int (*tape_init) _TME_P((struct tme_scsi_tape *));
|
|
int rc;
|
|
|
|
/* check our arguments: */
|
|
id = -1;
|
|
tape_type = NULL;
|
|
vendor = NULL;
|
|
product = NULL;
|
|
revision = NULL;
|
|
arg_i = 1;
|
|
usage = FALSE;
|
|
|
|
/* loop reading our arguments: */
|
|
for (;;) {
|
|
|
|
/* the SCSI ID: */
|
|
if (TME_ARG_IS(args[arg_i], "id")
|
|
&& id < 0
|
|
&& (id = tme_scsi_id_parse(args[arg_i + 1])) >= 0) {
|
|
arg_i += 2;
|
|
}
|
|
|
|
/* the tape type: */
|
|
else if (TME_ARG_IS(args[arg_i], "type")
|
|
&& tape_type == NULL
|
|
&& args[arg_i + 1] != NULL) {
|
|
tape_type = args[arg_i + 1];
|
|
arg_i += 2;
|
|
}
|
|
|
|
/* any inquiry vendor, product, or revision: */
|
|
else if (TME_ARG_IS(args[arg_i], "vendor")
|
|
&& vendor == NULL
|
|
&& args[arg_i + 1] != NULL) {
|
|
vendor = args[arg_i + 1];
|
|
arg_i += 2;
|
|
}
|
|
else if (TME_ARG_IS(args[arg_i], "product")
|
|
&& product == NULL
|
|
&& args[arg_i + 1] != NULL) {
|
|
product = args[arg_i + 1];
|
|
arg_i += 2;
|
|
}
|
|
else if (TME_ARG_IS(args[arg_i], "revision")
|
|
&& revision == NULL
|
|
&& args[arg_i + 1] != NULL) {
|
|
revision = args[arg_i + 1];
|
|
arg_i += 2;
|
|
}
|
|
|
|
/* if we've run out of arguments: */
|
|
else if (args[arg_i + 0] == NULL) {
|
|
|
|
/* we must have been given an ID and a type: */
|
|
if (id < 0
|
|
|| tape_type == NULL) {
|
|
usage = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* this is a bad argument: */
|
|
else {
|
|
tme_output_append_error(_output,
|
|
"%s %s",
|
|
args[arg_i],
|
|
_("unexpected"));
|
|
usage = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (usage) {
|
|
tme_output_append_error(_output,
|
|
"%s %s id %s type %s [ vendor %s ] [ product %s ] [ revision %s ]",
|
|
_("usage:"),
|
|
args[0],
|
|
_("TYPE"),
|
|
_("ID"),
|
|
_("VENDOR"),
|
|
_("PRODUCT"),
|
|
_("REVISION"));
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* make sure that this tape type is known: */
|
|
tape_init = NULL;
|
|
for (tape_list_i = 0;
|
|
tape_list_i < TME_ARRAY_ELS(_tme_scsi_tape_list);
|
|
tape_list_i++) {
|
|
if (!strcmp(_tme_scsi_tape_list[tape_list_i]._tme_scsi_tape_list_type,
|
|
tape_type)) {
|
|
tape_init = _tme_scsi_tape_list[tape_list_i]._tme_scsi_tape_list_init;
|
|
break;
|
|
}
|
|
}
|
|
if (tape_init == NULL) {
|
|
tme_output_append_error(_output, "%s", tape_type);
|
|
return (ENOENT);
|
|
}
|
|
|
|
/* start the tape structure: */
|
|
scsi_tape = tme_new0(struct tme_scsi_tape, 1);
|
|
scsi_tape->tme_scsi_tape_element = element;
|
|
scsi_tape->tme_scsi_tape_type = tme_strdup(tape_type);
|
|
|
|
/* initialize the generic SCSI device structure: */
|
|
scsi_device = &scsi_tape->tme_scsi_tape_device;
|
|
rc = tme_scsi_device_new(scsi_device, id);
|
|
assert (rc == TME_OK);
|
|
|
|
scsi_device->tme_scsi_device_vendor
|
|
= tme_strdup((vendor == NULL)
|
|
? "TME"
|
|
: vendor);
|
|
scsi_device->tme_scsi_device_product
|
|
= tme_strdup((product == NULL)
|
|
? "TAPE"
|
|
: product);
|
|
scsi_device->tme_scsi_device_revision
|
|
= tme_strdup((revision == NULL)
|
|
? "0000"
|
|
: revision);
|
|
|
|
/* set the commands for sequential-access devices: */
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_INQUIRY,
|
|
tme_scsi_tape_cdb_inquiry);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_REWIND,
|
|
tme_scsi_tape_cdb_rewind);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_BLOCK_LIMITS,
|
|
tme_scsi_tape_cdb_block_limits);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_READ0,
|
|
tme_scsi_tape_cdb_read0);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_WRITE0,
|
|
tme_scsi_tape_cdb_write0);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_WRITE_MARKS,
|
|
tme_scsi_tape_cdb_write_marks);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_SPACE,
|
|
tme_scsi_tape_cdb_space);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_MODE_SELECT,
|
|
tme_scsi_tape_cdb_mode_select);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_MODE_SENSE,
|
|
tme_scsi_tape_cdb_mode_sense);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_LOAD_UNLOAD,
|
|
tme_scsi_tape_cdb_load_unload);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_PREVENT_ALLOW,
|
|
tme_scsi_tape_cdb_prevent_allow);
|
|
|
|
/* there is no type-specific connected function: */
|
|
scsi_tape->tme_scsi_tape_connected = NULL;
|
|
|
|
/* use the default transfer status function: */
|
|
scsi_tape->tme_scsi_tape_xfer_status
|
|
= tme_scsi_tape_xfer_status;
|
|
|
|
/* use the default tape LUN addresser: */
|
|
scsi_device->tme_scsi_device_address_lun
|
|
= tme_scsi_tape_address_lun_aware;
|
|
|
|
/* call the type-specific initialization function: */
|
|
rc = (*tape_init)(scsi_tape);
|
|
assert (rc == TME_OK);
|
|
|
|
/* fill the element: */
|
|
element->tme_element_private = scsi_tape;
|
|
element->tme_element_connections_new = _tme_scsi_tape_connections_new;
|
|
|
|
return (TME_OK);
|
|
}
|