Files
org-hyperion-cules/tapedev.c
Enrico Sorichetti 1c4e638226 fixed some showstppers for zlib, clang, apple
silenced a few warnings, more to come later
2014-10-05 17:36:26 +02:00

2704 lines
96 KiB
C

/* TAPEDEV.C (c) Copyright Roger Bowler, 1999-2012 */
/* Hercules Tape Device Handler */
/* */
/* Released under "The Q Public License Version 1" */
/* (http://www.hercules-390.org/herclic.html) as modifications to */
/* Hercules. */
/* Original Author: Roger Bowler */
/* Prime Maintainer: Ivan Warren */
/* Secondary Maintainer: "Fish" (David B. Trout) */
/*-------------------------------------------------------------------*/
/* This module contains device handling functions for emulated */
/* magnetic tape devices for the Hercules ESA/390 emulator. */
/*-------------------------------------------------------------------*/
/* Messages issued by the TAPEDEV.C module are prefixed HHCTA0nn */
/* CCW processing functions have been moved to module TAPECCW.C */
/* */
/* Five emulated tape formats are supported: */
/* */
/* 1. AWSTAPE This is the format used by the P/390. */
/* The entire tape is contained in a single flat file. */
/* A tape block consists of one or more block segments. */
/* Each block segment is preceded by a 6-byte header. */
/* Files are separated by tapemarks, which consist */
/* of headers with zero block length. */
/* AWSTAPE files are readable and writable. */
/* */
/* Support for AWSTAPE is in the "AWSTAPE.C" member. */
/* */
/* 2. OMATAPE This is the Optical Media Attach device format. */
/* Each physical file on the tape is represented by */
/* a separate flat file. The collection of files that */
/* make up the physical tape is obtained from an ASCII */
/* text file called the "tape description file", whose */
/* file name is always tapes/xxxxxx.tdf (where xxxxxx */
/* is the volume serial number of the tape). */
/* Three formats of tape files are supported: */
/* * FIXED files contain fixed length EBCDIC blocks */
/* with no headers or delimiters. The block length */
/* is specified in the TDF file. */
/* * TEXT files contain variable length ASCII blocks */
/* delimited by carriage return line feed sequences. */
/* The data is translated to EBCDIC by this module. */
/* * HEADER files contain variable length blocks of */
/* EBCDIC data prefixed by a 16-byte header. */
/* The TDF file and all of the tape files must reside */
/* reside under the same directory which is normally */
/* on CDROM but can be on disk. */
/* OMATAPE files are supported as read-only media. */
/* */
/* OMATAPE tape Support is in the "OMATAPE.C" member. */
/* */
/* 3. SCSITAPE This format allows reading and writing of 4mm or */
/* 8mm DAT tape, 9-track open-reel tape, or 3480-type */
/* cartridge on an appropriate SCSI-attached drive. */
/* All SCSI tapes are processed using the generalized */
/* SCSI tape driver (st.c) which is controlled using */
/* the MTIOCxxx set of IOCTL commands. */
/* PROGRAMMING NOTE: the 'tape' portability macros for */
/* physical (SCSI) tapes MUST be used for all tape i/o! */
/* */
/* SCSI tape Support is in the "SCSITAPE.C" member. */
/* */
/* 4. HET This format is based on the AWSTAPE format but has */
/* been extended to support compression. Since the */
/* basic file format has remained the same, AWSTAPEs */
/* can be read/written using the HET routines. */
/* */
/* Support for HET is in the "HETTAPE.C" member. */
/* */
/* 5. FAKETAPE This is the format used by Fundamental Software */
/* on their FLEX-ES systems. It it similar to the AWS */
/* format. The entire tape is contained in a single */
/* flat file. A tape block is preceded by a 12-ASCII- */
/* hex-characters header which indicate the size of */
/* the previous and next blocks. Files are separated */
/* by tapemarks which consist of headers with a zero */
/* current block length. FakeTapes are both readable */
/* and writable. */
/* */
/* Support for FAKETAPE is in the "FAKETAPE.C" member. */
/* */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* Additional credits: */
/* 3480 commands contributed by Jan Jaeger */
/* Sense byte improvements by Jan Jaeger */
/* 3480 Read Block ID and Locate CCWs by Brandon Hill */
/* Unloaded tape support by Brandon Hill v209*/
/* HET format support by Leland Lucius v209*/
/* JCS - minor changes by John Summerfield 2003*/
/* PERFORM SUBSYSTEM FUNCTION / CONTROL ACCESS support by */
/* Adrian Trenkwalder (with futher enhancements by Fish) */
/* **INCOMPLETE** 3590 support by Fish (David B. Trout) */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* Reference information: */
/* SC53-1200 S/370 and S/390 Optical Media Attach/2 User's Guide */
/* SC53-1201 S/370 and S/390 Optical Media Attach/2 Technical Ref */
/* SG24-2506 IBM 3590 Tape Subsystem Technical Guide */
/* GA32-0331 IBM 3590 Hardware Reference */
/* GA32-0329 IBM 3590 Introduction and Planning Guide */
/* SG24-2594 IBM 3590 Multiplatform Implementation */
/* ANSI INCITS 131-1994 (R1999) SCSI-2 Reference */
/* GA32-0127 IBM 3490E Hardware Reference */
/* GC35-0152 EREP Release 3.5.0 Reference */
/* SA22-7204 ESA/390 Common I/O-Device Commands */
/*-------------------------------------------------------------------*/
#include "hstdinc.h"
#ifndef _TAPEDEV_C_
#define _TAPEDEV_C_
#endif
#include "hercules.h" /* need Hercules control blocks */
#include "tapedev.h" /* Main tape handler header file */
//#define ENABLE_TRACING_STMTS 1 // (Fish: DEBUGGING)
//#include "dbgtrace.h" // (Fish: DEBUGGING)
/*-------------------------------------------------------------------*/
#if defined(WIN32) && defined(OPTION_DYNAMIC_LOAD) && !defined(HDL_USE_LIBTOOL) && !defined(_MSVC_)
SYSBLK *psysblk;
#define sysblk (*psysblk)
#endif
/*-------------------------------------------------------------------*/
DEVHND tapedev_device_hndinfo =
{
&tapedev_init_handler, /* Device Initialisation */
&tapedev_execute_ccw, /* Device CCW execute */
&tapedev_close_device, /* Device Close */
&tapedev_query_device, /* Device Query */
NULL, /* Device Extended Query */
NULL, /* Device Start channel pgm */
NULL, /* Device End channel pgm */
NULL, /* Device Resume channel pgm */
NULL, /* Device Suspend channel pgm */
NULL, /* Device Halt channel pgm */
NULL, /* Device Read */
NULL, /* Device Write */
NULL, /* Device Query used */
NULL, /* Device Reserve */
NULL, /* Device Release */
NULL, /* Device Attention */
TapeImmedCommands, /* Immediate CCW Codes */
NULL, /* Signal Adapter Input */
NULL, /* Signal Adapter Output */
NULL, /* Signal Adapter Sync */
NULL, /* Signal Adapter Output Mult */
NULL, /* QDIO subsys desc */
NULL, /* QDIO set subchan ind */
NULL, /* Hercules suspend */
NULL /* Hercules resume */
};
/*-------------------------------------------------------------------*/
/* Libtool static name colision resolution... */
/* Note: lt_dlopen will look for symbol & modulename_LTX_symbol */
/*-------------------------------------------------------------------*/
#if !defined(HDL_BUILD_SHARED) && defined(HDL_USE_LIBTOOL)
#define hdl_ddev hdt3420_LTX_hdl_ddev
#define hdl_depc hdt3420_LTX_hdl_depc
#define hdl_reso hdt3420_LTX_hdl_reso
#define hdl_init hdt3420_LTX_hdl_init
#define hdl_fini hdt3420_LTX_hdl_fini
#endif
/*-------------------------------------------------------------------*/
#if defined(OPTION_DYNAMIC_LOAD)
HDL_DEPENDENCY_SECTION;
{
HDL_DEPENDENCY ( HERCULES );
HDL_DEPENDENCY ( DEVBLK );
HDL_DEPENDENCY ( SYSBLK );
}
END_DEPENDENCY_SECTION
/*-------------------------------------------------------------------*/
HDL_DEVICE_SECTION;
{
HDL_DEVICE ( 3410, tapedev_device_hndinfo );
HDL_DEVICE ( 3411, tapedev_device_hndinfo );
HDL_DEVICE ( 3420, tapedev_device_hndinfo );
HDL_DEVICE ( 3422, tapedev_device_hndinfo );
HDL_DEVICE ( 3430, tapedev_device_hndinfo );
HDL_DEVICE ( 3480, tapedev_device_hndinfo );
HDL_DEVICE ( 3490, tapedev_device_hndinfo );
HDL_DEVICE ( 3590, tapedev_device_hndinfo );
HDL_DEVICE ( 8809, tapedev_device_hndinfo );
HDL_DEVICE ( 9347, tapedev_device_hndinfo );
HDL_DEVICE ( 9348, tapedev_device_hndinfo );
}
END_DEVICE_SECTION
/*-------------------------------------------------------------------*/
#if defined(WIN32) && !defined(HDL_USE_LIBTOOL) && !defined(_MSVC_)
#undef sysblk
HDL_RESOLVER_SECTION;
{
HDL_RESOLVE_PTRVAR ( psysblk, sysblk );
}
END_RESOLVER_SECTION
#endif // defined(WIN32) && !defined(HDL_USE_LIBTOOL) && !defined(_MSVC_)
#endif // defined(OPTION_DYNAMIC_LOAD)
/*-------------------------------------------------------------------*/
/* (see 'tapedev.h' for layout of TAPEMEDIA_HANDLER structure) */
/*-------------------------------------------------------------------*/
TAPEMEDIA_HANDLER tmh_aws =
{
&generic_tmhcall,
&open_awstape,
&close_awstape,
&read_awstape,
&write_awstape,
&rewind_awstape,
&bsb_awstape,
&fsb_awstape,
&bsf_awstape,
&fsf_awstape,
&write_awsmark,
&sync_awstape,
&no_operation, // (DSE) ZZ FIXME: not coded yet
&no_operation, // (ERG)
&is_tapeloaded_filename,
&passedeot_awstape,
&readblkid_virtual,
&locateblk_virtual
};
/*-------------------------------------------------------------------*/
TAPEMEDIA_HANDLER tmh_het =
{
&generic_tmhcall,
&open_het,
&close_het,
&read_het,
&write_het,
&rewind_het,
&bsb_het,
&fsb_het,
&bsf_het,
&fsf_het,
&write_hetmark,
&sync_het,
&no_operation, // (DSE) ZZ FIXME: not coded yet
&no_operation, // (ERG)
&is_tapeloaded_filename,
&passedeot_het,
&readblkid_virtual,
&locateblk_virtual
};
/*-------------------------------------------------------------------*/
TAPEMEDIA_HANDLER tmh_fake =
{
&generic_tmhcall,
&open_faketape,
&close_faketape,
&read_faketape,
&write_faketape,
&rewind_faketape,
&bsb_faketape,
&fsb_faketape,
&bsf_faketape,
&fsf_faketape,
&write_fakemark,
&sync_faketape,
&no_operation, // (DSE) ZZ FIXME: not coded yet
&no_operation, // (ERG)
&is_tapeloaded_filename,
&passedeot_faketape,
&readblkid_virtual,
&locateblk_virtual
};
/*-------------------------------------------------------------------*/
TAPEMEDIA_HANDLER tmh_oma =
{
&generic_tmhcall,
&open_omatape,
&close_omatape,
&read_omatape,
&write_READONLY5, // WRITE
&rewind_omatape,
&bsb_omatape,
&fsb_omatape,
&bsf_omatape,
&fsf_omatape,
&write_READONLY, // WTM
&write_READONLY, // SYNC
&write_READONLY, // DSE
&write_READONLY, // ERG
&is_tapeloaded_filename,
&return_false1, // passedeot
&readblkid_virtual,
&locateblk_virtual
};
/*-------------------------------------------------------------------*/
#if defined(OPTION_SCSI_TAPE)
TAPEMEDIA_HANDLER tmh_scsi =
{
&generic_tmhcall,
&open_scsitape,
&close_scsitape,
&read_scsitape,
&write_scsitape,
&rewind_scsitape,
&bsb_scsitape,
&fsb_scsitape,
&bsf_scsitape,
&fsf_scsitape,
&write_scsimark,
&sync_scsitape,
&dse_scsitape,
&erg_scsitape,
&is_tape_mounted_scsitape,
&passedeot_scsitape,
&readblkid_scsitape,
&locateblk_scsitape
};
#endif /* defined(OPTION_SCSI_TAPE) */
/*-------------------------------------------------------------------*/
/* Device-Type Initialization Table (DEV/CU MODEL, FEATURES, ETC) */
/*-------------------------------------------------------------------*/
/*
PROGRAMMING NOTE: the MDR/OBR code (Device Characteristics bytes
40-41) are apparently CRITICALLY IMPORTANT for proper tape drive
functioning for certain operating systems. If the bytes are not
provided (set to zero) or are set incorrectly, certain operating
systems end up using unusual/undesirable Mode Set values in their
Channel Programs (such as x'20' Write Immediate for example). I
only note it here because these two particular bytes are rather
innocuous looking based upon their name and sparsely documented
and largely unexplained values, thereby possibly misleading one
into believing they weren't important and thus could be safely
set to zero if their values were unknown. Rest assured they are
NOT unimportant! Quite the opposite: the are, for some operating
systems, CRITICALLY IMPORTANT and must NOT be returned as zeros.
The following were obtained from "EREP Release 3.5.0 Reference"
(GC35-0152-03):
Model MDR OBR
------- ----- -----
3480 0x41 0x80
3490 0x42 0x81
3590 0x46 0x83
3590 (3591/3490 EMU) 0x47 0x84
3590 (3590/3490 EMU) 0x48 0x85
NOTE: only models 3480, 3490 and 3590 support the RDC (Read
Device Characteristics) channel command, and thus they're the
only ones we must know the MDR/OBR codes for (since the MDR/OBR
codes are only used in the RDC CCW and not anwhere else). That
is to say, NONE of the Channel Commands (CCWs) that all the
OTHER models happen to support have an MDR/OBR code anywhere
in their data. Only models 3480, 3490 and 3590 have MDR/OBR
codes buried in their CCW data (specifically the RDC CCW data).
Also note that, at the moment, we do not support emulating 3590's
or 3591's running in 3490 Emulation Mode (i.e. 3591/3490 EMU or
3590/3490 EMU). The user is free to use such a device with Herc-
ules however, but if they do, it should be specified as a 3490.
---------------------------------------------------------------------*/
typedef struct DEVINITTAB /* Initialization values */
{
U16 devtype; /* Device type */
BYTE devmodel; /* Device model number */
U16 cutype; /* Control unit type */
BYTE cumodel; /* Control unit model number */
U32 sctlfeat; /* Storage control features */
BYTE devclass; /* Device class code */
BYTE devtcode; /* Device type code */
BYTE MDR; /* Misc. Data Record ID */
BYTE OBR; /* Outboard Recorder ID */
int numdevid; /* #of SNSID bytes (see NOTE)*/
int numsense; /* #of SENSE bytes */
int haverdc; /* RDC Supported */
int displayfeat; /* Has LCD display */
}
DEVINITTAB;
DEVINITTAB DevInitTab[] = /* Initialization table */
{
// PROGRAMMING NOTE: we currently do not support a #of Sense-ID bytes
// value (numdevid) greater than 7 since our current channel-subsystem
// design does not support the concept of hardware physical attachment
// "Nodes" (i.e. separate control-unit, device and interface elements).
// Supporting more than 7 bytes of Sense-ID information would require
// support for Node Descriptors (ND) and Node Element Descriptors (NED)
// and the associated commands (CCWs) to query them (Read Configuration
// Data (0xFA) , Set Interface Identifier (0x73) and associated support
// in the Read Subsystem Data (0x3E) command), which is vast overkill
// and a complete waste of time given our current overly-simple channel
// subsystem design.
//
//--------------------------------------------------------------------
// 3410/3411/3420/3422/3430/8809/9347/9348
//--------------------------------------------------------------------
//
// devtype/mod cutype/mod sctlfeat cls typ MDR OBR sid sns rdc dsp
{ 0x3410,0x01, 0x3115,0x01, 0x00000000, 0, 0, 0, 0, 0, 9, 0, 0 },
{ 0x3411,0x01, 0x3115,0x01, 0x00000000, 0, 0, 0, 0, 0, 9, 0, 0 },
{ 0x3420,0x06, 0x3803,0x02, 0x00000000, 0, 0, 0, 0, 0, 24, 0, 0 }, // (DEFAULT: 3420)
{ 0x3422,0x01, 0x3422,0x01, 0x00000000, 0, 0, 0, 0, 7, 32, 0, 0 },
{ 0x3430,0x01, 0x3422,0x01, 0x00000000, 0, 0, 0, 0, 7, 32, 0, 0 },
{ 0x8809,0x01, 0x8809,0x01, 0x00000000, 0, 0, 0, 0, 0, 32, 0, 0 },
{ 0x9347,0x01, 0x9347,0x01, 0x00000000, 0, 0, 0, 0, 7, 32, 0, 0 },
{ 0x9348,0x01, 0x9348,0x01, 0x00000000, 0, 0, 0, 0, 7, 32, 0, 0 },
//--------------------------------------------------------------------
// 3480/3490/3590
//--------------------------------------------------------------------
//
// PROGRAMMING NOTE: we currently do not support a #of Sense-ID bytes
// value (numdevid) greater than 7 since our current channel-subsystem
// design does not support the concept of hardware physical attachment
// "Nodes" (i.e. separate control-unit, device and interface elements).
// Supporting more than 7 bytes of Sense-ID information would require
// support for Node Descriptors (ND) and Node Element Descriptors (NED)
// and the associated commands (CCWs) to query them (Read Configuration
// Data (0xFA) , Set Interface Identifier (0x73) and associated support
// in the Read Subsystem Data (0x3E) command), which is vast overkill
// and a complete waste of time given our current overly-simple channel
// subsystem design.
//
// PROGRAMMING NOTE: if you change the below devtype/mod or cutype/mod
// values, be sure to ALSO change tapeccws.c's READ CONFIGURATION DATA
// (CCW opcode 0xFA) values as well!
//
// PROGRAMMING NOTE: the bit values of the 'sctlfeat' field are:
//
// RDC Byte
// 6.7.8.9.
// ..80.... RBL Format-30 media information available
// ..40.... (unknown)
// ..20.... (unknown)
// ....80.. Device or media emulation active; emulated media
// information available in RBL Format-30 data. Set to
// zero when normal 3490E or earlier, or real device.
// ....40.. (unknown)
// ....20.. Set when bits 0-9 of the Block ID are zero for Locate
// Block on select device types
// ....10.. (unknown)
// ....08.. Set Special Intercept Condition (SIC) supported
// ....04.. Channel Path No-Operation supported (always
// on if Library Attachment Facility installed)
// ....02.. Logical Write-Protect supported (always on
// if Read Device Characteristics is supported)
// ....01.. Extended Buffered Log support enabled (if 64
// bytes of buffered log data, else 32 bytes)
// ......80 Automatic Cartridge Loader installed/enabled
// ......40 Improved Data Recording Capability (compression
// support) installed/enabled
// ......20 Suppress Volume Fencing
// ......10 Library Interface online/enabled
// ......08 Library Attachment Facility installed
// ......04 (unknown)
// ......02 (unknown)
// ......01 (unknown)
//
// PROGRAMMING NOTE: the below "0x00004EC4" value for the 'sctlfeat'
// field for Model 3590 was determined empirically on a real machine.
//
// devtype/mod cutype/mod sctlfeat cls typ MDR OBR sid sns rdc dsp
{ 0x3480,0x31, 0x3480,0x31, 0x000002C0, 0x80,0x80, 0x41,0x80, 7, 24, 1, 1 }, // 0x31 = D31
{ 0x3490,0x50, 0x3490,0x50, 0x000002C0, 0x80,0x80, 0x42,0x81, 7, 32, 1, 1 }, // 0x50 = C10
{ 0x3590,0x10, 0x3590,0x50, 0x00004EC4, 0x80,0x80, 0x46,0x83, 7, 32, 1, 1 }, // 0x10 = B1A, 0x50 = A50
{ 0xFFFF,0xFF, 0xFFFF,0xFF, 0xFFFFFFFF, 0xFF,0xFF, 0xFF,0xFF, -1, -1, -1, -1 }, //**** END OF TABLE ****
{ 0x3420,0x06, 0x3803,0x02, 0x00000000, 0, 0, 0, 0, 0, 24, 0, 0 }, // (DEFAULT: 3420)
};
/*-------------------------------------------------------------------*/
/* Initialize the device handler */
/*-------------------------------------------------------------------*/
static int tapedev_init_handler (DEVBLK *dev, int argc, char *argv[])
{
int rc;
DEVINITTAB* pDevInitTab;
int attn = 0;
/* Set flag so attention will be raised for re-init */
if(dev->devtype)
{
attn = 1;
}
/* Close current tape */
if(dev->fd>=0)
{
/* Prevent accidental re-init'ing of already loaded tape drives */
if (sysblk.nomountedtapereinit)
{
char* devclass;
tapedev_query_device(dev, &devclass, 0, NULL);
if (1
&& strcmp(devclass,"TAPE") == 0
&& (0
|| TAPEDEVT_SCSITAPE == dev->tapedevt
|| (argc >= 3 && strcmp(argv[2], TAPE_UNLOADED) != 0)
)
)
{
ASSERT( dev->tmh && dev->tmh->tapeloaded );
if (dev->tmh->tapeloaded( dev, NULL, 0 ))
{
release_lock (&dev->lock);
WRMSG(HHC02243, "E", SSID_TO_LCSS(dev->ssid), dev->devnum);
return -1;
}
}
}
dev->tmh->close(dev);
dev->fd=-1;
}
autoload_close(dev);
dev->tdparms.displayfeat=0;
/* reset excps count */
dev->excps = 0;
/* Determine the control unit type and model number */
/* Support for 3490/3422/3430/8809/9347, etc.. */
if (!sscanf( dev->typname, "%hx", &dev->devtype ))
dev->devtype = 0x3420;
// PROGAMMING NOTE: we use hard-coded values from our DevInitTab
// for virtual (non-SCSI) devices and, for the time being, for non-
// virtual (SCSI) devices too. Once we add direct SCSI I/O support
// we will need to add code to get this information directly from
// the actual SCSI device itself.
for
(
pDevInitTab = &DevInitTab[0];
pDevInitTab->devtype != 0xFFFF && pDevInitTab->devtype != dev->devtype;
pDevInitTab++
);
if (pDevInitTab->devtype == 0xFFFF) /* (entry not found?) */
{
// "Unsupported tape device type '%04X' specified"
WRMSG(HHC00225, "E", dev->devtype );
pDevInitTab++; /* (default entry; s/b same as 0x3420) */
pDevInitTab->devtype = dev->devtype; /* (don't know what else to do really) */
pDevInitTab->cutype = dev->devtype; /* (don't know what else to do really) */
}
/* Allow SENSE ID for certain specific legacy devices if requested */
dev->numdevid = pDevInitTab->numdevid; // (default == from table)
if (1
&& sysblk.legacysenseid // (if option requested, AND is)
&& (0 // (for allowable legacy device)
|| 0x3410 == dev->devtype
|| 0x3411 == dev->devtype
|| 0x3420 == dev->devtype
|| 0x8809 == dev->devtype
)
)
{
dev->numdevid = 7; // (allow for this legacy device)
}
/* Initialize the Sense-Id bytes if needed... */
if (dev->numdevid > 0)
{
dev->devid[0] = 0xFF;
dev->devid[1] = (pDevInitTab->cutype >> 8) & 0xFF;
dev->devid[2] = (pDevInitTab->cutype >> 0) & 0xFF;
dev->devid[3] = pDevInitTab->cumodel;
dev->devid[4] = (pDevInitTab->devtype >> 8) & 0xFF;
dev->devid[5] = (pDevInitTab->devtype >> 0) & 0xFF;
dev->devid[6] = pDevInitTab->devmodel;
/* Initialize the CIW information if needed... */
if (dev->numdevid > 7)
{
// PROGRAMMING NOTE: see note near 'DEVINITTAB'
// struct definition regarding requirements for
// supporting more than 7 bytes of SNSID info.
memcpy (&dev->devid[8], "\x40\xFA\x00\xA0", 4); // CIW Read Configuration Data (0xFA)
memcpy (&dev->devid[12], "\x41\x73\x00\x04", 4); // CIW Set Interface Identifier (0x73)
memcpy (&dev->devid[16], "\x42\x3E\x00\x60", 4); // CIW Read Subsystem Data (0x3E)
}
}
/* Initialize the Read Device Characteristics (RDC) bytes... */
if (pDevInitTab->haverdc)
{
dev->numdevchar = 64;
memset (dev->devchar, 0, sizeof(dev->devchar));
memcpy (dev->devchar, dev->devid+1, 6);
// Bytes 6-9: Subsystem Facilities...
dev->devchar[6] = (pDevInitTab->sctlfeat >> 24) & 0xFF;
dev->devchar[7] = (pDevInitTab->sctlfeat >> 16) & 0xFF;
dev->devchar[8] = (pDevInitTab->sctlfeat >> 8) & 0xFF;
dev->devchar[9] = (pDevInitTab->sctlfeat >> 0) & 0xFF;
// Bytes 10/11: Device Class/Type ...
dev->devchar[10] = pDevInitTab->devclass;
dev->devchar[11] = pDevInitTab->devtcode;
// Bytes 24-29: cutype/model & devtype/model ...
// (Note: undocumented; determined empirically)
dev->devchar[24] = (pDevInitTab->cutype >> 8) & 0xFF;
dev->devchar[25] = (pDevInitTab->cutype >> 0) & 0xFF;
dev->devchar[26] = pDevInitTab->cumodel;
dev->devchar[27] = (pDevInitTab->devtype >> 8) & 0xFF;
dev->devchar[28] = (pDevInitTab->devtype >> 0) & 0xFF;
dev->devchar[29] = pDevInitTab->devmodel;
// Bytes 40-41: MDR/OBR code...
dev->devchar[40] = pDevInitTab->MDR;
dev->devchar[41] = pDevInitTab->OBR;
}
/* Initialize other fields */
// dev->numdevid = pDevInitTab->numdevid; // (handled above)
dev->numsense = pDevInitTab->numsense;
dev->tdparms.displayfeat = pDevInitTab->displayfeat;
dev->fenced = 0; // (always, initially)
dev->SIC_active = 0; // (always, initially)
dev->SIC_supported = 0; // (until we're sure)
dev->forced_logging = 0; // (always, initially)
dev->noautomount = 0; // (always, initially)
/* Initialize SCSI tape control fields */
#if defined(OPTION_SCSI_TAPE)
dev->sstat = GMT_DR_OPEN(-1);
#endif
/* Clear the DPA */
memset (dev->pgid, 0, sizeof(dev->pgid));
/* Clear Drive password - Adrian */
memset (dev->drvpwd, 0, sizeof(dev->drvpwd));
/* Request the channel to merge data chained write CCWs into
a single buffer before passing data to the device handler */
dev->cdwmerge = 1;
/* Request a maximum sized device I/O buffer */
dev->bufsize = MAX_BLKLEN;
/* ISW */
/* Build a 'clear' sense */
memset (dev->sense, 0, sizeof(dev->sense));
dev->sns_pending = 0;
// Initialize the [non-SCSI] auto-loader...
// PROGRAMMING NOTE: we don't [yet] know at this early stage
// what type of tape device we're dealing with (SCSI (non-virtual)
// or non-SCSI (virtual)) since 'mountnewtape' hasn't been called
// yet (which is the function that determines which media handler
// should be used and is the one that initializes dev->tapedevt)
// The only thing we know (or WILL know once 'autoload_init'
// is called) is whether or not there was a [non-SCSI] auto-
// loader defined for the device. That's it and nothing more.
autoload_init( dev, argc, argv );
// Was an auto-loader defined for this device?
if ( !dev->als )
{
// No. Just mount whatever tape there is (if any)...
rc = mountnewtape( dev, argc, argv );
}
else
{
// Yes. Try mounting the FIRST auto-loader slot...
if ( (rc = autoload_mount_first( dev )) != 0 )
{
// If that doesn't work, try subsequent slots...
while
(
dev->als
&&
(rc = autoload_mount_next( dev )) != 0
)
{
; // (nop; just go on to next slot)
}
rc = dev->als ? rc : -1;
}
}
if (dev->devchar[8] & 0x08) // SIC supported?
dev->SIC_supported = 1; // remember that fact
#ifdef OPTION_SYNCIO
if (dev->tapedevt == TAPEDEVT_SCSITAPE)
dev->syncio = 0; // (SCSI i/o too slow; causes Machine checks)
else
dev->syncio = 2; // (aws/het/etc are fast; syncio likely safe)
#endif // OPTION_SYNCIO
/* Make attention pending if necessary */
if(attn)
{
release_lock (&dev->lock);
device_attention (dev, CSW_DE);
obtain_lock (&dev->lock);
}
return rc;
} /* end function tapedev_init_handler */
/*-------------------------------------------------------------------*/
/* Close the device */
/*-------------------------------------------------------------------*/
static int tapedev_close_device ( DEVBLK *dev )
{
autoload_close(dev);
dev->tmh->close(dev);
ASSERT( dev->fd < 0 );
dev->curfilen = 1;
dev->nxtblkpos = 0;
dev->prvblkpos = -1;
dev->curblkrem = 0;
dev->curbufoff = 0;
dev->blockid = 0;
dev->fenced = 0;
return 0;
} /* end function tapedev_close_device */
/*-------------------------------------------------------------------*/
/* Tape format determination REGEXPS. Used by gettapetype below */
/*-------------------------------------------------------------------*/
struct tape_format_entry /* (table layout) */
{
char* fmtreg; /* A regular expression */
int fmtcode; /* the device code */
TAPEMEDIA_HANDLER* tmh; /* The media dispatcher */
char* descr; /* readable description */
char* short_descr; /* (same but shorter) */
};
/*-------------------------------------------------------------------*/
/* Tape format determination REGEXPS. Used by gettapetype below */
/*-------------------------------------------------------------------*/
struct tape_format_entry fmttab [] = /* (table itself) */
{
/* This entry matches a filename ending with .aws */
#define AWSTAPE_FMTENTRY 0
#define DEFAULT_FMTENTRY AWSTAPE_FMTENTRY
{
"\\.aws$",
TAPEDEVT_AWSTAPE,
&tmh_aws,
"AWS Format tape file",
"AWS tape"
},
/* This entry matches a filename ending with .het */
#define HETTAPE_FMTENTRY 1
{
"\\.het$",
TAPEDEVT_HETTAPE,
&tmh_het,
"Hercules Emulated Tape file",
"HET tape"
},
/* This entry matches a filename ending with .tdf */
#define OMATAPE_FMTENTRY 2
{
"\\.tdf$",
TAPEDEVT_OMATAPE,
&tmh_oma,
"Optical Media Attachment (OMA) tape",
"OMA tape"
},
/* This entry matches a filename ending with .fkt */
#define FAKETAPE_FMTENTRY 3
{
"\\.fkt$",
TAPEDEVT_FAKETAPE,
&tmh_fake,
"Flex FakeTape file",
"FakeTape"
},
#if defined(OPTION_SCSI_TAPE)
/* This entry matches a filename starting with /dev/ */
#define SCSITAPE_FMTENTRY 4
{
"^/dev/",
TAPEDEVT_SCSITAPE,
&tmh_scsi,
"SCSI attached tape drive",
"SCSI tape"
},
#if defined(_MSVC_)
/* (same idea but for Windows SCSI tape device names) */
#undef SCSITAPE_FMTENTRY
#define SCSITAPE_FMTENTRY 5
{
"^((\\\\\\\\\\.\\\\)|(//\\./))\\w",
TAPEDEVT_SCSITAPE,
&tmh_scsi,
"SCSI attached tape drive",
"SCSI tape"
},
#endif // _MSVC_
#endif // OPTION_SCSI_TAPE
};
/*-------------------------------------------------------------------*/
/* gettapetype_byname determine tape device type by filename */
/*-------------------------------------------------------------------*/
/* returns fmttab entry# on success, -1 on error/unable to determine */
/*-------------------------------------------------------------------*/
int gettapetype_byname (DEVBLK *dev)
{
#if defined(HAVE_REGEX_H) || defined(HAVE_PCRE)
regex_t regwrk; /* REGEXP work area */
regmatch_t regwrk2; /* REGEXP match area */
char errbfr[1024]; /* Working storage */
int i; /* Loop control */
#endif // HAVE_REGEX_H
int rc; /* various rtns return codes */
/* Use the file name to determine the device type */
#if defined(HAVE_REGEX_H) || defined(HAVE_PCRE)
for (i=0; i < (int)arraysize( fmttab ); i++)
{
rc = regcomp (&regwrk, fmttab[i].fmtreg, REG_ICASE);
if (rc < 0)
{
regerror (rc, &regwrk, errbfr, 1024);
// "%1d:%04X Tape file '%s', type '%s': error in function '%s': '%s'"
WRMSG(HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, TTYPSTR(dev->tapedevt), "regcomp()", errbfr);
return -1;
}
rc = regexec (&regwrk, dev->filename, 1, &regwrk2, 0);
if (rc < 0)
{
regerror (rc, &regwrk, errbfr, 1024);
regfree ( &regwrk );
// "%1d:%04X Tape file '%s', type '%s': error in function '%s': '%s'"
WRMSG(HHC00205, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, TTYPSTR(dev->tapedevt), "regexec()", errbfr);
return -1;
}
regfree (&regwrk);
if (rc == 0) /* MATCH? */
return i;
ASSERT( rc == REG_NOMATCH );
}
#else // !HAVE_REGEX_H
if (1
&& (rc = strlen(dev->filename)) > 4
&& (rc = strcasecmp( &dev->filename[rc-4], ".aws" )) == 0
)
{
return AWSTAPE_FMTENTRY;
}
if (1
&& (rc = strlen(dev->filename)) > 4
&& (rc = strcasecmp( &dev->filename[rc-4], ".het" )) == 0
)
{
return HETTAPE_FMTENTRY;
}
if (1
&& (rc = strlen(dev->filename)) > 4
&& (rc = strcasecmp( &dev->filename[rc-4], ".tdf" )) == 0
)
{
return OMATAPE_FMTENTRY;
}
if (1
&& (rc = strlen(dev->filename)) > 4
&& (rc = strcasecmp( &dev->filename[rc-4], ".fkt" )) == 0
)
{
return FAKETAPE_FMTENTRY;
}
#if defined(OPTION_SCSI_TAPE)
if (1
&& (rc = strlen(dev->filename)) > 5
&& (rc = strncasecmp( dev->filename, "/dev/", 5 )) == 0
)
{
if (strncasecmp( dev->filename+5, "st", 2 ) == 0)
dev->stape_close_rewinds = 1; // (rewind at close)
else
dev->stape_close_rewinds = 0; // (otherwise don't)
return SCSITAPE_FMTENTRY;
}
#if defined(_MSVC_)
if (1
&& strncasecmp(dev->filename, "\\\\.\\", 4) == 0
&& *(dev->filename + 4) != 0
)
{
return SCSITAPE_FMTENTRY;
}
#endif // _MSVC_
#endif // OPTION_SCSI_TAPE
#endif // HAVE_REGEX_H
return -1; /* -1 == "unable to determine" */
} /* end function gettapetype_byname */
/*-------------------------------------------------------------------*/
/* gettapetype_bydata determine tape device type by file data */
/*-------------------------------------------------------------------*/
/* returns fmttab entry# on success, -1 on error/unable to determine */
/*-------------------------------------------------------------------*/
int gettapetype_bydata (DEVBLK *dev)
{
char pathname[MAX_PATH]; /* file path in host format */
int rc; /* various rtns return codes */
/* Try to determine the type based on actual file contents */
hostpath( pathname, dev->filename, sizeof(pathname) );
rc = HOPEN ( pathname, O_RDONLY | O_BINARY );
if (rc >= 0)
{
BYTE hdr[6]; /* block header i/o buffer */
int fd = rc; /* save file descriptor */
/* Read the header. If bytes 0-3 are ASCII "0000", then the
* tape is likely a Flex FakeTape. Otherwise if bytes 2-3 are
* binary zero (x'0000'), it's likely an AWS type tape. If byte
* 4 (first flag byte) has either of the ZLIB or BZIP2 flags on,
* then it's a HET tape. Otherwise it's just an ordinary AWS tape.
*/
rc = read (fd, hdr, sizeof(hdr));
close(fd);
if (rc >= 6)
{
/* Use the data to make the possible determination */
if (memcmp(hdr, "@TDF", 4) == 0)
return OMATAPE_FMTENTRY;
if (1
&& hdr[0] == 0x30 /* "ASCII"-zero len prev block? */
&& hdr[1] == 0x30
&& hdr[2] == 0x30
&& hdr[3] == 0x30
)
return FAKETAPE_FMTENTRY; /* Then obviously Flex FakeTape */
if (hdr[2] == 0 && hdr[3] == 0) /* 0 len prev blk? */
{
if (hdr[4] & HETHDR_FLAGS1_TAPEMARK) /* If tapemark then */
return -1; /* can't tell type. */
if (hdr[4] & HETHDR_FLAGS1_COMPRESS || /* ZLIB or BZIP2 or */
hdr[5] & HETHDR_FLAGS2_COMPRESS) /* Bus-Tech ZLIB? */
return HETTAPE_FMTENTRY; /* Then HET format. */
else
return AWSTAPE_FMTENTRY; /* Else default AWS */
}
}
}
return -1; /* -1 == "unable to determine" */
} /* end function gettapetype_bydata */
/*-------------------------------------------------------------------*/
/* gettapetype determine tape device type */
/*-------------------------------------------------------------------*/
/* returns fmttab entry# on success, -1 on error/unable to determine */
/*-------------------------------------------------------------------*/
int gettapetype (DEVBLK *dev, char **short_descr)
{
char* descr; /* Device descr from fmttab */
int i; /* fmttab entry# */
i = gettapetype_byname( dev ); /* Get type based on name */
#if defined(OPTION_SCSI_TAPE)
if (i != SCSITAPE_FMTENTRY) /* If not SCSI tape... */
#endif
{
if ( !strcasecmp( dev->filename, TAPE_UNLOADED ) )
{
i = DEFAULT_FMTENTRY;
}
else
{
int i2 = gettapetype_bydata( dev ); // Get type based on data..
if (i2 >= 0 && // If valid type by data, AND
( i2 != AWSTAPE_FMTENTRY || // *not* AWS by data (or if it
i != HETTAPE_FMTENTRY ) // is, if it's not HET by name)..
)
i = i2; // ..Use type based on data.
}
}
/* If file type still unknown, use a reasonable default value... */
if (i < 0)
{
i = DEFAULT_FMTENTRY;
if (strcmp (dev->filename, TAPE_UNLOADED) != 0)
{
// "%1d:%04X Tape file '%s', type '%s': format type is not determinable, presumed '%s'"
WRMSG(HHC00220, "W", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, TTYPSTR(dev->tapedevt), fmttab[i].short_descr );
}
}
dev->tapedevt = fmttab[i].fmtcode;
dev->tmh = fmttab[i].tmh;
descr = fmttab[i].descr;
*short_descr = fmttab[i].short_descr;
if (strcmp (dev->filename, TAPE_UNLOADED) != 0)
{
// "%1d:%04X Tape file '%s', type '%s': format type '%s'"
WRMSG(HHC00221, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, TTYPSTR(dev->tapedevt), descr);
}
return 0; // (success)
} /* end function gettapetype */
/*-------------------------------------------------------------------*/
/* The following table goes hand-in-hand with the 'enum' values */
/* that immediately follow. Used by 'mountnewtape' function. */
/*-------------------------------------------------------------------*/
PARSER ptab [] =
{
{ "awstape", NULL },
{ "idrc", "%d" },
{ "compress", "%d" },
{ "method", "%d" },
{ "level", "%d" },
{ "chunksize", "%d" },
{ "maxsize", "%s" },
{ "maxsizeK", "%d" },
{ "maxsizeM", "%d" },
{ "eotmargin", "%s" },
{ "strictsize", "%d" },
{ "readonly", "%d" },
{ "ro", NULL },
{ "noring", NULL },
{ "rw", NULL },
{ "ring", NULL },
{ "deonirq", "%d" },
{ "noautomount",NULL },
{ "--blkid-22", NULL },
{ "--blkid-24", NULL }, /* (synonym for --blkid-22) */
{ "--blkid-32", NULL },
{ "--no-erg", NULL },
{ NULL, NULL }, /* (end of table) */
};
/*-------------------------------------------------------------------*/
/* The following table goes hand-in-hand with the 'ptab' PARSER */
/* table immediately above. Used by 'mountnewtape' function. */
/*-------------------------------------------------------------------*/
enum
{
TDPARM_NONE,
TDPARM_AWSTAPE,
TDPARM_IDRC,
TDPARM_COMPRESS,
TDPARM_METHOD,
TDPARM_LEVEL,
TDPARM_CHKSIZE,
TDPARM_MAXSIZE,
TDPARM_MAXSIZEK,
TDPARM_MAXSIZEM,
TDPARM_EOTMARGIN,
TDPARM_STRICTSIZE,
TDPARM_READONLY,
TDPARM_RO,
TDPARM_NORING,
TDPARM_RW,
TDPARM_RING,
TDPARM_DEONIRQ,
TDPARM_NOAUTOMOUNT,
TDPARM_BLKID22,
TDPARM_BLKID24,
TDPARM_BLKID32,
TDPARM_NOERG
};
/*-------------------------------------------------------------------*/
/* mountnewtape -- mount a tape in the drive */
/*-------------------------------------------------------------------*/
/* */
/* Syntax: filename [options] */
/* */
/* where options are any of the entries in the 'ptab' PARSER */
/* table defined further above. Some commonly used options are: */
/* */
/* awstape sets the HET parms to be compatible with the */
/* R|P/390|'s tape file Format (HET files) */
/* */
/* idrc|compress 0|1: Write tape blocks with compression */
/* (std deviation: Read backward allowed on */
/* compressed HET tapes while it is not on */
/* IDRC formated 3480 tapes) */
/* */
/* --no-erg for SCSI tape only, means the hardware does */
/* not support the "Erase Gap" command and all */
/* such i/o's should return 'success' instead. */
/* */
/* --blkid-32 for SCSI tape only, means the hardware */
/* only supports full 32-bit block-ids. */
/* */
/*-------------------------------------------------------------------*/
int mountnewtape ( DEVBLK *dev, int argc, char **argv )
{
char* short_descr; /* Short descr from fmttab */
char msg[80];
int i; /* Loop control */
int rc, optrc; /* various rtns return codes */
union { /* Parser results */
U32 num; /* Parser results */
BYTE str[ 80 ]; /* Parser results */
} res; /* Parser results */
/* Release the previous OMA descriptor array if allocated */
if (dev->omadesc != NULL)
{
free (dev->omadesc);
dev->omadesc = NULL;
}
/* The first argument is the file name */
if (argc == 0 || strlen(argv[0]) >= sizeof(dev->filename))
strlcpy( dev->filename, TAPE_UNLOADED, sizeof(dev->filename) );
else
{
/* Save the file name in the device block */
hostpath(dev->filename, argv[0], sizeof(dev->filename));
}
/* Determine tape device type... */
VERIFY( gettapetype( dev, &short_descr ) == 0 );
/* (sanity check) */
ASSERT(dev->tapedevt != TAPEDEVT_UNKNOWN);
ASSERT(dev->tmh != NULL);
ASSERT(short_descr != NULL);
MSGBUF(msg, "not valid for %s", short_descr);
/* Initialize device dependent fields */
dev->fd = -1;
#if defined(OPTION_SCSI_TAPE)
dev->sstat = GMT_DR_OPEN(-1);
#endif
dev->omadesc = NULL;
dev->omafiles = 0;
dev->curfilen = 1;
dev->nxtblkpos = 0;
dev->prvblkpos = -1;
dev->curblkrem = 0;
dev->curbufoff = 0;
dev->readonly = 0;
dev->hetb = NULL;
dev->tdparms.compress = HETDFLT_COMPRESS;
dev->tdparms.method = HETDFLT_METHOD;
dev->tdparms.level = HETDFLT_LEVEL;
dev->tdparms.chksize = HETDFLT_CHKSIZE;
dev->tdparms.maxsize = 0; // no max size (default)
dev->eotmargin = 128*1024; // 128K EOT margin (default)
dev->tdparms.logical_readonly = 0; // read/write (default)
dev->noautomount = 0;
#if defined(OPTION_SCSI_TAPE)
// Real 3590's support Erase Gap and use 32-bit blockids.
if (TAPEDEVT_SCSITAPE == dev->tapedevt
&& 0x3590 == dev->devtype)
{
dev->stape_no_erg = 0; // (default for 3590 SCSI)
dev->stape_blkid_32 = 1; // (default for 3590 SCSI)
}
#endif
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
#define _HHC00223E() WRMSG(HHC00223, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, TTYPSTR(dev->tapedevt), argv[i], msg)
/* Process remaining options */
rc = 0;
for (i = 1; i < argc; i++)
{
optrc = 0;
switch (parser (&ptab[0], argv[i], &res))
{
case TDPARM_NONE:
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
WRMSG(HHC00223, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, TTYPSTR(dev->tapedevt), argv[i], "unrecognized");
optrc = -1;
break;
case TDPARM_AWSTAPE:
if (0
|| TAPEDEVT_SCSITAPE == dev->tapedevt
|| TAPEDEVT_FAKETAPE == dev->tapedevt
)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
dev->tdparms.compress = FALSE;
dev->tdparms.chksize = 4096;
break;
case TDPARM_IDRC:
case TDPARM_COMPRESS:
if (0
|| TAPEDEVT_SCSITAPE == dev->tapedevt
|| TAPEDEVT_FAKETAPE == dev->tapedevt
)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
dev->tdparms.compress = (res.num ? TRUE : FALSE);
break;
case TDPARM_METHOD:
if (0
|| TAPEDEVT_SCSITAPE == dev->tapedevt
|| TAPEDEVT_FAKETAPE == dev->tapedevt
)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
if (res.num < HETMIN_METHOD || res.num > HETMAX_METHOD)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
WRMSG(HHC00223, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, TTYPSTR(dev->tapedevt), argv[i], "method out of range");
optrc = -1;
break;
}
dev->tdparms.method = res.num;
break;
case TDPARM_LEVEL:
if (0
|| TAPEDEVT_SCSITAPE == dev->tapedevt
|| TAPEDEVT_FAKETAPE == dev->tapedevt
)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
if (res.num < HETMIN_LEVEL || res.num > HETMAX_LEVEL)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
WRMSG(HHC00223, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, TTYPSTR(dev->tapedevt), argv[i], "level out of range");
optrc = -1;
break;
}
dev->tdparms.level = res.num;
break;
case TDPARM_CHKSIZE:
if (0
|| TAPEDEVT_SCSITAPE == dev->tapedevt
|| TAPEDEVT_FAKETAPE == dev->tapedevt
)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
if (res.num < HETMIN_CHUNKSIZE || res.num > HETMAX_CHUNKSIZE)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
WRMSG(HHC00223, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, TTYPSTR(dev->tapedevt), argv[i], "chunksize out of range");
optrc = -1;
break;
}
dev->tdparms.chksize = res.num;
break;
case TDPARM_MAXSIZE:
if (TAPEDEVT_SCSITAPE == dev->tapedevt)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
else
{
int rc = 0;
U64 maxsize = 0;
BYTE f = '\0';
BYTE c = '\0';
rc = sscanf((const char*)res.str, "%"I64_FMT"u%c%c", &maxsize, &f, &c);
if ( rc < 1 || rc > 2 )
{
WRMSG( HHC01451, "E", res.str, "maxsize" );
optrc = -1;
}
else if ( rc == 2 )
{
switch (toupper(f))
{
case 'K':
maxsize <<= SHIFT_KIBIBYTE;
break;
case 'M':
maxsize <<= SHIFT_MEBIBYTE;
break;
case 'G':
maxsize <<= SHIFT_GIBIBYTE;
break;
#if SIZEOF_SIZE_T >= 8
case 'T':
maxsize <<= SHIFT_TEBIBYTE;
break;
#endif
default:
WRMSG( HHC01451, "E", res.str, "maxsize" );
maxsize = 0;
optrc = -1;
break;
}
if ( optrc != -1 )
dev->tdparms.maxsize = maxsize;
}
else
dev->tdparms.maxsize = maxsize;
}
break;
case TDPARM_MAXSIZEK:
if (TAPEDEVT_SCSITAPE == dev->tapedevt)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
dev->tdparms.maxsize=res.num*1024;
break;
case TDPARM_MAXSIZEM:
if (TAPEDEVT_SCSITAPE == dev->tapedevt)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
dev->tdparms.maxsize=res.num*1024*1024;
break;
case TDPARM_EOTMARGIN:
{
int rc = 0;
U64 eotmargin = 0;
BYTE f = '\0';
BYTE c = '\0';
rc = sscanf((const char*)res.str, "%"I64_FMT"u%c%c", &eotmargin, &f, &c);
if ( rc < 1 || rc > 2 )
{
WRMSG( HHC01451, "E", res.str, "eotmargin" );
optrc = -1;
}
else if ( rc == 2 )
{
switch (toupper(f))
{
case 'K':
eotmargin <<= SHIFT_KIBIBYTE;
break;
case 'M':
eotmargin <<= SHIFT_MEBIBYTE;
break;
case 'G':
eotmargin <<= SHIFT_GIBIBYTE;
break;
#if SIZEOF_SIZE_T >= 8
case 'T':
eotmargin <<= SHIFT_TEBIBYTE;
break;
#endif
default:
WRMSG( HHC01451, "E", res.str, "eotmargin" );
eotmargin = 0;
optrc = -1;
break;
}
if ( optrc != -1 )
dev->eotmargin = eotmargin;
}
else
dev->eotmargin = eotmargin;
}
break;
case TDPARM_STRICTSIZE:
if (TAPEDEVT_SCSITAPE == dev->tapedevt)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
dev->tdparms.strictsize=res.num;
break;
case TDPARM_READONLY:
if (TAPEDEVT_SCSITAPE == dev->tapedevt)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
dev->tdparms.logical_readonly=(res.num ? 1 : 0 );
break;
case TDPARM_RO:
case TDPARM_NORING:
if (TAPEDEVT_SCSITAPE == dev->tapedevt)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
dev->tdparms.logical_readonly=1;
break;
case TDPARM_RW:
case TDPARM_RING:
if (TAPEDEVT_SCSITAPE == dev->tapedevt)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
dev->tdparms.logical_readonly=0;
break;
case TDPARM_DEONIRQ:
if (TAPEDEVT_SCSITAPE == dev->tapedevt)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
dev->tdparms.deonirq=(res.num ? 1 : 0 );
break;
case TDPARM_NOAUTOMOUNT:
if (TAPEDEVT_SCSITAPE == dev->tapedevt)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
dev->noautomount = 1;
break;
#if defined(OPTION_SCSI_TAPE)
case TDPARM_BLKID22:
case TDPARM_BLKID24:
if (TAPEDEVT_SCSITAPE != dev->tapedevt)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
dev->stape_blkid_32 = 0;
break;
case TDPARM_BLKID32:
if (TAPEDEVT_SCSITAPE != dev->tapedevt)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
dev->stape_blkid_32 = 1;
break;
case TDPARM_NOERG:
if (TAPEDEVT_SCSITAPE != dev->tapedevt)
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
_HHC00223E(); optrc = -1; break;
}
dev->stape_no_erg = 1;
break;
#endif /* defined(OPTION_SCSI_TAPE) */
default:
// "%1d:%04X Tape file '%s', type '%s': option '%s' rejected: '%s'"
WRMSG(HHC00223, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, TTYPSTR(dev->tapedevt), argv[i], "parse error");
optrc = -1;
break;
} // end switch (parser (&ptab[0], argv[i], &res))
if (optrc < 0)
rc = -1;
else
{
// "%1d:%04X Tape file '%s', type '%s': option '%s' accepted"
WRMSG(HHC00222, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, TTYPSTR(dev->tapedevt), argv[i]);
}
} // end for (i = 1; i < argc; i++)
if (0 != rc)
return -1;
/* Adjust the display if necessary */
if(dev->tdparms.displayfeat)
{
if(strcmp(dev->filename,TAPE_UNLOADED)==0)
{
/* NO tape is loaded */
if(TAPEDISPTYP_UMOUNTMOUNT == dev->tapedisptype)
{
/* A new tape SHOULD be mounted */
dev->tapedisptype = TAPEDISPTYP_MOUNT;
dev->tapedispflags |= TAPEDISPFLG_REQAUTOMNT;
strlcpy( dev->tapemsg1, dev->tapemsg2, sizeof(dev->tapemsg1) );
}
else if(TAPEDISPTYP_UNMOUNT == dev->tapedisptype)
{
dev->tapedisptype = TAPEDISPTYP_IDLE;
}
}
else
{
/* A tape IS already loaded */
dev->tapedisptype = TAPEDISPTYP_IDLE;
}
}
UpdateDisplay(dev);
ReqAutoMount(dev);
return 0;
} /* end function mountnewtape */
/*-------------------------------------------------------------------*/
/* Query the device definition */
/*-------------------------------------------------------------------*/
static void tapedev_query_device ( DEVBLK *dev, char **devclass, int buflen, char *buffer )
{
char devparms[ MAX_PATH+1 + 128 ];
char dispmsg [ 256 ];
char fmt_mem [ 128 ]; // Max of 21 bytes used for U64
char fmt_eot [ 128 ]; // Max of 21 bytes used for U64
BEGIN_DEVICE_CLASS_QUERY( "TAPE", dev, devclass, buflen, buffer );
memset(buffer, 0, buflen);
memset(devparms, 0, sizeof(devparms));
memset(dispmsg, 0, sizeof(dispmsg));
memset(fmt_mem, 0, sizeof(fmt_mem));
memset(fmt_eot, 0, sizeof(fmt_eot));
GetDisplayMsg( dev, dispmsg, sizeof(dispmsg) );
if (strchr(dev->filename,' ')) strlcat( devparms, "\"", sizeof(devparms));
strlcat( devparms, dev->filename, sizeof(devparms));
if (strchr(dev->filename,' ')) strlcat( devparms, "\"", sizeof(devparms));
if (dev->noautomount)
strlcat( devparms, " noautomount", sizeof(devparms));
#if defined(OPTION_SCSI_TAPE)
if ( TAPEDEVT_SCSITAPE != dev->tapedevt )
#endif
{
const char suffix[9] = {0x00, 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'};
u_int i = 0;
U64 mem = (U64)dev->tdparms.maxsize;
if (mem)
{
for (i = 0; i < sizeof(suffix); i++)
{
if (mem & 0x3FF)
break;
mem >>= 10;
}
}
MSGBUF( fmt_mem, " maxsize=%"I64_FMT"u%c", mem, suffix[i]);
mem = (U64)dev->eotmargin;
i = 0;
if (mem)
{
for (i = 0; i < sizeof(suffix); i++)
{
if (mem & 0x3FF)
break;
mem >>= 10;
}
}
MSGBUF( fmt_eot, " eotmargin=%"I64_FMT"u%c", mem, suffix[i]);
}
if ( strcmp( dev->filename, TAPE_UNLOADED ) == 0 )
{
#if defined(OPTION_SCSI_TAPE)
if ( TAPEDEVT_SCSITAPE == dev->tapedevt )
{
if (0x3590 == dev->devtype) // emulating 3590
{
if (!dev->stape_blkid_32 ) strlcat( devparms, " --blkid-22", sizeof(devparms) );
}
else // emulating 3480, 3490
{
if ( dev->stape_blkid_32 ) strlcat( devparms, " --blkid-32", sizeof(devparms) );
}
if ( dev->stape_no_erg ) strlcat( devparms, " --no-erg", sizeof(devparms) );
}
#endif
snprintf(buffer, buflen, "%s%s%s IO[%" I64_FMT "u]%s%s deonirq=%c",
devparms,
dev->tdparms.displayfeat ? ", Display: " : "",
dev->tdparms.displayfeat ? dispmsg : "",
dev->excps,
fmt_mem, fmt_eot, dev->tdparms.deonirq == 0 ? 'N' : 'Y' );
buffer[buflen-1] = '\0';
}
else // (filename was specified)
{
char tapepos[64];
memset(tapepos, 0, sizeof(tapepos));
if ( TAPEDEVT_SCSITAPE != dev->tapedevt )
{
MSGBUF( tapepos, "[%d:%08"I64_FMT"X] ", dev->curfilen, dev->nxtblkpos );
}
#if defined(OPTION_SCSI_TAPE)
else // (this is a SCSI tape drive)
{
if (STS_BOT( dev ))
{
dev->eotwarning = 0;
strlcat(tapepos,"*BOT* ",sizeof(tapepos));
}
// If tape has a display, then GetDisplayMsg already
// appended *FP* for us. Otherwise we need to do it.
if ( !dev->tdparms.displayfeat )
if (STS_WR_PROT( dev ))
strlcat(tapepos,"*FP* ",sizeof(tapepos));
if (0x3590 == dev->devtype) // emulating 3590
{
if (!dev->stape_blkid_32 ) strlcat( devparms, " --blkid-22", sizeof(devparms) );
}
else // emulating 3480, 3490
{
if ( dev->stape_blkid_32 ) strlcat( devparms, " --blkid-32", sizeof(devparms) );
}
if ( dev->stape_no_erg ) strlcat( devparms, " --no-erg", sizeof(devparms) );
}
#endif
if ( TAPEDEVT_SCSITAPE != dev->tapedevt
#if defined(OPTION_SCSI_TAPE)
|| STS_MOUNTED(dev)
#endif
)
{
// Not a SCSI tape, -or- mounted SCSI tape...
snprintf( buffer, buflen, "%s%s %s%s%s IO[%" I64_FMT "u]",
devparms, (dev->readonly ? " ro" : ""),
tapepos,
dev->tdparms.displayfeat ? "Display: " : "",
dev->tdparms.displayfeat ? dispmsg : "",
dev->excps );
buffer[buflen-1] = '\0';
}
else /* ( TAPEDEVT_SCSITAPE == dev->tapedevt && STS_NOT_MOUNTED(dev) ) */
{
// UNmounted SCSI tape...
snprintf( buffer, buflen, "%s%s (%sNOTAPE)%s%s IO[%" I64_FMT "u]",
devparms, (dev->readonly ? " ro" : ""),
dev->fd < 0 ? "closed; " : "",
dev->tdparms.displayfeat ? ", Display: " : "",
dev->tdparms.displayfeat ? dispmsg : "",
dev->excps );
buffer[buflen-1] = '\0';
}
}
return;
} /* end function tapedev_query_device */
/*-------------------------------------------------------------------*/
/* Issue a message on the console indicating the display status */
/*-------------------------------------------------------------------*/
void UpdateDisplay( DEVBLK *dev )
{
if ( dev->tdparms.displayfeat )
{
char msgbfr[256];
GetDisplayMsg( dev, msgbfr, sizeof(msgbfr) );
if ( dev->prev_tapemsg )
{
if ( strcmp( msgbfr, dev->prev_tapemsg ) == 0 )
return;
free( dev->prev_tapemsg );
dev->prev_tapemsg = NULL;
}
dev->prev_tapemsg = strdup( msgbfr );
// "%1d:%04X Tape file '%s', type '%s': display '%s'"
WRMSG(HHC00224, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, TTYPSTR(dev->tapedevt), msgbfr );
}
#if defined(OPTION_SCSI_TAPE)
else
if (TAPEDEVT_SCSITAPE == dev->tapedevt)
int_scsi_status_update( dev, 1 );
#endif
}
/*-------------------------------------------------------------------*/
/* Issue Automatic Mount Requests as defined by the display */
/*-------------------------------------------------------------------*/
void ReqAutoMount( DEVBLK *dev )
{
char volser[7];
BYTE tapeloaded, autoload, mountreq, unmountreq, stdlbled, scratch;
char* lbltype;
char* tapemsg = " ";
///////////////////////////////////////////////////////////////////
// The Automatic Cartridge Loader or "ACL" (sometimes also referred
// to as an "Automatic Cartridge Feeder" (ACF) too) automatically
// loads the next cartridge [from the magazine] whenever a tape is
// unloaded, BUT ONLY IF the 'Index Automatic Load' bit (bit 7) of
// the FCB (Format Control Byte, byte 0) was on whenever the Load
// Display ccw was sent to the drive. If the bit was not on when
// the Load Display ccw was issued, then the requested message (if
// any) is displayed until the next tape mount/dismount and the ACL
// is NOT activated (i.e. the next tape is NOT automatically loaded).
// If the bit was on however, then, as stated, the ACF component of
// the drive will automatically load the next [specified] cartridge.
// Whenever the ACL facility is activated (via bit 7 of byte 0 of
// the Load Display ccw), then only bytes 1-8 of the "Display Until
// Mounted" message (or bytes 9-17 of a "Display Until Dismounted
// Then Mounted" message) are displayed to let the operator know
// which tape is currently being processed by the autoloader and
// thus is basically for informational purposes only (the operator
// does NOT need to do anything since the auto-loader is handling
// tape mounts for them automatically; i.e. the message is NOT an
// operator mount/dismount request).
// If the 'Index Automatic Load' bit was not set in the Load Display
// CCW however, then the specified "Display Until Mounted", "Display
// Until Unmounted" or "Display Until Unmounted Then Display Until
// Mounted" message is meant as a mount, unmount, or unmount-then-
// mount request for the actual [human being] operator, and thus
// they DO need to take some sort of action (since the ACL automatic
// loader facility is not active; i.e. the message is a request to
// the operator to manually unload, load or unload then load a tape).
// THUS... If the TAPEDISPFLG_AUTOLOADER flag is set (indicating
// the autoloader is (or should be) active), then the message we
// issue is simply for INFORMATIONAL purposes only (i.e. "FYI: the
// following tape is being *automatically* loaded; you don't need
// to actually do anything")
// If the TAPEDISPFLG_AUTOLOADER is flag is NOT set however, then
// we need to issue a message notifying the operator of what they
// are *expected* to do (e.g. either unload, load or unload/load
// the specified tape volume).
// Also please note that while there are no formally established
// standards regarding the format of the Load Display CCW message
// text, there are however certain established conventions (estab-
// lished by IBM naturally). If the first character is an 'M', it
// means "Please MOUNT the indicated volume". An 'R' [apparently]
// means "Retain", and, similarly, 'K' means "Keep" (same thing as
// "Retain"). If the LAST character is an 'S', then it means that
// a Standard Labeled volume is being requested, whereas an 'N'
// (or really, anything OTHER than an 'S' (except 'A')) means an
// unlabeled (or non-labeled) tape volume is being requested. An
// 'A' as the last character means a Standard Labeled ASCII tape
// is being requested. If the message is "SCRTCH" (or something
// similar), then a either a standard labeled or unlabeled scratch
// tape is obviously being requested (there doesn't seem to be any
// convention/consensus regarding the format for requesting scratch
// tapes; some shops for example use 'XXXSCR' to indicate that a
// scratch tape from tape pool 'XXX' should be mounted).
///////////////////////////////////////////////////////////////////
/* Open the file/drive if needed (kick off auto-mount if needed) */
if (dev->fd < 0)
{
BYTE unitstat = 0, code = 0;
BYTE *sensebkup;
/* Save any pending sense */
sensebkup=malloc(dev->numsense);
memcpy(sensebkup,dev->sense,dev->numsense);
dev->tmh->open( dev, &unitstat, code );
/* Restore pending sense */
memcpy(dev->sense,sensebkup,dev->numsense);
free(sensebkup);
#if defined(OPTION_SCSI_TAPE)
if (TAPEDEVT_SCSITAPE == dev->tapedevt)
{
// PROGRAMMING NOTE: it's important to do TWO refreshes here
// to cause the auto-mount thread to get created. Doing only
// one doesn't work and doing two shouldn't cause any harm.
GENTMH_PARMS gen_parms;
gen_parms.action = GENTMH_SCSI_ACTION_UPDATE_STATUS;
gen_parms.dev = dev;
// (refresh potentially stale status)
VERIFY( dev->tmh->generic( &gen_parms ) == 0 );
// (force auto-mount thread creation)
VERIFY( dev->tmh->generic( &gen_parms ) == 0 );
}
#endif /* defined(OPTION_SCSI_TAPE) */
}
/* Disabled when [non-SCSI] ACL in use */
if ( dev->als )
return;
/* Do we actually have any work to do? */
if ( !( dev->tapedispflags & TAPEDISPFLG_REQAUTOMNT ) )
return; // (nothing to do!)
/* Reset work flag */
dev->tapedispflags &= ~TAPEDISPFLG_REQAUTOMNT;
/* If the drive doesn't have a display,
then it can't have an auto-loader either */
if ( !dev->tdparms.displayfeat )
return;
/* Determine if mount or unmount request
and get pointer to correct message */
tapeloaded = dev->tmh->tapeloaded( dev, NULL, 0 ) ? TRUE : FALSE;
mountreq = FALSE; // (default)
unmountreq = FALSE; // (default)
if (tapeloaded)
{
// A tape IS already loaded...
// 1st byte of message1 non-blank, *AND*,
// unmount request or,
// unmountmount request and not message2-only flag?
if (' ' != *(tapemsg = dev->tapemsg1) &&
(0
|| TAPEDISPTYP_UNMOUNT == dev->tapedisptype
|| (1
&& TAPEDISPTYP_UMOUNTMOUNT == dev->tapedisptype
&& !(dev->tapedispflags & TAPEDISPFLG_MESSAGE2)
)
)
)
unmountreq = TRUE;
}
else
{
// NO TAPE is loaded yet...
// mount request and 1st byte of msg1 non-blank, *OR*,
// unmountmount request and 1st byte of msg2 non-blank?
if (
(1
&& TAPEDISPTYP_MOUNT == dev->tapedisptype
&& ' ' != *(tapemsg = dev->tapemsg1)
)
||
(1
&& TAPEDISPTYP_UMOUNTMOUNT == dev->tapedisptype
&& ' ' != *(tapemsg = dev->tapemsg2)
))
mountreq = TRUE;
}
/* Extract volser from message */
strncpy( volser, tapemsg+1, 6 ); volser[6]=0;
/* Set some boolean flags */
autoload = ( dev->tapedispflags & TAPEDISPFLG_AUTOLOADER ) ? TRUE : FALSE;
stdlbled = ( 'S' == tapemsg[7] ) ? TRUE : FALSE;
// ascii = ( 'A' == tapemsg[7] ) ? TRUE : FALSE;
scratch = ( 'S' == tapemsg[0] ) ? TRUE : FALSE;
lbltype = stdlbled ? "SL" : "UL";
#if defined(OPTION_SCSI_TAPE)
#if 1
// ****************************************************************
// ZZ FIXME: ZZ TODO: *** Programming Note ***
// Since we currently don't have any way of activating a SCSI tape
// drive's REAL autoloader mechanism whenever we receive an auto-
// mount message [from the guest o/s via the Load Display CCW], we
// leave it to the operator to action the mount message displayed
// Once ASPI code eventually gets added to Herc (and/or something
// similar for the Linux world), then the following workaround can
// be safely removed.
autoload = FALSE; // (temporarily forced; see above)
// ****************************************************************
#endif
#endif /* defined(OPTION_SCSI_TAPE) */
if ( autoload )
{
// ZZ TODO: Here is where we'd issue i/o (ASPI?) to the actual
// hardware autoloader facility (i.e. the SCSI medium changer)
// to unload and/or load the tape(s) if this were a SCSI auto-
// loading tape drive.
if ( unmountreq )
{
// "%1d:%04X Tape file '%s', type '%s': '%s' tape volume '%s' being auto unloaded"
WRMSG(HHC00226, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename,
TTYPSTR(dev->tapedevt), lbltype, scratch ? "<scratch>" : volser);
}
if ( mountreq )
{
// "%1d:%04X Tape file '%s', type '%s': '%s' tape volume '%s' being auto loaded"
WRMSG(HHC00227, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename,
TTYPSTR(dev->tapedevt), lbltype, scratch ? "<scratch>" : volser);
}
}
} /* end function ReqAutoMount */
/*-------------------------------------------------------------------*/
/* Get 3480/3490/3590 Display text in 'human' form */
/* If not a 3480/3490/3590, then just update status if a SCSI tape */
/*-------------------------------------------------------------------*/
void GetDisplayMsg( DEVBLK *dev, char *msgbfr, size_t lenbfr )
{
msgbfr[0]=0;
if ( !dev->tdparms.displayfeat )
{
// (drive doesn't have a display)
#if defined(OPTION_SCSI_TAPE)
if (TAPEDEVT_SCSITAPE == dev->tapedevt)
int_scsi_status_update( dev, 1 );
#endif
return;
}
if ( !IS_TAPEDISPTYP_SYSMSG( dev ) )
{
// -------------------------
// Display Host message
// -------------------------
// "When bit 3 (alternate) is set to 1, then
// bits 4 (blink) and 5 (low/high) are ignored."
strlcpy( msgbfr, "\"", lenbfr );
if ( dev->tapedispflags & TAPEDISPFLG_ALTERNATE )
{
char msg1[9];
char msg2[9];
strlcpy ( msg1, dev->tapemsg1, sizeof(msg1) );
strlcat ( msg1, " ", sizeof(msg1) );
strlcpy ( msg2, dev->tapemsg2, sizeof(msg2) );
strlcat ( msg2, " ", sizeof(msg2) );
strlcat ( msgbfr, msg1, lenbfr );
strlcat ( msgbfr, "\" / \"", lenbfr );
strlcat ( msgbfr, msg2, lenbfr );
strlcat ( msgbfr, "\"", lenbfr );
strlcat ( msgbfr, " (alternating)", lenbfr );
}
else
{
if ( dev->tapedispflags & TAPEDISPFLG_MESSAGE2 )
strlcat( msgbfr, dev->tapemsg2, lenbfr );
else
strlcat( msgbfr, dev->tapemsg1, lenbfr );
strlcat ( msgbfr, "\"", lenbfr );
if ( dev->tapedispflags & TAPEDISPFLG_BLINKING )
strlcat ( msgbfr, " (blinking)", lenbfr );
}
if ( dev->tapedispflags & TAPEDISPFLG_AUTOLOADER )
strlcat( msgbfr, " (AUTOLOADER)", lenbfr );
return;
}
// ----------------------------------------------
// Display SYS message (Unit/Device message)
// ----------------------------------------------
// First, build the system message, then move it into
// the caller's buffer...
strlcpy( dev->tapesysmsg, "\"", sizeof(dev->tapesysmsg) );
switch ( dev->tapedisptype )
{
case TAPEDISPTYP_IDLE:
case TAPEDISPTYP_WAITACT:
default:
// Blank display if no tape loaded...
if ( !dev->tmh->tapeloaded( dev, NULL, 0 ) )
{
strlcat( dev->tapesysmsg, " ", sizeof(dev->tapesysmsg) );
break;
}
// " NT RDY " if tape IS loaded, but not ready...
// (IBM docs say " NT RDY " means "Loaded but not ready")
ASSERT( dev->tmh->tapeloaded( dev, NULL, 0 ) );
if (0
|| dev->fd < 0
#if defined(OPTION_SCSI_TAPE)
|| (1
&& TAPEDEVT_SCSITAPE == dev->tapedevt
&& !STS_ONLINE( dev )
)
#endif
)
{
strlcat( dev->tapesysmsg, " NT RDY ", sizeof(dev->tapesysmsg) );
break;
}
// Otherwise tape is loaded and ready --> "READY"
ASSERT( dev->tmh->tapeloaded( dev, NULL, 0 ) );
strlcat ( dev->tapesysmsg, " READY ", sizeof(dev->tapesysmsg) );
strlcat( dev->tapesysmsg, "\"", sizeof(dev->tapesysmsg) );
if (0
|| dev->readonly
#if defined(OPTION_SCSI_TAPE)
|| (1
&& TAPEDEVT_SCSITAPE == dev->tapedevt
&& STS_WR_PROT( dev )
)
#endif
)
// (append "file protect" indicator)
strlcat ( dev->tapesysmsg, " *FP*", sizeof(dev->tapesysmsg) );
// Copy system message to caller's buffer
strlcpy( msgbfr, dev->tapesysmsg, lenbfr );
return;
case TAPEDISPTYP_ERASING:
strlcat ( dev->tapesysmsg, " ERASING", sizeof(dev->tapesysmsg) );
break;
case TAPEDISPTYP_REWINDING:
strlcat ( dev->tapesysmsg, "REWINDNG", sizeof(dev->tapesysmsg) );
break;
case TAPEDISPTYP_UNLOADING:
strlcat ( dev->tapesysmsg, "UNLOADNG", sizeof(dev->tapesysmsg) );
break;
case TAPEDISPTYP_CLEAN:
strlcat ( dev->tapesysmsg, "*CLEAN ", sizeof(dev->tapesysmsg) );
break;
}
strlcat( dev->tapesysmsg, "\"", sizeof(dev->tapesysmsg) );
// Copy system message to caller's buffer
strlcpy( msgbfr, dev->tapesysmsg, lenbfr );
} /* end function GetDisplayMsg */
/*-------------------------------------------------------------------*/
/* IsAtLoadPoint */
/*-------------------------------------------------------------------*/
/* Called by the device-type-specific 'build_sense_xxxx' functions */
/* (indirectly via the 'build_senseX' function) when building sense */
/* for any i/o error (non-"TAPE_BSENSE_STATUSONLY" type call) */
/*-------------------------------------------------------------------*/
int IsAtLoadPoint (DEVBLK *dev)
{
int ldpt=0;
if ( dev->fd >= 0 )
{
/* Set load point indicator if tape is at load point */
switch (dev->tapedevt)
{
default:
case TAPEDEVT_AWSTAPE:
if (dev->nxtblkpos==0)
{
ldpt=1;
}
break;
case TAPEDEVT_HETTAPE:
if (dev->hetb->cblk == 0)
{
ldpt=1;
}
break;
#if defined(OPTION_SCSI_TAPE)
case TAPEDEVT_SCSITAPE:
int_scsi_status_update( dev, 0 );
if ( STS_BOT( dev ) )
{
dev->eotwarning = 0;
ldpt=1;
}
break;
#endif /* defined(OPTION_SCSI_TAPE) */
case TAPEDEVT_OMATAPE:
if (dev->nxtblkpos == 0 && dev->curfilen == 1)
{
ldpt=1;
}
break;
} /* end switch(dev->tapedevt) */
}
else // ( dev->fd < 0 )
{
if ( TAPEDEVT_SCSITAPE == dev->tapedevt )
ldpt=0; /* (tape cannot possibly be at loadpoint
if the device cannot even be opened!) */
else if ( strcmp( dev->filename, TAPE_UNLOADED ) != 0 )
{
/* If the tape has a filename but the tape is not yet */
/* opened, then we are at loadpoint */
ldpt=1;
}
}
return ldpt;
} /* end function IsAtLoadPoint */
/*********************************************************************/
/*********************************************************************/
/** **/
/** AUTOLOADER FUNCTIONS **/
/** **/
/*********************************************************************/
/*********************************************************************/
/*-------------------------------------------------------------------*/
/* autoload_init */
/*-------------------------------------------------------------------*/
/* initialise the Autoloader feature */
/*-------------------------------------------------------------------*/
void autoload_init( DEVBLK *dev, int ac, char **av )
{
char bfr[4096];
char *rec;
FILE *aldf;
char pathname[MAX_PATH];
int argc;
char *argv[MAX_ARGS];
autoload_close( dev );
if (0
|| ac < 1
|| av[0][0] != '@'
)
return;
// "Tape autoloader: file request fn '%s'"
WRMSG(HHC00228, "I", &av[0][1]);
hostpath( pathname, &av[0][1], sizeof(pathname) );
if (!(aldf = fopen( pathname, "r" )))
return; // open error
if (ac > 1)
autoload_global_parms( dev, ac-1, &av[1] );
while ((rec = fgets( bfr, 4096, aldf )))
{
if (0 == parse_args( rec, MAX_ARGS, argv, &argc ))
continue; // (zero args == blank line; skip..)
if (strcmp( argv[0], "*" ) == 0)
autoload_global_parms( dev, argc, argv );
else
autoload_tape_entry( dev, argc, argv );
}
fclose(aldf);
return;
} /* end function autoload_init */
/*-------------------------------------------------------------------*/
/* autoload_close */
/*-------------------------------------------------------------------*/
/* terminate autoloader operations: release all storage that */
/* was allocated by the autoloader facility */
/*-------------------------------------------------------------------*/
void autoload_close(DEVBLK *dev)
{
int i;
if(dev->al_argv!=NULL)
{
for(i=0;i<dev->al_argc;i++)
{
free(dev->al_argv[i]);
dev->al_argv[i]=NULL;
}
free(dev->al_argv);
dev->al_argv=NULL;
dev->al_argc=0;
}
dev->al_argc=0;
if(dev->als!=NULL)
{
for(i=0;i<dev->alss;i++)
{
autoload_clean_entry(dev,i);
}
free(dev->als);
dev->als=NULL;
dev->alss=0;
}
} /* end function autoload_close */
/*-------------------------------------------------------------------*/
/* autoload_clean_entry */
/*-------------------------------------------------------------------*/
/* release storage allocated for an autoloader slot */
/* (except the slot itself) */
/*-------------------------------------------------------------------*/
void autoload_clean_entry(DEVBLK *dev,int ix)
{
int i;
for(i=0;i<dev->als[ix].argc;i++)
{
free(dev->als[ix].argv[i]);
dev->als[ix].argv[i]=NULL;
}
dev->als[ix].argc=0;
if(dev->als[ix].filename!=NULL)
{
free(dev->als[ix].filename);
dev->als[ix].filename=NULL;
}
} /* end function autoload_clean_entry */
/*-------------------------------------------------------------------*/
/* autoload_global_parms */
/*-------------------------------------------------------------------*/
/* Appends an additional list of parameters that will */
/* be passed for every tape mounted by the autoloader */
/*-------------------------------------------------------------------*/
void autoload_global_parms( DEVBLK *dev, int argc, char *argv[] )
{
int i;
char *p;
if (!dev->al_argv)
{
dev->al_argv = calloc( AUTOLOADER_MAX, sizeof(char*));
dev->al_argc = 0;
if (!dev->al_argv) return;
}
for (i=1; i < argc && dev->al_argc < AUTOLOADER_MAX; ++i)
{
p = (char*) strdup( argv[i] );
if (!p) return;
// "Tape autoloader: adding '%s' value '%s'"
WRMSG(HHC00229, "I", "global parm", p);
dev->al_argv[ dev->al_argc ] = p;
dev->al_argc++;
}
} /* end function autoload_global_parms */
/*-------------------------------------------------------------------*/
/* autoload_tape_entry */
/*-------------------------------------------------------------------*/
/* populate an autoloader slot (creates new slot if needed) */
/*-------------------------------------------------------------------*/
void autoload_tape_entry( DEVBLK *dev, int argc, char *argv[] )
{
int i;
TAPEAUTOLOADENTRY tae;
if (dev->alss >= AUTOLOADER_MAX)
return;
if (!dev->als)
{
dev->als = calloc( 1, sizeof(tae) );
dev->alss = 0;
if (!dev->als) return;
}
else
{
TAPEAUTOLOADENTRY *als = (TAPEAUTOLOADENTRY*)
realloc( dev->als, (dev->alss + 1) * sizeof(tae) );
if (!als) return;
dev->als = als;
}
tae.argc = 0;
tae.argv = NULL;
tae.filename = strdup( argv[0] );
if (!tae.filename) return;
// "Tape autoloader: adding '%s' value '%s'"
WRMSG( HHC00229, "I", "tape entry", tae.filename );
if (argc > 1)
{
tae.argv = calloc( argc-1, sizeof(char*));
if (!tae.argv)
{
free( tae.filename );
return;
}
for (i=1, tae.argc=0; i < argc-1; i++)
{
tae.argv[ tae.argc ] = strdup( argv[i] );
if (!tae.argv[ tae.argc ]) break;
tae.argc++;
}
}
memcpy( &dev->als[ dev->alss ], &tae, sizeof(tae) );
dev->alss++;
} /* end function autoload_tape_entry */
/*-------------------------------------------------------------------*/
/* autoload_wait_for_tapemount_thread */
/*-------------------------------------------------------------------*/
void *autoload_wait_for_tapemount_thread(void *db)
{
int rc = -1;
DEVBLK *dev = (DEVBLK*) db;
obtain_lock(&dev->lock);
{
while
(
dev->als
&&
(rc = autoload_mount_next( dev )) != 0
)
{
release_lock( &dev->lock );
SLEEP(AUTOLOAD_WAIT_FOR_TAPEMOUNT_INTERVAL_SECS);
obtain_lock( &dev->lock );
}
}
release_lock(&dev->lock);
if ( rc == 0 )
device_attention(dev,CSW_DE);
return NULL;
} /* end function autoload_wait_for_tapemount_thread */
/*-------------------------------------------------------------------*/
/* autoload_mount_first */
/*-------------------------------------------------------------------*/
/* mount in the drive the tape which is */
/* positionned in the 1st autoloader slot */
/*-------------------------------------------------------------------*/
int autoload_mount_first(DEVBLK *dev)
{
dev->alsix=0;
return(autoload_mount_tape(dev,0));
}
/*-------------------------------------------------------------------*/
/* autoload_mount_next */
/*-------------------------------------------------------------------*/
/* mount in the drive the tape whch is */
/* positionned in the slot after the currently mounted tape. */
/* if this is the last tape, close the autoloader */
/*-------------------------------------------------------------------*/
int autoload_mount_next(DEVBLK *dev)
{
if(dev->alsix>=dev->alss)
{
autoload_close(dev);
return -1;
}
dev->alsix++;
return(autoload_mount_tape(dev,dev->alsix));
}
/*-------------------------------------------------------------------*/
/* autoload_mount_tape */
/*-------------------------------------------------------------------*/
/* mount in the drive the tape which is */
/* positionned in the autoloader slot #alix */
/*-------------------------------------------------------------------*/
int autoload_mount_tape( DEVBLK *dev, int alix )
{
int argc, i, rc;
char **argv;
if (alix >= dev->alss)
return -1;
argc = 1 + dev->al_argc + dev->als[ alix ].argc;
argv = calloc( argc, sizeof(BYTE*) );
if (!argv) return -1;
argv[0] = dev->als[ alix ].filename;
for (i=0, argc=1; i < dev->al_argc; i++, argc++)
{
argv[ argc ] = strdup( dev->al_argv[i] );
if (!argv[ argc ]) break;
}
for (i=0; i < dev->als[ alix ].argc; i++, argc++)
{
argv[ argc ] = strdup( dev->als[ alix ].argv[i] );
if (!argv[ argc ]) break;
}
rc = mountnewtape( dev, argc, argv );
for (i=1; i < argc; i++)
free( argv[i] );
free( argv );
return rc;
} /* end function autoload_mount_tape */
/*-------------------------------------------------------------------*/
/* is_tapeloaded_filename */
/*-------------------------------------------------------------------*/
int is_tapeloaded_filename ( DEVBLK *dev, BYTE *unitstat, BYTE code )
{
UNREFERENCED(unitstat);
UNREFERENCED(code);
// true 1 == tape loaded, false 0 == tape not loaded
return strcmp( dev->filename, TAPE_UNLOADED ) != 0 ? 1 : 0;
}
/*-------------------------------------------------------------------*/
/* return_false1 */
/*-------------------------------------------------------------------*/
int return_false1 ( DEVBLK *dev )
{
UNREFERENCED(dev);
return 0;
}
/*-------------------------------------------------------------------*/
/* write_READONLY */
/*-------------------------------------------------------------------*/
int write_READONLY ( DEVBLK *dev, BYTE *unitstat, BYTE code )
{
build_senseX(TAPE_BSENSE_WRITEPROTECT,dev,unitstat,code);
return -1;
}
/*-------------------------------------------------------------------*/
/* write_READONLY5 */
/*-------------------------------------------------------------------*/
int write_READONLY5 ( DEVBLK *dev, BYTE *bfr, U32 blklen, BYTE *unitstat, BYTE code )
{
UNREFERENCED(bfr);
UNREFERENCED(blklen);
build_senseX(TAPE_BSENSE_WRITEPROTECT,dev,unitstat,code);
return -1;
}
/*-------------------------------------------------------------------*/
/* no_operation */
/*-------------------------------------------------------------------*/
int no_operation ( DEVBLK *dev, BYTE *unitstat, BYTE code )
{
build_senseX( TAPE_BSENSE_STATUSONLY, dev, unitstat, code );
return 0;
}
/*-------------------------------------------------------------------*/
/* readblkid_virtual */
/*-------------------------------------------------------------------*/
int readblkid_virtual ( DEVBLK* dev, BYTE* logical, BYTE* physical )
{
// NOTE: returned value is always in guest BIG-ENDIAN format...
BYTE blockid[4];
if (0x3590 == dev->devtype)
{
// Full 32-bit block-id...
blockid[0] = (dev->blockid >> 24) & 0xFF;
blockid[1] = (dev->blockid >> 16) & 0xFF;
blockid[2] = (dev->blockid >> 8 ) & 0xFF;
blockid[3] = (dev->blockid ) & 0xFF;
}
else // (3480 et. al)
{
// "22-bit" block-id...
blockid[0] = 0x01; // ("wrap" value)
blockid[1] = (dev->blockid >> 16) & 0x3F;
blockid[2] = (dev->blockid >> 8 ) & 0xFF;
blockid[3] = (dev->blockid ) & 0xFF;
}
// NOTE: For virtual tape devices, we return the same value
// for both the logical "Channel block ID" value as well as
// the physical "Device block ID" value...
if (logical) memcpy( logical, &blockid[0], 4 );
if (physical) memcpy( physical, &blockid[0], 4 );
return 0;
}
/*-------------------------------------------------------------------*/
/* locateblk_virtual */
/*-------------------------------------------------------------------*/
int locateblk_virtual ( DEVBLK* dev, U32 blockid, BYTE *unitstat, BYTE code )
{
// NOTE: 'blockid' passed in host (little-endian) format...
int rc;
/* Do it the hard way: rewind to load-point and then
keep doing fsb, fsb, fsb... until we find our block
*/
if ((rc = dev->tmh->rewind( dev, unitstat, code)) >= 0)
{
/* Reset position counters to start of file */
dev->curfilen = 1;
dev->nxtblkpos = 0;
dev->prvblkpos = -1;
dev->blockid = 0;
/* Do it the hard way */
while ( dev->blockid < blockid && ( rc >= 0 ) )
rc = dev->tmh->fsb( dev, unitstat, code );
}
return rc;
}
/*-------------------------------------------------------------------*/
/* generic_tmhcall generic media-type-handler call... */
/*-------------------------------------------------------------------*/
int generic_tmhcall ( GENTMH_PARMS* pGenParms )
{
if (!pGenParms)
{
errno = EINVAL; // (invalid arguments)
return -1; // (return failure)
}
switch (pGenParms->action)
{
#if defined(OPTION_SCSI_TAPE)
case GENTMH_SCSI_ACTION_UPDATE_STATUS:
{
return update_status_scsitape( pGenParms->dev );
}
#endif /* defined(OPTION_SCSI_TAPE) */
default:
{
errno = EINVAL; // (invalid arguments)
return -1; // (return failure)
}
}
UNREACHABLE_CODE();
}