mirror of
https://github.com/phabrics/Run-Sun3-SunOS-4.1.1.git
synced 2026-04-29 11:02:59 -04:00
403 lines
13 KiB
C
403 lines
13 KiB
C
/* $Id: emulexmt02.c,v 1.4 2007/08/25 22:54:59 fredette Exp $ */
|
|
|
|
/* scsi/emulexmt02.c - Emulex MT-02 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: emulexmt02.c,v 1.4 2007/08/25 22:54:59 fredette Exp $");
|
|
|
|
/* includes: */
|
|
#include <tme/scsi/scsi-tape.h>
|
|
|
|
/* macros: */
|
|
|
|
/* the Emulex MT-02 is a SCSI<->QIC translator, and being QIC,
|
|
it only supports a 512 byte block size: */
|
|
#define TME_EMULEXMT02_BLOCK_SIZE (512)
|
|
|
|
/* types: */
|
|
|
|
/* the Emulex MT-02 returns extended sense plus some vendor-specific
|
|
additional bytes: */
|
|
static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_request_sense)
|
|
{
|
|
int lun;
|
|
struct tme_scsi_device_sense *sense;
|
|
tme_uint8_t transfer_length;
|
|
tme_uint8_t error;
|
|
|
|
/* get the addressed LUN: */
|
|
lun = scsi_device->tme_scsi_device_addressed_lun;
|
|
|
|
/* get the sense: */
|
|
sense = &scsi_device->tme_scsi_device_sense[lun];
|
|
|
|
/* see how much space for sense bytes the initiator
|
|
has allocated. zero means four: */
|
|
transfer_length
|
|
= scsi_device->tme_scsi_device_cdb[4];
|
|
if (transfer_length == 0) {
|
|
transfer_length = 4;
|
|
}
|
|
|
|
/* the sun2 PROM insists that the fifth byte of the sense
|
|
have its least significant bit set: */
|
|
sense->tme_scsi_device_sense_data[4] |= 0x01;
|
|
|
|
/* the Emulex MT-02 returns eight additional sense bytes: */
|
|
sense->tme_scsi_device_sense_data[7]
|
|
= 0x08;
|
|
|
|
/* the Emulex error code. get this by dispatching on the
|
|
generic sense key: */
|
|
switch (sense->tme_scsi_device_sense_data[2] & 0xf) {
|
|
case 0x0: /* NO SENSE */
|
|
/* EOM: */
|
|
if (sense->tme_scsi_device_sense_data[2] & 0x40) {
|
|
error = 0x34; /* END OF MEDIA */
|
|
}
|
|
/* ILI: */
|
|
else if (sense->tme_scsi_device_sense_data[2] & 0x20) {
|
|
error = 0x19; /* BAD BLOCK */
|
|
}
|
|
else if (sense->tme_scsi_device_sense_data[2] & 0x80) {
|
|
error = 0x1c; /* FILE MARK */
|
|
}
|
|
else {
|
|
error = 0x00; /* NO SENSE */
|
|
}
|
|
break;
|
|
case 0x1: error = 0x18; break; /* RECOVERED ERROR */
|
|
case 0x2: error = 0x04; break; /* NOT READY */
|
|
case 0x3: error = 0x11; break; /* MEDIA ERROR */
|
|
case 0x4: error = 0x0b; break; /* HARDWARE ERROR */
|
|
case 0x5: error = 0x20; break; /* INVALID COMMAND */
|
|
case 0x6: error = 0x30; break; /* UNIT ATTENTION */
|
|
case 0x7: error = 0x17; break; /* DATA PROTECT */
|
|
case 0x8: error = 0x19; break; /* BAD BLOCK */
|
|
case 0xd: error = 0x14; break; /* BLOCK NOT FOUND */
|
|
default: abort();
|
|
}
|
|
sense->tme_scsi_device_sense_data[8] = error;
|
|
|
|
/* set transfer length */
|
|
sense->tme_scsi_device_sense_valid = transfer_length;
|
|
|
|
/* the Emulex retry count: */
|
|
sense->tme_scsi_device_sense_data[9] = 0x00;
|
|
sense->tme_scsi_device_sense_data[10] = 0x10;
|
|
|
|
/* call the normal REQUEST SENSE handler: */
|
|
tme_scsi_device_cdb_request_sense(scsi_device,
|
|
control_old,
|
|
control_new);
|
|
}
|
|
|
|
/* the Emulex MT-02 returns an all-bits-zero INQUIRY response: */
|
|
static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_inquiry)
|
|
{
|
|
int lun;
|
|
|
|
/* get the addressed LUN: */
|
|
lun = scsi_device->tme_scsi_device_addressed_lun;
|
|
|
|
/* return the INQUIRY data: */
|
|
memset(scsi_device->tme_scsi_device_data,
|
|
0,
|
|
sizeof(scsi_device->tme_scsi_device_data));
|
|
|
|
/* The SunOS/sun3 4.1.1 tape bootblock will refuse to boot off of a
|
|
SCSI device unless the INQUIRY response indicates a tape (so
|
|
apparently it's impossible to install SunOS 4.1.1 on a real Sun3
|
|
from an Emulex tape).
|
|
|
|
Because we're lazy and don't want to implement tme-scsi-1 tape
|
|
yet, we just indicate that we're a tape.
|
|
|
|
However, this has some ramifications for NetBSD/sun2 1.6*, which
|
|
was rigged to specially match an all-bits-zero INQUIRY for an
|
|
Emulex tape. To work around this, when running NetBSD/sun2 or
|
|
NetBSD/sun3 in the emulator, specify vendor EMULEX product "MT-02
|
|
QIC" in the tmesh configuration file, which will cause a regular,
|
|
full INQUIRY response to be sent, but with the specific vendor
|
|
and product so the right quirk entry gets matched: */
|
|
scsi_device->tme_scsi_device_data[0] = TME_SCSI_TYPE_TAPE;
|
|
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
|
|
= 5;
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_out
|
|
= scsi_device->tme_scsi_device_data;
|
|
scsi_device->tme_scsi_device_dma.tme_scsi_dma_in
|
|
= NULL;
|
|
|
|
/* return the data and the GOOD status: */
|
|
tme_scsi_device_target_do_dsmf(scsi_device,
|
|
TME_SCSI_STATUS_GOOD,
|
|
TME_SCSI_MSG_CMD_COMPLETE);
|
|
}
|
|
|
|
/* the Emulex MT-02 behaves as if the "Fixed" bit is always set in
|
|
its READ and WRITE CDBs: */
|
|
static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_read0)
|
|
{
|
|
scsi_device->tme_scsi_device_cdb[1] |= 0x01;
|
|
tme_scsi_tape_cdb_xfer0(scsi_device, TRUE);
|
|
}
|
|
static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_write0)
|
|
{
|
|
scsi_device->tme_scsi_device_cdb[1] |= 0x01;
|
|
tme_scsi_tape_cdb_xfer0(scsi_device, FALSE);
|
|
}
|
|
|
|
/* the Emulex MT-02 MODE SENSE command: */
|
|
static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_mode_sense)
|
|
{
|
|
tme_uint8_t *data;
|
|
tme_uint32_t blocks, block_size;
|
|
int lun;
|
|
|
|
/* get the addressed LUN: */
|
|
lun = scsi_device->tme_scsi_device_addressed_lun;
|
|
|
|
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. we assume a 60MB tape: */
|
|
block_size = TME_EMULEXMT02_BLOCK_SIZE;
|
|
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: */
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* the Emulex MT-02 MODE SELECT command: */
|
|
static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_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;
|
|
|
|
/* XXX for now we discard the parameter list after reading it in: */
|
|
tme_scsi_device_target_do_dsmf(scsi_device,
|
|
TME_SCSI_STATUS_GOOD,
|
|
TME_SCSI_MSG_CMD_COMPLETE);
|
|
}
|
|
|
|
/* this vendor-unique command (0xd) puts us into qic02 mode: */
|
|
static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_qic02)
|
|
{
|
|
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: */
|
|
static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_block_limits)
|
|
{
|
|
tme_uint8_t *data;
|
|
int lun;
|
|
|
|
/* 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: */
|
|
*(data++) = (TME_EMULEXMT02_BLOCK_SIZE >> 16) & 0xff;
|
|
*(data++) = (TME_EMULEXMT02_BLOCK_SIZE >> 8) & 0xff;
|
|
*(data++) = (TME_EMULEXMT02_BLOCK_SIZE >> 0) & 0xff;
|
|
|
|
/* the Minimum Block Length: */
|
|
*(data++) = (TME_EMULEXMT02_BLOCK_SIZE >> 8) & 0xff;
|
|
*(data++) = (TME_EMULEXMT02_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 enforces the block size at tape connection time: */
|
|
static void
|
|
_tme_emulexmt02_connected(struct tme_scsi_tape *scsi_tape,
|
|
int lun)
|
|
{
|
|
struct tme_scsi_tape_connection *conn_scsi_tape;
|
|
struct tme_tape_connection *conn_tape;
|
|
unsigned long sizes[3];
|
|
int rc;
|
|
|
|
/* 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: */
|
|
sizes[0] = TME_EMULEXMT02_BLOCK_SIZE;
|
|
sizes[1] = TME_EMULEXMT02_BLOCK_SIZE;
|
|
sizes[2] = TME_EMULEXMT02_BLOCK_SIZE;
|
|
rc
|
|
= ((*conn_tape->tme_tape_connection_control)
|
|
(conn_tape,
|
|
TME_TAPE_CONTROL_BLOCK_SIZE_SET,
|
|
sizes));
|
|
assert (rc == TME_OK);
|
|
}
|
|
|
|
/* this initializes Emulex MT-02 SCSI tape emulation: */
|
|
int
|
|
tme_scsi_tape_emulexmt02_init(struct tme_scsi_tape *scsi_tape)
|
|
{
|
|
struct tme_scsi_device *scsi_device;
|
|
|
|
scsi_device = &scsi_tape->tme_scsi_tape_device;
|
|
|
|
/* Emulex MT-02 boards don't really support the INQUIRY command: */
|
|
/* XXX FIXME - this is a hack. if the user has specified EMULEX
|
|
for the vendor name, don't override the INQUIRY handling: */
|
|
if (strcmp(scsi_device->tme_scsi_device_vendor, "EMULEX")) {
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_INQUIRY,
|
|
_tme_emulexmt02_cdb_inquiry);
|
|
}
|
|
|
|
/* our type-specific connection function: */
|
|
scsi_tape->tme_scsi_tape_connected
|
|
= _tme_emulexmt02_connected;
|
|
|
|
/* our block sizes: */
|
|
scsi_tape->tme_scsi_tape_block_size_min = TME_EMULEXMT02_BLOCK_SIZE;
|
|
scsi_tape->tme_scsi_tape_block_size_max = TME_EMULEXMT02_BLOCK_SIZE;
|
|
scsi_tape->tme_scsi_tape_block_size_current = TME_EMULEXMT02_BLOCK_SIZE;
|
|
|
|
/* our type-specific CDB functions: */
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
0x0d,
|
|
_tme_emulexmt02_cdb_qic02);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_READ0,
|
|
_tme_emulexmt02_cdb_read0);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_WRITE0,
|
|
_tme_emulexmt02_cdb_write0);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_MODE_SENSE,
|
|
_tme_emulexmt02_cdb_mode_sense);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_MODE_SELECT,
|
|
_tme_emulexmt02_cdb_mode_select);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_BLOCK_LIMITS,
|
|
_tme_emulexmt02_cdb_block_limits);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_REQUEST_SENSE,
|
|
_tme_emulexmt02_cdb_request_sense);
|
|
TME_SCSI_DEVICE_DO_CDB(scsi_device,
|
|
TME_SCSI_CDB_TAPE_RESERVE,
|
|
tme_scsi_device_cdb_illegal);
|
|
|
|
return (TME_OK);
|
|
}
|