WinUAE/scsitape.cpp

861 lines
20 KiB
C++

/*
* UAE - The Un*x Amiga Emulator
*
* SCSI tape emulation
*
* Copyright 2013 Toni Wilen
*
*/
#include "sysconfig.h"
#include "sysdeps.h"
#include "options.h"
#include "filesys.h"
#include "scsi.h"
#include "blkdev.h"
#include "zfile.h"
#include "memory.h"
#include "threaddep/thread.h"
#include "a2091.h"
#include "fsdb.h"
int log_tapeemu = 1;
#define TAPE_INDEX _T("index.tape")
static struct scsi_data_tape *tapeunits[MAX_FILESYSTEM_UNITS];
static bool notape (struct scsi_data_tape *tape)
{
return tape->tape_dir[0] == 0 || tape->nomedia;
}
static int rl (uae_u8 *p)
{
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]);
}
static void wl (uae_u8 *p, uae_u32 v)
{
p[0] = v >> 24;
p[1] = v >> 16;
p[2] = v >> 8;
p[3] = v;
}
void tape_free (struct scsi_data_tape *tape)
{
if (!tape)
return;
zfile_fclose (tape->zf);
zfile_fclose (tape->index);
zfile_closedir_archive (tape->zd);
for (int i = 0; i < MAX_FILESYSTEM_UNITS; i++) {
if (tapeunits[i] == tape)
tapeunits[i] = NULL;
}
xfree(tape);
}
static void tape_init (int unit, struct scsi_data_tape *tape, const TCHAR *tape_directory, bool readonly)
{
TCHAR path[MAX_DPATH];
TCHAR tape_dir_path[MAX_DPATH];
memset (tape, 0, sizeof (struct scsi_data_tape));
cfgfile_resolve_path_out_load(tape_directory, tape_dir_path, MAX_DPATH, PATH_TAPE);
_tcscpy(tape->tape_dir, tape_dir_path);
path[0] = 0;
tape->blocksize = 512;
tape->wp = readonly;
tape->beom = -1;
tape->nomedia = false;
tape->unloaded = false;
if (my_existsdir (tape->tape_dir)) {
tape->realdir = true;
} else {
if (_tcslen(tape->tape_dir) > _tcslen(TAPE_INDEX) && !_tcsicmp(tape->tape_dir + _tcslen(tape->tape_dir) - _tcslen(TAPE_INDEX), TAPE_INDEX)) {
_tcscpy(path, tape->tape_dir);
tape->index = zfile_fopen(path, _T("rb"), ZFD_NORMAL);
tape->tape_dir[_tcslen(tape->tape_dir) - _tcslen(TAPE_INDEX) - 1] = 0;
if (my_existsdir(tape->tape_dir)) {
tape->realdir = true;
} else {
_tcscpy(tape->tape_dir, tape_dir_path);
zfile_fclose(tape->index);
tape->index = NULL;
}
} else {
tape->zd = zfile_opendir_archive(tape->tape_dir, ZFD_ARCHIVE | ZFD_NORECURSE);
if (!tape->zd) {
tape->nomedia = true;
}
}
}
if (!tape->index) {
_tcscpy(path, tape_dir_path);
_tcscat(path, FSDB_DIR_SEPARATOR_S);
_tcscat(path, TAPE_INDEX);
tape->index = zfile_fopen(path, _T("rb"), ZFD_NORMAL);
}
write_log(_T("TAPEEMU MODE: %d,%d PATH: '%s' INDEX: '%s'\n"), tape->realdir, tape->nomedia, tape->tape_dir, tape->index ? path : _T("<none>"));
tapeunits[unit] = tape;
}
bool tape_can_write(const TCHAR *tape_directory)
{
TCHAR tmp[MAX_DPATH];
if (!tape_directory[0])
return false;
if (my_existsdir(tape_directory))
return true;
_tcscpy(tmp, tape_directory);
if (_tcslen(tmp) > _tcslen(TAPE_INDEX) && !_tcsicmp(tmp + _tcslen(tmp) - _tcslen(TAPE_INDEX), TAPE_INDEX))
return true;
tmp[_tcslen(tmp) - _tcslen(TAPE_INDEX) - 1] = 0;
if (my_existsdir(tmp))
return true;
return false;
}
struct scsi_data_tape *tape_alloc (int unitnum, const TCHAR *tape_directory, bool readonly)
{
struct scsi_data_tape *tape = xcalloc (struct scsi_data_tape, 1);
tape_init (unitnum, tape, tape_directory, readonly);
return tape;
}
void tape_media_change (int unitnum, struct uaedev_config_info *ci)
{
struct scsi_data_tape *tape = tapeunits[unitnum];
if (!tape)
return;
tape_init (unitnum, tape, ci->rootdir, ci->readonly);
}
bool tape_get_info (int unitnum, struct device_info *di)
{
struct scsi_data_tape *tape = tapeunits[unitnum];
memset (di, 0, sizeof (struct device_info));
if (!tape)
return false;
di->media_inserted = notape (tape) == false;
_tcscpy (di->label, tape->tape_dir);
return true;
}
static void tape_write_filemark(struct scsi_data_tape *tape)
{
TCHAR path[MAX_DPATH];
if (!tape->realdir)
return;
_stprintf(path, _T("%s%s%s"), tape->tape_dir, FSDB_DIR_SEPARATOR_S, TAPE_INDEX);
struct zfile *zf = zfile_fopen(path, _T("a+b"));
if (zf) {
zfile_fputs(zf, _T("\n"));
zfile_fclose(zf);
}
}
static void rewind (struct scsi_data_tape *tape)
{
if (tape->pending_filemark) {
tape_write_filemark(tape);
}
tape->pending_filemark = false;
tape->init_loaded = false;
zfile_fclose (tape->zf);
tape->zf = NULL;
my_closedir (tape->od);
tape->file_number = 0;
tape->file_offset = 0;
tape->od = NULL;
tape->beom = -1;
if (tape->index)
zfile_fseek (tape->index, 0, SEEK_SET);
if (tape->zd)
zfile_resetdir_archive (tape->zd);
}
static void erase (struct scsi_data_tape *tape)
{
struct my_opendir_s *od;
if (!tape->realdir)
return;
tape->pending_filemark = false;
rewind (tape);
od = my_opendir (tape->tape_dir);
if (od) {
for (;;) {
TCHAR path[MAX_DPATH], filename[MAX_DPATH];
if (!my_readdir (od, filename))
break;
TCHAR *ext = _tcsrchr (filename, '.');
if (ext && !_tcsicmp (ext, _T(".tape"))) {
_stprintf (path, _T("%s%s%s"), tape->tape_dir, FSDB_DIR_SEPARATOR_S, filename);
if (my_existsfile (path))
my_unlink (path, false);
}
}
my_closedir (od);
}
}
static void next_file (struct scsi_data_tape *tape)
{
zfile_fclose (tape->zf);
tape->zf = NULL;
tape->file_offset = 0;
tape->last_filemark = false;
if (log_tapeemu)
write_log(_T("TAPEEMU: Next file\n"));
if (tape->beom > 0)
goto end;
if (!tape->index && tape->realdir) {
TCHAR tmp[MAX_DPATH];
_stprintf(tmp, _T("%s%s%s"), tape->tape_dir, FSDB_DIR_SEPARATOR_S, TAPE_INDEX);
tape->index = zfile_fopen(tmp, _T("rb"), ZFD_NORMAL);
}
if (tape->index) {
TCHAR path[MAX_DPATH];
TCHAR name[256];
name[0] = 0;
zfile_fgets (name, sizeof name / sizeof (TCHAR), tape->index);
if (name[0] == 0) {
// was last line? Check if filemark needs to be added
if (tape->last_filemark) {
tape->last_filemark = false;
if (log_tapeemu)
write_log(_T("TAPEEMU: Last Filemark\n"));
return;
}
goto end;
}
if (name[0] == 10 || name[0] == 13) {
if (log_tapeemu)
write_log(_T("TAPEEMU: Extra Filemark\n"));
return;
}
// LF at the end = have at least one filemark
if (name[0] != '\0' && (name[_tcslen(name) - 1] == 10 || name[_tcslen(name) - 1] == 13)) {
tape->last_filemark = true;
}
my_trim (name);
if (name[0] == 0)
goto end;
_tcscpy (path, tape->tape_dir);
_tcscat (path, FSDB_DIR_SEPARATOR_S);
_tcscat (path, name);
tape->zf = zfile_fopen (path, _T("rb"), ZFD_NORMAL);
if (log_tapeemu) {
if (tape->zf)
write_log(_T("TAPEEMU: File '%s' Size %lld\n"), path, zfile_size(tape->zf));
else
write_log(_T("TAPEEMU: File '%s' could not be opened\n"), path);
}
} else if (tape->realdir) {
TCHAR path[MAX_DPATH], filename[MAX_DPATH];
if (tape->od == NULL)
tape->od = my_opendir (tape->tape_dir);
if (!tape->od) {
tape_free (tape);
goto end;
}
for (;;) {
if (!my_readdir (tape->od, filename))
goto end;
if (_tcsicmp (filename, TAPE_INDEX))
continue;
_stprintf (path, _T("%s%s%s"), tape->tape_dir, FSDB_DIR_SEPARATOR_S, filename);
if (!my_existsfile (path))
continue;
tape->zf = zfile_fopen (path, _T("rb"), 0);
if (!tape->zf) {
if (log_tapeemu)
write_log(_T("TAPEEMU DIR: File '%s' could not be opened\n"), path);
continue;
}
break;
}
if (tape->zf && log_tapeemu) {
write_log(_T("TAPEEMU DIR: File '%s' Size %lld\n"), zfile_getname(tape->zf), zfile_size(tape->zf));
}
} else {
tape->zf = zfile_readdir_archive_open (tape->zd, _T("rb"));
if (log_tapeemu && tape->zf)
write_log(_T("TAPEEMU ARC: File '%s' Size %lld\n"), zfile_getname(tape->zf), zfile_size(tape->zf));
}
if (tape->zf) {
tape->file_number++;
return;
}
end:
if (tape->beom < 1)
tape->beom = 1;
if (tape->beom == 2) {
if (log_tapeemu)
write_log(_T("TAPEEMU: end of media\n"));
} else {
if (log_tapeemu)
write_log(_T("TAPEEMU: end of data\n"));
}
}
static int tape_read (struct scsi_data_tape *tape, uae_u8 *scsi_data, int len, bool *eof)
{
int got = 0;
*eof = false;
if (tape->beom > 0)
return 0;
if (!tape->init_loaded) {
rewind(tape);
tape->init_loaded = true;
next_file(tape);
if (tape->beom > 0)
return 0;
}
if (tape->zf) {
zfile_fseek(tape->zf, tape->file_offset, SEEK_SET);
if (scsi_data) {
got = (int)zfile_fread(scsi_data, 1, len, tape->zf);
uae_s64 pos = zfile_ftell(tape->zf);
if (log_tapeemu)
write_log(_T("TAPEEMU READ: Requested %ld, read %ld, pos %lld, %lld remaining.\n"), len, got, pos, zfile_size(tape->zf) - pos);
} else {
got = 0;
if (len > 0) {
uae_u8 *data = xmalloc(uae_u8, len);
if (data) {
got = (int)zfile_fread(data, 1, len, tape->zf);
xfree(data);
}
}
}
tape->file_offset += got;
}
if (got < len) {
*eof = true;
next_file(tape);
}
return got;
}
static int tape_write (struct scsi_data_tape *tape, uae_u8 *scsi_data, int len)
{
TCHAR path[MAX_DPATH], numname[30];
int exists;
if (!tape->realdir)
return -1;
_stprintf (numname, _T("%05d.tape"), tape->file_number);
_stprintf (path, _T("%s%s%s"), tape->tape_dir, FSDB_DIR_SEPARATOR_S, numname);
exists = my_existsfile (path);
struct zfile *zf = zfile_fopen (path, _T("a+b"));
if (!zf)
return -1;
zfile_fseek (zf, 0, SEEK_END);
len = (int)zfile_fwrite (scsi_data, 1, len, zf);
zfile_fclose (zf);
if (!exists) {
if (tape->index) {
zfile_fclose(tape->index);
tape->index = NULL;
}
_stprintf (path, _T("%s%s%s"), tape->tape_dir, FSDB_DIR_SEPARATOR_S, TAPE_INDEX);
zf = zfile_fopen (path, _T("a+b"));
if (zf) {
zfile_fputs (zf, numname);
}
zfile_fclose (zf);
}
return len;
}
int scsi_tape_emulate (struct scsi_data_tape *tape, uae_u8 *cmdbuf, int scsi_cmd_len,
uae_u8 *scsi_data, int *data_len, uae_u8 *r, int *reply_len, uae_u8 *s, int *sense_len)
{
int len;
int lr = 0, ls = 0;
int scsi_len = -1;
int status = 0;
int lun;
bool eof;
bool isinfo = false;
uae_u32 info = 0;
if (cmdbuf == NULL)
return 0;
if (log_tapeemu)
write_log (_T("TAPEEMU: %02X.%02X.%02X.%02X.%02X.%02X\n"),
cmdbuf[0], cmdbuf[1], cmdbuf[2], cmdbuf[3], cmdbuf[4], cmdbuf[5]);
if (cmdbuf[0] == 3) {
if (tape->beom == -1)
s[9] |= 0x8; // beginning of media
if (tape->beom == 2)
s[2] |= 0x40; // end of media
if (*sense_len < 0x12)
*sense_len = 0x12;
return 0;
}
*reply_len = *sense_len = 0;
memset (r, 0, 256);
memset (s, 0, 256);
s[0] = 0x70;
lun = cmdbuf[1] >> 5;
if (cmdbuf[0] != 0x03 && cmdbuf[0] != 0x12 && lun) {
status = 2; /* CHECK CONDITION */
s[0] = 0x70;
s[2] = 5; /* ILLEGAL REQUEST */
s[12] = 0x25; /* INVALID LUN */
ls = 0x12;
goto end;
}
switch (cmdbuf[0])
{
case 0x00: /* TEST UNIT READY */
if (notape (tape))
goto notape;
if (tape->unloaded)
goto unloaded;
scsi_len = 0;
break;
case 0x19: /* ERASE */
if (log_tapeemu)
write_log (_T("TAPEEMU ERASE\n"));
if (notape (tape))
goto notape;
if (tape->unloaded)
goto unloaded;
if (tape->wp)
goto writeprot;
erase (tape);
rewind (tape);
tape->beom = 2;
scsi_len = 0;
break;
case 0x1b: /* LOAD/UNLOAD */
{
bool load = (cmdbuf[4] & 1) != 0;
bool ret = (cmdbuf[4] & 2) != 0;
bool eot = (cmdbuf[4] & 4) != 0;
if (log_tapeemu)
write_log (_T("TAPEEMU LOAD/UNLOAD %d:%d:%d\n"), eot, ret, load);
if (notape (tape))
goto notape;
if (load && eot)
goto errreq;
rewind (tape);
tape->unloaded = !load;
if (eot) {
tape->beom = 2;
} else {
tape->beom = -1;
}
scsi_len = 0;
break;
}
case 0x01: /* REWIND */
if (log_tapeemu)
write_log (_T("TAPEEMU: REWIND. IMMED=%d\n"), cmdbuf[1] & 1);
if (notape (tape))
goto notape;
if (tape->unloaded)
goto unloaded;
rewind (tape);
scsi_len = 0;
break;
case 0x11: /* SPACE */
{
int code = cmdbuf[1] & 15;
int count = rl (cmdbuf + 1) & 0xffffff;
if (log_tapeemu)
write_log (_T("TAPEEMU: SPACE code=%d count=%d\n"), code, count);
if (notape (tape))
goto notape;
if (tape->unloaded)
goto unloaded;
if (code >= 2)
goto errreq;
if (count < 0)
goto errreq;
if (code == 1) {
while (count > 0) {
next_file(tape);
if (tape->beom > 0) {
info = count;
isinfo = true;
goto endofdata;
}
count--;
}
} else {
while (count > 0) {
bool eof = false;
int got = tape_read(tape, NULL, tape->blocksize, &eof);
if (eof || got < tape->blocksize) {
info = count;
isinfo = true;
goto endofdata;
}
count--;
}
}
scsi_len = 0;
}
break;
case 0x10: /* WRITE FILEMARK */
if (cmdbuf[1] & 2) // set mark?
goto errreq;
len = rl (cmdbuf + 1) & 0xffffff;
if (log_tapeemu)
write_log (_T("TAPEEMU WRITE FILEMARK %d\n"), len);
if (notape (tape))
goto notape;
if (tape->unloaded)
goto unloaded;
if (tape->wp)
goto writeprot;
// sanity check
if (len > 256) {
write_log(_T("TAPEEMU: WRITE FILEMARK too many filemarks (%d)\n"), len);
len = 256;
}
while (len > 0) {
tape_write_filemark(tape);
len--;
}
tape->pending_filemark = false;
tape->file_number++;
if (tape->beom < 1)
tape->beom = 1;
scsi_len = 0;
break;
case 0x0a: /* WRITE (6) */
if (!(cmdbuf[1] & 1))
goto errreq;
len = rl (cmdbuf + 1) & 0xffffff;
len *= tape->blocksize;
if (log_tapeemu)
write_log (_T("TAPEEMU WRITE %lld (%d, %d)\n"), len, rl (cmdbuf + 1) & 0xffffff, cmdbuf[1] & 1);
if (notape (tape))
goto notape;
if (tape->unloaded)
goto unloaded;
if (tape->wp)
goto writeprot;
if (len && tape->beom < 1)
tape->beom = 1;
scsi_len = tape_write (tape, scsi_data, len);
if (scsi_len < 0)
goto writeprot;
tape->pending_filemark = true;
break;
case 0x08: /* READ (6) */
if (!(cmdbuf[1] & 1))
goto errreq;
len = rl (cmdbuf + 1) & 0xffffff;
len *= tape->blocksize;
if (log_tapeemu)
write_log (_T("TAPEEMU READ %lld (%d, %d)\n"), len, rl (cmdbuf + 1) & 0xffffff, cmdbuf[1] & 1);
if (notape (tape))
goto notape;
if (tape->unloaded)
goto unloaded;
if (len && tape->beom < 0)
tape->beom = 0;
scsi_len = tape_read (tape, scsi_data, len, &eof);
if (log_tapeemu)
write_log (_T("-> READ %d bytes, LEN=%d EOF=%d\n"), scsi_len, len, eof);
if ((cmdbuf[1] & 1) && scsi_len > 0 && scsi_len < len) {
int gap = tape->blocksize - (scsi_len & (tape->blocksize - 1));
if (gap > 0 && gap < tape->blocksize)
memset (scsi_data + scsi_len, 0, gap);
scsi_len += tape->blocksize - 1;
scsi_len &= ~(tape->blocksize - 1);
}
if (eof) {
status = SCSI_STATUS_CHECK_CONDITION;
s[0] = 0x70; // code
s[2] = 0x80 | 0; /* File Mark Detected + NO SENSE */
if (cmdbuf[1] & 1)
info = (len - scsi_len) / tape->blocksize; // remaining blocks
else
info = len; // request size
isinfo = true;
s[12] = 0;
s[13] = 1; /* File Mark detected */
ls = 0x12;
if (log_tapeemu)
write_log(_T("TAPEEMU READ FILE END, %ld remaining (BEOM=%d)\n"), len - scsi_len, tape->beom);
} else if (tape->beom > 0) {
scsi_len = 0;
if (tape->beom <= 0)
tape->beom = 1;
status = SCSI_STATUS_CHECK_CONDITION;
s[0] = 0x70; // code
s[2] = tape->beom == 1 ? 8 : 0; /* BLANK CHECK */
if (cmdbuf[1] & 1)
info = len / tape->blocksize;
else
info = len;
isinfo = true;
s[12] = 0;
s[13] = tape->beom == 2 ? 2 : 5; /* End-of-media / End-of-data */
ls = 0x12;
if (log_tapeemu)
write_log(_T("TAPEEMU READ DATA END, %ld remaining\n"), len - scsi_len);
}
break;
case 0x5a: // MODE SENSE(10)
case 0x1a: /* MODE SENSE(6) */
{
uae_u8 *p;
int maxlen;
bool sense10 = cmdbuf[0] == 0x5a;
int totalsize, bdsize;
int pc = cmdbuf[2] >> 6;
int pcode = cmdbuf[2] & 0x3f;
int dbd = cmdbuf[1] & 8;
if (cmdbuf[0] == 0x5a)
dbd = 1;
if (log_tapeemu)
write_log (_T("TAPEEMU MODE SENSE PC=%d CODE=%d DBD=%d\n"), pc, pcode, dbd);
p = r;
if (sense10) {
totalsize = 8 - 2;
maxlen = (cmdbuf[7] << 8) | cmdbuf[8];
p[2] = 0;
p[3] = 0;
p[4] = 0;
p[5] = 0;
p[6] = 0;
p[7] = 0;
p += 8;
} else {
totalsize = 4 - 1;
maxlen = cmdbuf[4];
p[1] = 0;
p[2] = tape->wp ? 0x80 : 0;
p[3] = 0;
p += 4;
}
bdsize = 0;
if (!dbd) {
p[0] = 0x10; // density qic-150
p[1] = 0;
p[2] = 0;
p[3] = 0;
wl(p + 4, tape->blocksize);
bdsize = 8;
p += bdsize;
}
if (pcode)
goto errreq;
if (sense10) {
totalsize += bdsize;
r[6] = bdsize >> 8;
r[7] = bdsize & 0xff;
r[0] = totalsize >> 8;
r[1] = totalsize & 0xff;
} else {
totalsize += bdsize;
r[3] = bdsize & 0xff;
r[0] = totalsize & 0xff;
}
lr = totalsize + 1;
if (lr > maxlen)
lr = maxlen;
}
break;
case 0x55: // MODE SELECT(10)
case 0x15: // MODE SELECT(6)
{
uae_u8 *p;
bool mode10 = cmdbuf[0] == 0x55;
int nb = 0, bl = 512;
p = scsi_data + 4;
if (mode10)
p += 4;
if (scsi_data[3] >= 8) {
nb = rl (p) & 0xffffff;
bl = rl (p + 4) & 0xffffff;
}
if (log_tapeemu)
write_log (_T("TAPEEMU MODE SELECT BUFM=%d BDL=%d Density=%d NB=%d BL=%d\n"), (scsi_data[2] & 0x10) != 0, scsi_data[3], p[0], nb, bl);
if (nb || bl != 512)
goto err;
scsi_len = 0;
break;
}
case 0x05: /* READ BLOCK LIMITS */
if (log_tapeemu)
write_log (_T("TAPEEMU READ BLOCK LIMITS\n"));
r[0] = 0;
r[1] = 0;
r[2] = 2; // 0x200 = 512
r[3] = 0;
r[4] = 2; // 0x200 = 512
r[5] = 0;
lr = 6;
break;
case 0x16: /* RESERVE UNIT */
if (log_tapeemu)
write_log (_T("TAPEEMU RESERVE UNIT\n"));
scsi_len = 0;
break;
case 0x17: /* RELEASE UNIT */
if (log_tapeemu)
write_log (_T("TAPEEMU RELEASE UNIT\n"));
scsi_len = 0;
break;
case 0x1e: /* PREVENT/ALLOW MEDIUM REMOVAL */
if (log_tapeemu)
write_log (_T("TAPEEMU PREVENT/ALLOW MEDIUM REMOVAL\n"));
scsi_len = 0;
break;
case 0x12: /* INQUIRY */
{
if (log_tapeemu)
write_log (_T("TAPEEMU INQUIRY\n"));
if ((cmdbuf[1] & 1) || cmdbuf[2] != 0)
goto err;
len = cmdbuf[4];
if (cmdbuf[1] >> 5) {
r[0] = 0x7f;
} else {
r[0] = INQ_SEQD;
}
r[1] |= 0x80; // removable
r[2] = 0x01; /* supports SCSI-1 */
r[3] = 0x00; /* response data format */
r[4] = 32; /* additional length */
r[6] = 0x10; /* Density */
r[7] = 0;
scsi_len = lr = len < 36 ? len : 36;
char *s = ua (_T("UAE"));
memcpy (r + 8, s, strlen (s));
xfree (s);
s = ua (_T("SCSI TAPE"));
memcpy (r + 16, s, strlen (s));
xfree (s);
s = ua (_T("0.3"));
memcpy (r + 32, s, strlen (s));
xfree (s);
for (int i = 8; i < 36; i++) {
if (r[i] == 0)
r[i] = 32;
}
}
break;
default:
write_log (_T("TAPEEMU: unsupported scsi command 0x%02X\n"), cmdbuf[0]);
err:
status = SCSI_STATUS_CHECK_CONDITION;
s[0] = 0x70;
s[2] = SCSI_SK_ILLEGAL_REQ;
s[12] = SCSI_INVALID_COMMAND;
ls = 0x12;
break;
writeprot:
status = 2; /* CHECK CONDITION */
s[0] = 0x70;
s[2] = SCSI_SK_DATA_PROTECT;
s[12] = 0x27; /* WRITE PROTECTED */
ls = 0x12;
break;
errreq:
lr = -1;
status = SCSI_STATUS_CHECK_CONDITION;
s[0] = 0x70;
s[2] = SCSI_SK_ILLEGAL_REQ;
s[12] = SCSI_INVALID_FIELD;
ls = 0x12;
break;
endofdata:
status = SCSI_STATUS_CHECK_CONDITION;
s[0] = 0x70;
s[2] = SCSI_SK_BLANK_CHECK;
s[12] = 0;
s[13] = 5; /* End-of-data */
ls = 0x12;
break;
unloaded:
status = SCSI_STATUS_CHECK_CONDITION;
s[0] = 0x70;
s[2] = SCSI_SK_NOT_READY;
s[12] = SCSI_NOT_READY;
ls = 0x12;
break;
notape:
status = SCSI_STATUS_CHECK_CONDITION;
s[0] = 0x70;
s[2] = SCSI_SK_NOT_READY;
s[12] = SCSI_MEDIUM_NOT_PRESENT;
ls = 0x12;
break;
}
end:
*data_len = scsi_len;
*reply_len = lr;
*sense_len = ls;
if (lr > 0) {
if (log_tapeemu) {
write_log (_T("TAPEEMU REPLY: "));
for (int i = 0; i < lr && i < 40; i++)
write_log (_T("%02X."), r[i]);
write_log (_T("\n"));
}
}
if (ls > 0) {
// SCSI-1
if (ls > 14)
ls = 14;
if (isinfo) {
s[0] |= 0x80;
wl(&s[3], info);
}
if (ls > 7)
s[7] = ls - 8;
if (tape->beom == 2)
s[2] |= 0x40;
if (tape->beom == -1)
s[9] |= 0x8;
if (log_tapeemu) {
write_log (_T("TAPEEMU SENSE: "));
for (int i = 0; i < ls; i++)
write_log (_T("%02X."), s[i]);
write_log (_T("\n"));
}
}
return status;
}