mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-07-26 10:38:47 +02:00
d79382bbfe
* chore: run codespell on source/header files Fixes most reported typos discovered by codespell (while trying to be unopinionated about American vs. British English). * chore: run codespell on build/readme files
1689 lines
61 KiB
C
1689 lines
61 KiB
C
/* FBADASD.C (C) Copyright Roger Bowler, 1999-2012 */
|
|
/* (C) and others 2013-2023 */
|
|
/* ESA/390 FBA Direct Access Storage Device Handler */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* This module contains device handling functions for emulated */
|
|
/* fixed block architecture direct access storage devices. */
|
|
/* Ref: SA22-1025-00 "System/390 Internal Disk Subsystem */
|
|
/* Reference Guide (Multiprise 3000)" */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Additional credits: */
|
|
/* 0671 device support by Jay Maynard */
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
#include "hstdinc.h"
|
|
|
|
#define _FBADASD_C_
|
|
#define _HDASD_DLL_
|
|
|
|
#include "hercules.h"
|
|
#include "dasdblks.h" // (need #define DEFAULT_FBA_TYPE)
|
|
#include "sr.h"
|
|
#include "cckddasd.h"
|
|
#include "ccwarn.h"
|
|
|
|
DISABLE_GCC_UNUSED_SET_WARNING;
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Bit definitions for Define Extent mask byte */
|
|
/*-------------------------------------------------------------------*/
|
|
#define FBAMASK_WR_RSHIFT 6 /* Write Control bits */
|
|
#define FBAMASK_WR_MASK 0x03 /* Write Control bits */
|
|
#define FBAMASK_WR_NOFMT 0 /* All writes except HA/R0 */
|
|
#define FBAMASK_WR_NONE 1 /* Inhibit ALL writes */
|
|
#define FBAMASK_WR_UPDONLY 2 /* Update writes only */
|
|
#define FBAMASK_WR_ALL 3 /* Permit ALL writes */
|
|
/*-------------------------------------------------------------------*/
|
|
#define FBAMASK_RSRVD 0x20 /* Reserved bit - must be 0 */
|
|
/*-------------------------------------------------------------------*/
|
|
#define FBAMASK_SK_RSHIFT 3 /* Seek Control bits */
|
|
#define FBAMASK_SK_MASK 0x03 /* Seek Control bits */
|
|
#define FBAMASK_SK_ALL 0 /* Permit all seeks */
|
|
#define FBAMASK_SK_CYLHD 1 /* Seek Cyl or Head only */
|
|
#define FBAMASK_SK_HEAD 2 /* Seek Head only */
|
|
#define FBAMASK_SK_NONE 3 /* Inhibit ALL seeks/MulTrk */
|
|
/*-------------------------------------------------------------------*/
|
|
#define FBAMASK_AUTH_RSHIFT 1 /* Access Authorization bits */
|
|
#define FBAMASK_AUTH_MASK 0x03 /* Access Authorization bits */
|
|
#define FBAMASK_AUTH_NORMAL 0 /* Normal authorization */
|
|
#define FBAMASK_AUTH_DEV 1 /* Device Support Auth. */
|
|
#define FBAMASK_AUTH_DIAG 2 /* Diagnostic Authorization */
|
|
#define FBAMASK_AUTH_NORETRY 3 /* Device Support, NO retry */
|
|
/*-------------------------------------------------------------------*/
|
|
#define FBAMASK_PCI_RSHIFT 0 /* PCI mode bits */
|
|
#define FBAMASK_PCI_MASK 0x01 /* PCI mode bits */
|
|
#define FBAMASK_PCI_NOFETCH 0 /* NOT PCI fetch mode */
|
|
#define FBAMASK_PCI_FETCH 1 /* PCI fetch mode */
|
|
/*-------------------------------------------------------------------*/
|
|
#define FBAMASK_BITS( field ) /* Helper macro helper macro */ \
|
|
((dev->fbamask >> FBAMASK_ ## field ## _RSHIFT) & FBAMASK_ ## field ## _MASK)
|
|
#define IS_FBAMASK( field, value ) /* Helper macro */ \
|
|
(FBAMASK_BITS( field ) == FBAMASK_ ## field ## _ ## value)
|
|
/*-------------------------------------------------------------------*/
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Bit definitions for Locate operation byte */
|
|
/*-------------------------------------------------------------------*/
|
|
#define FBAOPER_RESV 0xE0 /* Reserved bits - must be 0 */
|
|
/* Note : Bit 3 seems to be */
|
|
/* The "suppress" bit but is */
|
|
/* apparently ignored */
|
|
/* It is therefore valid but */
|
|
/* not used. */
|
|
#define FBAOPER_CODE 0x0F /* Operation code bits... */
|
|
#define FBAOPER_WRITE 0x01 /* ...write data */
|
|
#define FBAOPER_READREP 0x02 /* ...read replicated data */
|
|
#define FBAOPER_FMTDEFC 0x04 /* ...format defective block */
|
|
#define FBAOPER_WRTVRFY 0x05 /* ...write data and verify */
|
|
#define FBAOPER_READ 0x06 /* ...read data */
|
|
|
|
static int fba_read (DEVBLK *dev, BYTE *buf, int len, BYTE *unitstat);
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Initialize the device handler */
|
|
/*-------------------------------------------------------------------*/
|
|
int fba_dasd_init_handler ( DEVBLK *dev, int argc, char *argv[] )
|
|
{
|
|
CKD_DEVHDR devhdr; /* Device header */
|
|
CCKD_DEVHDR cdevhdr; /* Compressed device header */
|
|
int rc; /* Return code */
|
|
struct stat statbuf; /* File information */
|
|
int startblk; /* Device origin block number*/
|
|
int numblks; /* Device block count */
|
|
BYTE c; /* Character work area */
|
|
char *cu = NULL; /* Specified control unit */
|
|
bool cfba = false; /* true = Compressed fba */
|
|
int i; /* Loop index */
|
|
char filename[FILENAME_MAX+3]; /* work area for display */
|
|
|
|
/* For re-initialisation, close the existing file, if any */
|
|
if (dev->fd >= 0)
|
|
(dev->hnd->close)(dev);
|
|
|
|
if (!dev->typname || !sscanf(dev->typname,"%hx",&(dev->devtype)))
|
|
dev->devtype = DEFAULT_FBA_TYPE;
|
|
|
|
/* reset excps count */
|
|
dev->excps = 0;
|
|
|
|
/* The first argument is the file name */
|
|
if (argc == 0 || strlen(argv[0]) >= sizeof(dev->filename))
|
|
{
|
|
// "%1d:%04X FBA file: name missing or invalid filename length"
|
|
WRMSG( HHC00500, "E", LCSS_DEVNUM);
|
|
return -1;
|
|
}
|
|
|
|
/* Save the file name in the device block */
|
|
hostpath(dev->filename, argv[0], sizeof(dev->filename));
|
|
|
|
if (!strchr( dev->filename, SPACE ))
|
|
MSGBUF( filename, "%s", dev->filename );
|
|
else
|
|
MSGBUF( filename, "'%s'", dev->filename );
|
|
|
|
#if defined( OPTION_SHARED_DEVICES )
|
|
/* Device is shareable */
|
|
dev->shareable = 1;
|
|
#endif // defined( OPTION_SHARED_DEVICES )
|
|
|
|
/* Check for possible remote device */
|
|
if (stat(dev->filename, &statbuf) < 0)
|
|
{
|
|
rc = shared_fba_init ( dev, argc, argv);
|
|
if (rc < 0)
|
|
{
|
|
// "%1d:%04X FBA file %s not found or invalid"
|
|
WRMSG( HHC00501, "E", LCSS_DEVNUM, dev->filename );
|
|
return -1;
|
|
}
|
|
else
|
|
return rc;
|
|
}
|
|
|
|
/* Open the device file */
|
|
dev->fd = HOPEN (dev->filename, O_RDWR|O_BINARY);
|
|
if (dev->fd < 0)
|
|
{
|
|
dev->fd = HOPEN (dev->filename, O_RDONLY|O_BINARY);
|
|
if (dev->fd < 0)
|
|
{
|
|
// "%1d:%04X FBA file %s: error in function %s: %s"
|
|
WRMSG( HHC00502, "E", LCSS_DEVNUM,
|
|
dev->filename, "open()", strerror( errno ));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Read the first block to see if it's compressed */
|
|
rc = read (dev->fd, &devhdr, CKD_DEVHDR_SIZE);
|
|
if (rc < (int) CKD_DEVHDR_SIZE)
|
|
{
|
|
/* Handle read error condition */
|
|
if (rc < 0)
|
|
// "%1d:%04X FBA file %s: error in function %s: %s"
|
|
WRMSG( HHC00502, "E", LCSS_DEVNUM,
|
|
dev->filename, "read()", strerror( errno ));
|
|
else
|
|
// "%1d:%04X FBA file %s: error in function %s: %s"
|
|
WRMSG( HHC00502, "E", LCSS_DEVNUM,
|
|
dev->filename, "read()", "unexpected end of file" );
|
|
close (dev->fd);
|
|
dev->fd = -1;
|
|
return -1;
|
|
}
|
|
|
|
/* Processing for compressed fba dasd */
|
|
if (is_dh_devid_typ( devhdr.dh_devid, FBA_C370_TYP ))
|
|
{
|
|
dev->cckd64 = 0;
|
|
|
|
cfba = true;
|
|
|
|
/* Read the compressed device header */
|
|
rc = read (dev->fd, &cdevhdr, CCKD_DEVHDR_SIZE);
|
|
if (rc < (int) CCKD_DEVHDR_SIZE)
|
|
{
|
|
/* Handle read error condition */
|
|
if (rc < 0)
|
|
// "%1d:%04X FBA file %s: error in function %s: %s"
|
|
WRMSG( HHC00502, "E", LCSS_DEVNUM,
|
|
dev->filename, "read()", strerror( errno ));
|
|
else
|
|
// "%1d:%04X FBA file %s: error in function %s: %s"
|
|
WRMSG( HHC00502, "E", LCSS_DEVNUM,
|
|
dev->filename, "read()", "unexpected end of file" );
|
|
close (dev->fd);
|
|
dev->fd = -1;
|
|
return -1;
|
|
}
|
|
|
|
/* Set block size, device origin, and device size in blocks */
|
|
dev->fbablksiz = 512;
|
|
dev->fbaorigin = 0;
|
|
FETCH_LE_FW( dev->fbanumblk, cdevhdr.cdh_cyls );
|
|
|
|
/* process the remaining arguments */
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
if (strlen (argv[i]) > 3
|
|
&& memcmp ("sf=", argv[i], 3) == 0)
|
|
{
|
|
/* Parse the shadow file name parameter */
|
|
cckd_sf_parse_sfn( dev, argv[i]+3 );
|
|
continue;
|
|
}
|
|
if (strlen (argv[i]) > 3
|
|
&& memcmp("cu=", argv[i], 3) == 0) /* support for cu= added but */
|
|
{ /* is ignored for the present */
|
|
cu = argv[i]+3;
|
|
continue;
|
|
}
|
|
|
|
// "%1d:%04X %s file: parameter %s in argument %d is invalid"
|
|
WRMSG( HHC00503, "E", LCSS_DEVNUM, FBATYP( cfba, 0 ), argv[i], i + 1 );
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Processing for compressed fba64 dasd */
|
|
else if (is_dh_devid_typ( devhdr.dh_devid, FBA_C064_TYP ))
|
|
{
|
|
dev->cckd64 = 1;
|
|
close( dev->fd );
|
|
dev->fd = -1;
|
|
return fba64_dasd_init_handler( dev, argc, argv );
|
|
}
|
|
|
|
/* Processing for regular fba dasd */
|
|
else
|
|
{
|
|
dev->cckd64 = 0;
|
|
|
|
if (dev->dasdsfn || dev->dasdsfx)
|
|
{
|
|
// "%1d:%04X %s file %s: shadow files not supported for %s dasd"
|
|
WRMSG( HHC00469, "E", LCSS_DEVNUM, FBATYP( cfba, dev->cckd64 ),
|
|
filename, FBATYP( cfba, dev->cckd64 ) );
|
|
return -1;
|
|
}
|
|
|
|
/* Determine the device size */
|
|
rc = fstat (dev->fd, &statbuf);
|
|
if (rc < 0)
|
|
{
|
|
// "%1d:%04X FBA file %s: error in function %s: %s"
|
|
WRMSG( HHC00502, "E", LCSS_DEVNUM,
|
|
dev->filename, "fstat()", strerror( errno ));
|
|
close (dev->fd);
|
|
dev->fd = -1;
|
|
return -1;
|
|
}
|
|
#if defined(OPTION_FBA_BLKDEVICE) && defined(BLKGETSIZE)
|
|
if(S_ISBLK(statbuf.st_mode))
|
|
{
|
|
rc=ioctl(dev->fd,BLKGETSIZE,&statbuf.st_size);
|
|
if(rc<0)
|
|
{
|
|
// "%1d:%04X FBA file %s: error in function %s: %s"
|
|
WRMSG( HHC00502, "E", LCSS_DEVNUM,
|
|
dev->filename, "ioctl()", strerror( errno ));
|
|
close (dev->fd);
|
|
dev->fd = -1;
|
|
return -1;
|
|
}
|
|
dev->fbablksiz = 512;
|
|
dev->fbaorigin = 0;
|
|
dev->fbanumblk = statbuf.st_size;
|
|
if (!dev->quiet)
|
|
// "%1d:%04X FBA file %s: REAL FBA opened"
|
|
WRMSG( HHC00504, "I", LCSS_DEVNUM, dev->filename );
|
|
}
|
|
else
|
|
#endif // defined(OPTION_FBA_BLKDEVICE) && defined(BLKGETSIZE)
|
|
{
|
|
/* Set block size, device origin, and device size in blocks */
|
|
dev->fbablksiz = 512;
|
|
dev->fbaorigin = 0;
|
|
dev->fbanumblk = (int)(statbuf.st_size / dev->fbablksiz);
|
|
}
|
|
|
|
/* The second argument SHOULD be the device origin block number */
|
|
if (argc >= 2)
|
|
{
|
|
if (1
|
|
&& str_ne_n( argv[1], "sf=", 3 )
|
|
&& sscanf( argv[1], "%u%c", &startblk, &c ) == 1
|
|
&& startblk < dev->fbanumblk
|
|
)
|
|
{
|
|
dev->fbaorigin = (off_t)startblk;
|
|
dev->fbanumblk -= startblk;
|
|
}
|
|
else // (error)
|
|
{
|
|
if (str_ne_n( argv[1], "sf=", 3 ))
|
|
{
|
|
// "%1d:%04X %s file %s: invalid device origin block number %s"
|
|
WRMSG( HHC00505, "E", LCSS_DEVNUM, FBATYP( cfba, 0 ), dev->filename, argv[1] );
|
|
}
|
|
else
|
|
{
|
|
// "%1d:%04X %s file %s: shadow files not supported for %s dasd"
|
|
WRMSG( HHC00469, "E", LCSS_DEVNUM, FBATYP( cfba, 0 ),
|
|
dev->filename, FBATYP( cfba, 0 ) );
|
|
}
|
|
|
|
close( dev->fd );
|
|
dev->fd = -1;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* The third argument is the device block count */
|
|
if (argc >= 3 && strcmp(argv[2],"*") != 0)
|
|
{
|
|
if (sscanf(argv[2], "%u%c", &numblks, &c) != 1
|
|
|| numblks > dev->fbanumblk)
|
|
{
|
|
// "%1d:%04X %s file %s: invalid device block count %s"
|
|
WRMSG( HHC00506, "E", LCSS_DEVNUM, FBATYP( cfba, 0 ), dev->filename, argv[2] );
|
|
close (dev->fd);
|
|
dev->fd = -1;
|
|
return -1;
|
|
}
|
|
dev->fbanumblk = numblks;
|
|
}
|
|
}
|
|
|
|
dev->fbaend = (dev->fbaorigin + dev->fbanumblk) * dev->fbablksiz;
|
|
|
|
if (!dev->quiet)
|
|
// "%1d:%04X %s file %s: origin %"PRId64", blks %d"
|
|
WRMSG( HHC00507, "I", LCSS_DEVNUM, FBATYP( cfba, 0 ),
|
|
dev->filename, dev->fbaorigin, dev->fbanumblk );
|
|
|
|
/* Set number of sense bytes */
|
|
dev->numsense = 24;
|
|
|
|
/* Locate the FBA dasd table entry */
|
|
dev->fbatab = dasd_lookup (DASD_FBADEV, NULL, dev->devtype, dev->fbanumblk);
|
|
if (dev->fbatab == NULL)
|
|
{
|
|
// "%1d:%04X %s file %s: device type %4.4X not found in dasd table"
|
|
WRMSG( HHC00508, "E", LCSS_DEVNUM, FBATYP( cfba, 0 ), dev->filename, dev->devtype );
|
|
close (dev->fd);
|
|
dev->fd = -1;
|
|
return -1;
|
|
}
|
|
|
|
/* Build the dh_devid area */
|
|
dev->numdevid = dasd_build_fba_devid (dev->fbatab,(BYTE *)&dev->devid);
|
|
|
|
/* Build the devchar area */
|
|
dev->numdevchar = dasd_build_fba_devchar (dev->fbatab,
|
|
(BYTE *)&dev->devchar,dev->fbanumblk);
|
|
|
|
/* Initialize current blkgrp and cache entry */
|
|
dev->bufcur = dev->cache = -1;
|
|
|
|
/* Activate I/O tracing */
|
|
// dev->ccwtrace = 1;
|
|
|
|
/* Call the compressed init handler if compressed fba */
|
|
if (cfba)
|
|
return cckd_dasd_init_handler (dev, argc, argv);
|
|
|
|
return 0;
|
|
} /* end function fba_dasd_init_handler */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Query the device definition */
|
|
/*-------------------------------------------------------------------*/
|
|
void fba_dasd_query_device (DEVBLK *dev, char **devclass,
|
|
int buflen, char *buffer)
|
|
{
|
|
char filename[ PATH_MAX + 1 ];/* full path or just name */
|
|
CCKD_EXT* cckd; /* CCKD Extension Block */
|
|
|
|
BEGIN_DEVICE_CLASS_QUERY( "DASD", dev, devclass, buflen, buffer );
|
|
|
|
cckd = dev->cckd_ext;
|
|
|
|
if (!cckd)
|
|
{
|
|
snprintf( buffer, buflen, "%s%s [%"PRId64",%d] IO[%"PRIu64"]",
|
|
dev->cckd64 ? "*64* " : "",
|
|
filename,
|
|
dev->fbaorigin, dev->fbanumblk,
|
|
dev->excps);
|
|
}
|
|
else
|
|
{
|
|
snprintf( buffer, buflen, "%s%s [%"PRId64",%d] [%d sfs] IO[%"PRIu64"]",
|
|
dev->cckd64 ? "*64* " : "",
|
|
filename,
|
|
dev->fbaorigin, dev->fbanumblk,
|
|
cckd->sfn,
|
|
dev->excps);
|
|
}
|
|
|
|
} /* end function fba_dasd_query_device */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Calculate length of an FBA block group */
|
|
/*-------------------------------------------------------------------*/
|
|
static int fba_blkgrp_len (DEVBLK *dev, S64 blkgrp)
|
|
{
|
|
off_t offset; /* Offset of block group */
|
|
|
|
offset = blkgrp * CFBA_BLKGRP_SIZE;
|
|
|
|
if (dev->fbaend - offset < CFBA_BLKGRP_SIZE)
|
|
return (int)(dev->fbaend - offset);
|
|
else
|
|
return CFBA_BLKGRP_SIZE;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read fba block(s) */
|
|
/*-------------------------------------------------------------------*/
|
|
static int fba_read (DEVBLK *dev, BYTE *buf, int len, BYTE *unitstat)
|
|
{
|
|
int rc; /* Return code */
|
|
int blkgrp; /* Block group number */
|
|
int blklen; /* Length left in block group*/
|
|
int off; /* Device buffer offset */
|
|
int bufoff; /* Buffer offset */
|
|
int copylen; /* Length left to copy */
|
|
|
|
/* Command reject if referencing outside the volume */
|
|
if (dev->fbarba < dev->fbaorigin * dev->fbablksiz
|
|
|| dev->fbarba + len > dev->fbaend)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
/* Read the block group */
|
|
blkgrp = (int)(dev->fbarba / CFBA_BLKGRP_SIZE);
|
|
rc = (dev->hnd->read) (dev, blkgrp, unitstat);
|
|
if (rc < 0)
|
|
return -1;
|
|
|
|
off = dev->fbarba % CFBA_BLKGRP_SIZE;
|
|
blklen = dev->buflen - off;
|
|
|
|
/* Initialize target buffer offset and length to copy */
|
|
bufoff = 0;
|
|
copylen = len;
|
|
|
|
/* Copy from the device buffer to the target buffer */
|
|
while (copylen > 0)
|
|
{
|
|
int len = copylen < blklen ? copylen : blklen;
|
|
|
|
/* Copy to the target buffer */
|
|
if (buf) memcpy (buf + bufoff, dev->buf + off, len);
|
|
|
|
/* Update offsets and lengths */
|
|
bufoff += len;
|
|
copylen -= blklen;
|
|
|
|
/* Read the next block group if still more to copy */
|
|
if (copylen > 0)
|
|
{
|
|
blkgrp++;
|
|
off = 0;
|
|
rc = (dev->hnd->read) (dev, blkgrp, unitstat);
|
|
if (rc < 0)
|
|
return -1;
|
|
blklen = fba_blkgrp_len (dev, blkgrp);
|
|
}
|
|
}
|
|
|
|
/* Update the rba */
|
|
dev->fbarba += len;
|
|
|
|
return len;
|
|
} /* end function fba_read */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write fba block(s) */
|
|
/*-------------------------------------------------------------------*/
|
|
static int fba_write (DEVBLK *dev, BYTE *buf, int len, BYTE *unitstat)
|
|
{
|
|
int rc; /* Return code */
|
|
int blkgrp; /* Block group number */
|
|
int blklen; /* Length left in block group*/
|
|
int off; /* Target buffer offset */
|
|
int bufoff; /* Source buffer offset */
|
|
int copylen; /* Length left to copy */
|
|
|
|
/* Command reject if referencing outside the volume */
|
|
if (dev->fbarba < dev->fbaorigin * dev->fbablksiz
|
|
|| dev->fbarba + len > dev->fbaend)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
/* Read the block group */
|
|
blkgrp = (int)(dev->fbarba / CFBA_BLKGRP_SIZE);
|
|
rc = (dev->hnd->read) (dev, blkgrp, unitstat);
|
|
if (rc < 0)
|
|
return -1;
|
|
|
|
off = dev->fbarba % CFBA_BLKGRP_SIZE;
|
|
blklen = dev->buflen - off;
|
|
|
|
/* Initialize source buffer offset and length to copy */
|
|
bufoff = 0;
|
|
copylen = len;
|
|
|
|
/* Copy to the device buffer from the target buffer */
|
|
while (copylen > 0)
|
|
{
|
|
int len = copylen < blklen ? copylen : blklen;
|
|
|
|
/* Write to the block group */
|
|
rc = (dev->hnd->write) (dev, blkgrp, off, buf + bufoff,
|
|
len, unitstat);
|
|
if (rc < 0)
|
|
return -1;
|
|
|
|
/* Update offsets and lengths */
|
|
bufoff += len;
|
|
copylen -= len;
|
|
blkgrp++;
|
|
off = 0;
|
|
blklen = fba_blkgrp_len (dev, blkgrp);
|
|
}
|
|
|
|
/* Update the rba */
|
|
dev->fbarba += len;
|
|
|
|
return len;
|
|
} /* end function fba_write */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* FBA read block group exit */
|
|
/*-------------------------------------------------------------------*/
|
|
static
|
|
int fba_dasd_read_blkgrp (DEVBLK *dev, int blkgrp, BYTE *unitstat)
|
|
{
|
|
int rc; /* Return code */
|
|
int i, o; /* Cache indexes */
|
|
int len; /* Length to read */
|
|
off_t offset; /* File offsets */
|
|
|
|
/* Return if reading the same block group */
|
|
if (blkgrp >= 0 && blkgrp == dev->bufcur)
|
|
return 0;
|
|
|
|
/* Write the previous block group if modified */
|
|
if (dev->bufupd)
|
|
{
|
|
dev->bufupd = 0;
|
|
|
|
/* Seek to the old block group offset */
|
|
offset = (off_t)(((S64)dev->bufcur * CFBA_BLKGRP_SIZE) + dev->bufupdlo);
|
|
offset = lseek (dev->fd, offset, SEEK_SET);
|
|
if (offset < 0)
|
|
{
|
|
/* Handle seek error condition */
|
|
// "%1d:%04X FBA file %s: error in function %s: %s"
|
|
WRMSG( HHC00502, "E", LCSS_DEVNUM,
|
|
dev->filename, "lseek()", strerror( errno ));
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
cache_lock(CACHE_DEVBUF);
|
|
cache_setflag(CACHE_DEVBUF, dev->cache, ~FBA_CACHE_ACTIVE, 0);
|
|
cache_unlock(CACHE_DEVBUF);
|
|
dev->bufupdlo = dev->bufupdhi = 0;
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
|
|
/* Write the portion of the block group that was modified */
|
|
rc = write (dev->fd, dev->buf + dev->bufupdlo,
|
|
dev->bufupdhi - dev->bufupdlo);
|
|
if (rc < dev->bufupdhi - dev->bufupdlo)
|
|
{
|
|
/* Handle write error condition */
|
|
// "%1d:%04X FBA file %s: error in function %s: %s"
|
|
WRMSG( HHC00502, "E", LCSS_DEVNUM,
|
|
dev->filename, "write()", strerror( errno ));
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
cache_lock(CACHE_DEVBUF);
|
|
cache_setflag(CACHE_DEVBUF, dev->cache, ~FBA_CACHE_ACTIVE, 0);
|
|
cache_unlock(CACHE_DEVBUF);
|
|
dev->bufupdlo = dev->bufupdhi = 0;
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
|
|
dev->bufupdlo = dev->bufupdhi = 0;
|
|
}
|
|
|
|
cache_lock (CACHE_DEVBUF);
|
|
|
|
/* Make the previous cache entry inactive */
|
|
if (dev->cache >= 0)
|
|
cache_setflag(CACHE_DEVBUF, dev->cache, ~FBA_CACHE_ACTIVE, 0);
|
|
dev->bufcur = dev->cache = -1;
|
|
|
|
/* Return on special case when called by the close handler */
|
|
if (blkgrp < 0)
|
|
{
|
|
cache_unlock (CACHE_DEVBUF);
|
|
return 0;
|
|
}
|
|
|
|
fba_read_blkgrp_retry:
|
|
|
|
/* Search the cache */
|
|
i = cache_lookup (CACHE_DEVBUF, FBA_CACHE_SETKEY(dev->devnum, blkgrp), &o);
|
|
|
|
/* Cache hit */
|
|
if (i >= 0)
|
|
{
|
|
cache_setflag(CACHE_DEVBUF, i, ~0, FBA_CACHE_ACTIVE);
|
|
cache_setage(CACHE_DEVBUF, i);
|
|
cache_unlock(CACHE_DEVBUF);
|
|
|
|
// "Thread "TIDPAT" %1d:%04X FBA file %s: read blkgrp %d cache hit, using cache[%d]"
|
|
if (dev->ccwtrace && sysblk.traceFILE)
|
|
tf_0516( dev, blkgrp, i );
|
|
else
|
|
LOGDEVTR( HHC00516, "I", dev->filename, blkgrp, i );
|
|
|
|
dev->cachehits++;
|
|
dev->cache = i;
|
|
dev->buf = cache_getbuf(CACHE_DEVBUF, dev->cache, 0);
|
|
dev->bufcur = blkgrp;
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = fba_blkgrp_len (dev, blkgrp);
|
|
dev->buflen = fba_blkgrp_len (dev, blkgrp);
|
|
dev->bufsize = cache_getlen(CACHE_DEVBUF, dev->cache);
|
|
return 0;
|
|
}
|
|
|
|
/* Wait if no available cache entry */
|
|
if (o < 0)
|
|
{
|
|
// "Thread "TIDPAT" %1d:%04X FBA file %s: read blkgrp %d no available cache entry, waiting"
|
|
if (dev->ccwtrace && sysblk.traceFILE)
|
|
tf_0517( dev, blkgrp );
|
|
else
|
|
LOGDEVTR( HHC00517, "I", dev->filename, blkgrp );
|
|
dev->cachewaits++;
|
|
cache_wait(CACHE_DEVBUF);
|
|
goto fba_read_blkgrp_retry;
|
|
}
|
|
|
|
/* Cache miss */
|
|
// "Thread "TIDPAT" %1d:%04X FBA file %s: read blkgrp %d cache miss, using cache[%d]"
|
|
if (dev->ccwtrace && sysblk.traceFILE)
|
|
tf_0518( dev, blkgrp, o );
|
|
else
|
|
LOGDEVTR( HHC00518, "I", dev->filename, blkgrp, o );
|
|
|
|
dev->cachemisses++;
|
|
|
|
/* Make this cache entry active */
|
|
cache_setkey (CACHE_DEVBUF, o, FBA_CACHE_SETKEY(dev->devnum, blkgrp));
|
|
cache_setflag(CACHE_DEVBUF, o, 0, FBA_CACHE_ACTIVE|DEVBUF_TYPE_FBA);
|
|
cache_setage (CACHE_DEVBUF, o);
|
|
dev->buf = cache_getbuf(CACHE_DEVBUF, o, CFBA_BLKGRP_SIZE);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
/* Get offset and length */
|
|
offset = (off_t)((S64)blkgrp * CFBA_BLKGRP_SIZE);
|
|
len = fba_blkgrp_len (dev, blkgrp);
|
|
|
|
// "Thread "TIDPAT" %1d:%04X FBA file %s: read blkgrp %d offset %"PRId64" len %d"
|
|
if (dev->ccwtrace && sysblk.traceFILE)
|
|
tf_0519( dev, blkgrp, offset, fba_blkgrp_len( dev, blkgrp ));
|
|
else
|
|
LOGDEVTR( HHC00519, "I", dev->filename, blkgrp, offset, fba_blkgrp_len( dev, blkgrp ));
|
|
|
|
/* Seek to the block group offset */
|
|
offset = lseek (dev->fd, offset, SEEK_SET);
|
|
if (offset < 0)
|
|
{
|
|
/* Handle seek error condition */
|
|
// "%1d:%04X FBA file %s: error in function %s: %s"
|
|
WRMSG( HHC00502, "E", LCSS_DEVNUM,
|
|
dev->filename, "lseek()", strerror( errno ));
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
cache_lock(CACHE_DEVBUF);
|
|
cache_release(CACHE_DEVBUF, o, 0);
|
|
cache_unlock(CACHE_DEVBUF);
|
|
return -1;
|
|
}
|
|
|
|
/* Read the block group */
|
|
rc = read (dev->fd, dev->buf, len);
|
|
if (rc < len)
|
|
{
|
|
/* Handle read error condition */
|
|
// "%1d:%04X FBA file %s: error in function %s: %s"
|
|
WRMSG( HHC00502, "E", LCSS_DEVNUM,
|
|
dev->filename, "read()", rc < 0 ? strerror( errno ) : "unexpected end of file" );
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
cache_lock(CACHE_DEVBUF);
|
|
cache_release(CACHE_DEVBUF, o, 0);
|
|
cache_unlock(CACHE_DEVBUF);
|
|
return -1;
|
|
}
|
|
|
|
dev->cache = o;
|
|
dev->buf = cache_getbuf(CACHE_DEVBUF, dev->cache, 0);
|
|
dev->bufcur = blkgrp;
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = fba_blkgrp_len (dev, blkgrp);
|
|
dev->buflen = fba_blkgrp_len (dev, blkgrp);
|
|
dev->bufsize = cache_getlen(CACHE_DEVBUF, dev->cache);
|
|
|
|
return 0;
|
|
|
|
} /* end function fba_dasd_read_blkgrp */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* FBA update block group exit */
|
|
/*-------------------------------------------------------------------*/
|
|
static
|
|
int fba_dasd_update_blkgrp (DEVBLK *dev, int blkgrp, int off,
|
|
BYTE *buf, int len, BYTE *unitstat)
|
|
{
|
|
int rc; /* Return code */
|
|
|
|
/* Read the block group */
|
|
if (blkgrp != dev->bufcur)
|
|
{
|
|
rc = (dev->hnd->read) (dev, blkgrp, unitstat);
|
|
if (rc < 0)
|
|
{
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Copy to the device buffer */
|
|
if (buf) memcpy (dev->buf + off, buf, len);
|
|
|
|
/* Update high/low offsets */
|
|
if (!dev->bufupd || off < dev->bufupdlo)
|
|
dev->bufupdlo = off;
|
|
if (off + len > dev-> bufupdhi)
|
|
dev->bufupdhi = off + len;
|
|
|
|
/* Indicate block group has been modified */
|
|
if (!dev->bufupd)
|
|
{
|
|
dev->bufupd = 1;
|
|
shared_update_notify (dev, blkgrp);
|
|
}
|
|
|
|
return len;
|
|
} /* end function fba_dasd_update_blkgrp */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Channel program end exit */
|
|
/*-------------------------------------------------------------------*/
|
|
static
|
|
void fba_dasd_end (DEVBLK *dev)
|
|
{
|
|
BYTE unitstat;
|
|
|
|
/* Forces updated buffer to be written */
|
|
(dev->hnd->read) (dev, -1, &unitstat);
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Release cache entries */
|
|
/*-------------------------------------------------------------------*/
|
|
int fbadasd_purge_cache (int *answer, int ix, int i, void *data)
|
|
{
|
|
U16 devnum; /* Cached device number */
|
|
int blkgrp; /* Cached block group */
|
|
DEVBLK *dev = data; /* -> device block */
|
|
|
|
UNREFERENCED(answer);
|
|
FBA_CACHE_GETKEY(i, devnum, blkgrp);
|
|
if (dev->devnum == devnum)
|
|
cache_release (ix, i, CACHE_FREEBUF);
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Close the device */
|
|
/*-------------------------------------------------------------------*/
|
|
int fba_dasd_close_device ( DEVBLK *dev )
|
|
{
|
|
BYTE unitstat;
|
|
|
|
/* Forces updated buffer to be written */
|
|
(dev->hnd->read) (dev, -1, &unitstat);
|
|
|
|
/* Free the cache */
|
|
cache_lock(CACHE_DEVBUF);
|
|
cache_scan(CACHE_DEVBUF, fbadasd_purge_cache, dev);
|
|
cache_unlock(CACHE_DEVBUF);
|
|
|
|
/* Close the device file */
|
|
close (dev->fd);
|
|
dev->fd = -1;
|
|
|
|
dev->buf = NULL;
|
|
dev->bufsize = 0;
|
|
|
|
return 0;
|
|
} /* end function fba_dasd_close_device */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Return used blocks */
|
|
/*-------------------------------------------------------------------*/
|
|
static
|
|
int fba_dasd_used (DEVBLK *dev)
|
|
{
|
|
return dev->fbanumblk;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Execute a Channel Command Word */
|
|
/*-------------------------------------------------------------------*/
|
|
void fba_dasd_execute_ccw ( DEVBLK *dev, BYTE code, BYTE flags,
|
|
BYTE chained, U32 count, BYTE prevcode, int ccwseq,
|
|
BYTE *iobuf, BYTE *more, BYTE *unitstat, U32 *residual )
|
|
{
|
|
int rc; /* Return code */
|
|
int num; /* Number of bytes to move */
|
|
BYTE hexzeroes[512]; /* Bytes for zero fill */
|
|
int rem; /* Byte count for zero fill */
|
|
int repcnt; /* Replication count */
|
|
|
|
/* Reset extent flag at start of CCW chain */
|
|
if (chained == 0)
|
|
dev->fbaxtdef = 0;
|
|
|
|
/* Process depending on CCW opcode */
|
|
switch (code) {
|
|
|
|
case 0x02:
|
|
/*---------------------------------------------------------------*/
|
|
/* READ IPL */
|
|
/*---------------------------------------------------------------*/
|
|
/* Must be first CCW or chained from a previous READ IPL CCW */
|
|
if (chained && prevcode != 0x02)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Zeroize the file mask and set extent for entire device */
|
|
dev->fbamask = 0;
|
|
dev->fbaxblkn = 0;
|
|
dev->fbaxfirst = 0;
|
|
dev->fbaxlast = dev->fbanumblk - 1;
|
|
|
|
/* Seek to start of block zero */
|
|
dev->fbarba = dev->fbaorigin * dev->fbablksiz;
|
|
|
|
/* Overrun if data chaining */
|
|
if ((flags & CCW_FLAGS_CD))
|
|
{
|
|
dev->sense[0] = SENSE_OR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Calculate number of bytes to read and set residual count */
|
|
num = (count < (U32)dev->fbablksiz) ?
|
|
count : (U32)dev->fbablksiz;
|
|
*residual = count - num;
|
|
if (count < (U32)dev->fbablksiz) *more = 1;
|
|
|
|
/* Read physical block into channel buffer */
|
|
rc = fba_read (dev, iobuf, num, unitstat);
|
|
if (rc < num) break;
|
|
|
|
/* Set extent defined flag */
|
|
dev->fbaxtdef = 1;
|
|
|
|
/* Set normal status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0x03:
|
|
/*---------------------------------------------------------------*/
|
|
/* CONTROL NO-OPERATION */
|
|
/*---------------------------------------------------------------*/
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0x41:
|
|
/*---------------------------------------------------------------*/
|
|
/* WRITE */
|
|
/*---------------------------------------------------------------*/
|
|
/* Reject if previous command was not LOCATE */
|
|
if (prevcode != 0x43)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Reject if locate command did not specify write operation */
|
|
if ((dev->fbaoper & FBAOPER_CODE) != FBAOPER_WRITE
|
|
&& (dev->fbaoper & FBAOPER_CODE) != FBAOPER_WRTVRFY)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Prepare a block of zeroes for write padding */
|
|
memset( hexzeroes, 0, sizeof(hexzeroes) );
|
|
|
|
/* Write physical blocks of data to the device */
|
|
while (dev->fbalcnum > 0)
|
|
{
|
|
/* Exit if data chaining and this CCW is exhausted */
|
|
if ((flags & CCW_FLAGS_CD) && count == 0)
|
|
break;
|
|
|
|
/* Overrun if data chaining within a physical block */
|
|
if ((flags & CCW_FLAGS_CD) && count < (U32)dev->fbablksiz)
|
|
{
|
|
dev->sense[0] = SENSE_OR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Calculate number of bytes to write */
|
|
num = (count < (U32)dev->fbablksiz) ?
|
|
count : (U32)dev->fbablksiz;
|
|
if (num < dev->fbablksiz) *more = 1;
|
|
|
|
/* Write physical block from channel buffer */
|
|
if (num > 0)
|
|
{
|
|
rc = fba_write (dev, iobuf, num, unitstat);
|
|
if (rc < num) break;
|
|
}
|
|
|
|
/* Fill remainder of block with zeroes */
|
|
if (num < dev->fbablksiz)
|
|
{
|
|
rem = dev->fbablksiz - num;
|
|
rc = fba_write (dev, hexzeroes, rem, unitstat);
|
|
if (rc < rem) break;
|
|
}
|
|
|
|
/* Prepare to write next block */
|
|
count -= num;
|
|
iobuf += num;
|
|
dev->fbalcnum--;
|
|
|
|
} /* end while */
|
|
|
|
/* Set residual byte count */
|
|
*residual = count;
|
|
if (dev->fbalcnum > 0) *more = 1;
|
|
|
|
/* Set ending status */
|
|
*unitstat |= CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0x42:
|
|
/*---------------------------------------------------------------*/
|
|
/* READ */
|
|
/*---------------------------------------------------------------*/
|
|
/* Reject if previous command was not LOCATE or READ IPL */
|
|
if (prevcode != 0x43 && prevcode != 0x02)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Reject if locate command did not specify read operation */
|
|
if (prevcode != 0x02
|
|
&& (dev->fbaoper & FBAOPER_CODE) != FBAOPER_READ
|
|
&& (dev->fbaoper & FBAOPER_CODE) != FBAOPER_READREP)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Read physical blocks of data from device */
|
|
while (dev->fbalcnum > 0 && count > 0)
|
|
{
|
|
/* Overrun if data chaining within a physical block */
|
|
if ((flags & CCW_FLAGS_CD) && count < (U32)dev->fbablksiz)
|
|
{
|
|
dev->sense[0] = SENSE_OR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Calculate number of bytes to read */
|
|
num = (count < (U32)dev->fbablksiz) ?
|
|
count : (U32)dev->fbablksiz;
|
|
if (num < dev->fbablksiz) *more = 1;
|
|
|
|
/* Read physical block into channel buffer */
|
|
rc = fba_read (dev, iobuf, num, unitstat);
|
|
if (rc < num) break;
|
|
|
|
/* Prepare to read next block */
|
|
count -= num;
|
|
iobuf += num;
|
|
dev->fbalcnum--;
|
|
|
|
} /* end while */
|
|
|
|
/* Set residual byte count */
|
|
*residual = count;
|
|
if (dev->fbalcnum > 0) *more = 1;
|
|
|
|
/* Set ending status */
|
|
*unitstat |= CSW_CE | CSW_DE;
|
|
|
|
break;
|
|
|
|
case 0x43:
|
|
/*---------------------------------------------------------------*/
|
|
/* LOCATE */
|
|
/*---------------------------------------------------------------*/
|
|
/* Calculate residual byte count */
|
|
num = (count < 8) ? count : 8;
|
|
*residual = count - num;
|
|
|
|
/* Control information length must be at least 8 bytes */
|
|
if (count < 8)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* LOCATE must be preceded by DEFINE EXTENT or READ IPL */
|
|
if (dev->fbaxtdef == 0)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Save and validate the operation byte */
|
|
dev->fbaoper = iobuf[0];
|
|
if (dev->fbaoper & FBAOPER_RESV)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Validate and process operation code */
|
|
if ((dev->fbaoper & FBAOPER_CODE) == FBAOPER_WRITE
|
|
|| (dev->fbaoper & FBAOPER_CODE) == FBAOPER_WRTVRFY)
|
|
{
|
|
/* Reject command if mask inhibits all writes */
|
|
if (IS_FBAMASK( WR, NONE ))
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
}
|
|
else if ((dev->fbaoper & FBAOPER_CODE) == FBAOPER_READ
|
|
|| (dev->fbaoper & FBAOPER_CODE) == FBAOPER_READREP)
|
|
{
|
|
}
|
|
else if ((dev->fbaoper & FBAOPER_CODE) == FBAOPER_FMTDEFC)
|
|
{
|
|
/* Reject command if file mask inhibits format writes */
|
|
if (IS_FBAMASK( WR, NOFMT ))
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
}
|
|
else /* Operation code is invalid */
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Byte 1 contains the replication count */
|
|
repcnt = iobuf[1];
|
|
|
|
/* Bytes 2-3 contain the block count */
|
|
dev->fbalcnum = fetch_hw(iobuf + 2);
|
|
|
|
/* Bytes 4-7 contain the displacement of the first block
|
|
relative to the start of the dataset */
|
|
dev->fbalcblk = fetch_fw(iobuf + 4);
|
|
|
|
/* Verify that the block count is non-zero, and that
|
|
the starting and ending blocks fall within the extent */
|
|
if ( dev->fbalcnum == 0
|
|
|| dev->fbalcnum > dev->fbaxlast + 1
|
|
|| dev->fbalcblk < dev->fbaxfirst
|
|
|| dev->fbalcblk > dev->fbaxlast + 1 - dev->fbalcnum)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* For replicated data, verify that the replication count
|
|
is non-zero and is a multiple of the block count */
|
|
if ((dev->fbaoper & FBAOPER_CODE) == FBAOPER_READREP)
|
|
{
|
|
if (repcnt == 0 || repcnt % dev->fbalcnum != 0)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Position device to start of block */
|
|
dev->fbarba = (dev->fbaorigin
|
|
+ dev->fbaxblkn
|
|
+ dev->fbalcblk - dev->fbaxfirst
|
|
) * dev->fbablksiz;
|
|
|
|
// "Thread "TIDPAT" %1d:%04X FBA file %s: positioning to 0x%"PRIX64" %"PRId64
|
|
if (dev->ccwtrace && sysblk.traceFILE)
|
|
tf_0520( dev );
|
|
else
|
|
LOGDEVTR( HHC00520, "I", dev->filename, dev->fbarba, dev->fbarba );
|
|
|
|
/* Return normal status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0x63:
|
|
/*---------------------------------------------------------------*/
|
|
/* DEFINE EXTENT */
|
|
/*---------------------------------------------------------------*/
|
|
/* Calculate residual byte count */
|
|
num = (count < 16) ? count : 16;
|
|
*residual = count - num;
|
|
|
|
/* Control information length must be at least 16 bytes */
|
|
if (count < 16)
|
|
{
|
|
// "%1d:%04X FBA file %s: define extent data too short: %d bytes"
|
|
WRMSG( HHC00509, "E", LCSS_DEVNUM, dev->filename, count );
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Reject if extent previously defined in this CCW chain */
|
|
if (dev->fbaxtdef)
|
|
{
|
|
// "%1d:%04X FBA file %s: second define extent in chain"
|
|
WRMSG( HHC00510, "E", LCSS_DEVNUM, dev->filename );
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Save DX mask byte*/
|
|
dev->fbamask = iobuf[0];
|
|
|
|
/* validate the DX mask byte */
|
|
if (dev->fbamask & FBAMASK_RSRVD)
|
|
{
|
|
// "%1d:%04X FBA file %s: invalid file mask %2.2X"
|
|
WRMSG( HHC00511, "E", LCSS_DEVNUM, dev->filename, dev->fbamask );
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
// VM/ESA sends 0x00000200 0x00000000 0x00000000 0x0001404F
|
|
// /* Verify that bytes 1-3 are zeroes */
|
|
// if (iobuf[1] != 0 || iobuf[2] != 0 || iobuf[3] != 0)
|
|
// {
|
|
// LOGMSG( "fbadasd: invalid reserved bytes %2.2X %2.2X %2.2X\n",
|
|
// iobuf[1], iobuf[2], iobuf[3] );
|
|
// dev->sense[0] = SENSE_CR;
|
|
// *unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
// break;
|
|
// }
|
|
|
|
/* Bytes 4-7 contain the block number of the first block
|
|
of the extent relative to the start of the device */
|
|
dev->fbaxblkn = fetch_fw(iobuf + 4);
|
|
|
|
/* Bytes 8-11 contain the block number of the first block
|
|
of the extent relative to the start of the dataset */
|
|
dev->fbaxfirst = fetch_fw(iobuf + 8);
|
|
|
|
/* Bytes 12-15 contain the block number of the last block
|
|
of the extent relative to the start of the dataset */
|
|
dev->fbaxlast = fetch_fw(iobuf + 12);
|
|
|
|
/* Validate the extent description by checking that the
|
|
ending block is not less than the starting block and
|
|
that the ending block does not exceed the device size */
|
|
if (dev->fbaxlast < dev->fbaxfirst
|
|
|| dev->fbaxblkn > (U32)dev->fbanumblk
|
|
|| dev->fbaxlast - dev->fbaxfirst >= dev->fbanumblk - dev->fbaxblkn)
|
|
{
|
|
// "%1d:%04X FBA file %s: invalid extent: first block %d last block %d numblks %d device size %d"
|
|
WRMSG( HHC00512, "E", LCSS_DEVNUM,
|
|
dev->filename, dev->fbaxfirst, dev->fbaxlast,
|
|
dev->fbaxblkn, dev->fbanumblk );
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Set extent defined flag and return normal status */
|
|
dev->fbaxtdef = 1;
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0x64:
|
|
/*---------------------------------------------------------------*/
|
|
/* READ DEVICE CHARACTERISTICS */
|
|
/*---------------------------------------------------------------*/
|
|
/* Calculate residual byte count */
|
|
num = (count < dev->numdevchar) ? count : dev->numdevchar;
|
|
*residual = count - num;
|
|
if (count < dev->numdevchar) *more = 1;
|
|
|
|
/* Copy device characteristics bytes to channel buffer */
|
|
memcpy (iobuf, dev->devchar, num);
|
|
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0x94:
|
|
/*---------------------------------------------------------------*/
|
|
/* DEVICE RELEASE */
|
|
/*---------------------------------------------------------------*/
|
|
/* Reject if extent previously defined in this CCW chain */
|
|
if (dev->fbaxtdef)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
if (dev->hnd->release) (dev->hnd->release) (dev);
|
|
|
|
OBTAIN_DEVLOCK( dev );
|
|
{
|
|
dev->reserved = 0;
|
|
}
|
|
RELEASE_DEVLOCK( dev );
|
|
|
|
/* Return sense information */
|
|
goto sense;
|
|
|
|
case 0xB4:
|
|
/*---------------------------------------------------------------*/
|
|
/* DEVICE RESERVE */
|
|
/*---------------------------------------------------------------*/
|
|
/* Reject if extent previously defined in this CCW chain */
|
|
if (dev->fbaxtdef)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Reserve device to the ID of the active channel program */
|
|
|
|
OBTAIN_DEVLOCK( dev );
|
|
{
|
|
dev->reserved = 1;
|
|
}
|
|
RELEASE_DEVLOCK( dev );
|
|
|
|
if (dev->hnd->reserve) (dev->hnd->reserve) (dev);
|
|
|
|
/* Return sense information */
|
|
goto sense;
|
|
|
|
case 0x14:
|
|
/*---------------------------------------------------------------*/
|
|
/* UNCONDITIONAL RESERVE */
|
|
/*---------------------------------------------------------------*/
|
|
/* Reject if this is not the first CCW in the chain */
|
|
if (ccwseq > 0)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
break;
|
|
}
|
|
|
|
/* Reserve device to the ID of the active channel program */
|
|
|
|
OBTAIN_DEVLOCK( dev );
|
|
{
|
|
dev->reserved = 1;
|
|
}
|
|
RELEASE_DEVLOCK( dev );
|
|
|
|
if (dev->hnd->reserve) (dev->hnd->reserve) (dev);
|
|
|
|
/* Return sense information */
|
|
goto sense;
|
|
|
|
case 0x04:
|
|
/*---------------------------------------------------------------*/
|
|
/* SENSE */
|
|
/*---------------------------------------------------------------*/
|
|
sense:
|
|
/* Calculate residual byte count */
|
|
num = (count < dev->numsense) ? count : dev->numsense;
|
|
*residual = count - num;
|
|
if (count < dev->numsense) *more = 1;
|
|
|
|
/* Copy device sense bytes to channel I/O buffer */
|
|
memcpy (iobuf, dev->sense, num);
|
|
|
|
/* Clear the device sense bytes */
|
|
memset(dev->sense, 0, sizeof(dev->sense));
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0xE4:
|
|
/*---------------------------------------------------------------*/
|
|
/* SENSE ID */
|
|
/*---------------------------------------------------------------*/
|
|
/* Calculate residual byte count */
|
|
num = (count < dev->numdevid) ? count : dev->numdevid;
|
|
*residual = count - num;
|
|
if (count < dev->numdevid) *more = 1;
|
|
|
|
/* Copy device identifier bytes to channel I/O buffer */
|
|
memcpy (iobuf, dev->devid, num);
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
case 0xA4:
|
|
/*---------------------------------------------------------------*/
|
|
/* READ AND RESET BUFFERED LOG */
|
|
/*---------------------------------------------------------------*/
|
|
/* Calculate residual byte count */
|
|
num = (count < 24) ? count : 24;
|
|
*residual = count - num;
|
|
if (count < 24) *more = 1;
|
|
|
|
/* Copy device identifier bytes to channel I/O buffer */
|
|
memset( iobuf, 0, num );
|
|
|
|
/* Return unit status */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
break;
|
|
|
|
default:
|
|
/*---------------------------------------------------------------*/
|
|
/* INVALID OPERATION */
|
|
/*---------------------------------------------------------------*/
|
|
/* Set command reject sense byte, and unit check status */
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
|
|
} /* end switch(code) */
|
|
|
|
} /* end function fba_dasd_execute_ccw */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Read Standard Block (used by Diagnose instructions) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void fbadasd_read_block
|
|
( DEVBLK *dev, int blknum, int blksize, int blkfactor,
|
|
BYTE *iobuf, BYTE *unitstat, U32 *residual )
|
|
{
|
|
int rc; /* Return code */
|
|
int sector; /* First sector being read */
|
|
|
|
/* Unit check if block number is invalid */
|
|
sector = blknum * blkfactor;
|
|
if (sector >= dev->fbanumblk)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return;
|
|
}
|
|
|
|
/* Seek to start of desired block */
|
|
dev->fbarba = ( dev->fbaorigin + sector ) * dev->fbablksiz;
|
|
|
|
/* Read block into I/O buffer */
|
|
rc = fba_read (dev, iobuf, blksize, unitstat);
|
|
if (rc < blksize)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return;
|
|
}
|
|
|
|
/* Return unit status and residual byte count */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
*residual = 0;
|
|
|
|
} /* end function fbadasd_read_block */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Write Standard Block (used by Diagnose instructions) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void fbadasd_write_block (
|
|
DEVBLK *dev, int blknum, int blksize, int blkfactor,
|
|
BYTE *iobuf, BYTE *unitstat, U32 *residual )
|
|
{
|
|
int rc; /* Return code from write function */
|
|
int sector; /* First sector being read */
|
|
|
|
/* Unit check if block number is invalid */
|
|
sector = blknum * blkfactor;
|
|
if (sector >= dev->fbanumblk || sector < 0 )
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return;
|
|
}
|
|
|
|
/* Seek to start of desired block */
|
|
dev->fbarba = (off_t)(( dev->fbaorigin + sector ) * dev->fbablksiz);
|
|
|
|
/* Read block into I/O buffer */
|
|
rc = fba_write (dev, iobuf, blksize, unitstat);
|
|
if (rc < blksize)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return;
|
|
}
|
|
|
|
/* Return unit status and residual byte count */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
*residual = 0;
|
|
|
|
} /* end function fbadasd_write_block */
|
|
|
|
/* Deprecated: Will be replaced by fbadasd_read/write_block functions */
|
|
/*-------------------------------------------------------------------*/
|
|
/* Synchronous Fixed Block I/O (used by Diagnose instruction) */
|
|
/*-------------------------------------------------------------------*/
|
|
DLL_EXPORT void fbadasd_syncblk_io ( DEVBLK *dev, BYTE type, int blknum,
|
|
int blksize, BYTE *iobuf, BYTE *unitstat, U32 *residual )
|
|
{
|
|
int rc; /* Return code */
|
|
int blkfactor; /* Number of device blocks
|
|
per logical block */
|
|
|
|
/* Calculate the blocking factor */
|
|
blkfactor = blksize / dev->fbablksiz;
|
|
|
|
/* Unit check if block number is invalid */
|
|
if (blknum * blkfactor >= dev->fbanumblk)
|
|
{
|
|
dev->sense[0] = SENSE_CR;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return;
|
|
}
|
|
|
|
/* Seek to start of desired block */
|
|
dev->fbarba = dev->fbaorigin * dev->fbablksiz;
|
|
|
|
/* Process depending on operation type */
|
|
switch (type) {
|
|
|
|
case 0x01:
|
|
/* Write block from I/O buffer */
|
|
rc = fba_write (dev, iobuf, blksize, unitstat);
|
|
if (rc < blksize) return;
|
|
break;
|
|
|
|
case 0x02:
|
|
/* Read block into I/O buffer */
|
|
rc = fba_read (dev, iobuf, blksize, unitstat);
|
|
if (rc < blksize) return;
|
|
break;
|
|
|
|
} /* end switch(type) */
|
|
|
|
/* Return unit status and residual byte count */
|
|
*unitstat = CSW_CE | CSW_DE;
|
|
*residual = 0;
|
|
|
|
} /* end function fbadasd_syncblk_io */
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Hercules suspend/resume text unit key values */
|
|
/*-------------------------------------------------------------------*/
|
|
#define SR_DEV_FBA_BUFCUR ( SR_DEV_FBA | 0x001 )
|
|
#define SR_DEV_FBA_BUFOFF ( SR_DEV_FBA | 0x002 )
|
|
#define SR_DEV_FBA_ORIGIN ( SR_DEV_FBA | 0x003 )
|
|
#define SR_DEV_FBA_NUMBLK ( SR_DEV_FBA | 0x004 )
|
|
#define SR_DEV_FBA_RBA ( SR_DEV_FBA | 0x005 )
|
|
#define SR_DEV_FBA_END ( SR_DEV_FBA | 0x006 )
|
|
#define SR_DEV_FBA_DXBLKN ( SR_DEV_FBA | 0x007 )
|
|
#define SR_DEV_FBA_DXFIRST ( SR_DEV_FBA | 0x008 )
|
|
#define SR_DEV_FBA_DXLAST ( SR_DEV_FBA | 0x009 )
|
|
#define SR_DEV_FBA_LCBLK ( SR_DEV_FBA | 0x00a )
|
|
#define SR_DEV_FBA_LCNUM ( SR_DEV_FBA | 0x00b )
|
|
#define SR_DEV_FBA_BLKSIZ ( SR_DEV_FBA | 0x00c )
|
|
#define SR_DEV_FBA_XTDEF ( SR_DEV_FBA | 0x00d )
|
|
#define SR_DEV_FBA_OPER ( SR_DEV_FBA | 0x00e )
|
|
#define SR_DEV_FBA_MASK ( SR_DEV_FBA | 0x00f )
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Hercules suspend */
|
|
/*-------------------------------------------------------------------*/
|
|
int fba_dasd_hsuspend(DEVBLK *dev, void *file)
|
|
{
|
|
if (dev->bufcur >= 0)
|
|
{
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_BUFCUR, dev->bufcur, sizeof( dev->bufcur ));
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_BUFOFF, dev->bufoff, sizeof( dev->bufoff ));
|
|
}
|
|
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_ORIGIN, dev->fbaorigin, sizeof( dev->fbaorigin ));
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_NUMBLK, dev->fbanumblk, sizeof( dev->fbanumblk ));
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_RBA, dev->fbarba, sizeof( dev->fbarba ));
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_END, dev->fbaend, sizeof( dev->fbaend ));
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_DXBLKN, dev->fbaxblkn, sizeof( dev->fbaxblkn ));
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_DXFIRST, dev->fbaxfirst, sizeof( dev->fbaxfirst ));
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_DXLAST, dev->fbaxlast, sizeof( dev->fbaxlast ));
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_LCBLK, dev->fbalcblk, sizeof( dev->fbalcblk ));
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_LCNUM, dev->fbalcnum, sizeof( dev->fbalcnum ));
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_BLKSIZ, dev->fbablksiz, sizeof( dev->fbablksiz ));
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_XTDEF, dev->fbaxtdef, 1 );
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_OPER, dev->fbaoper, sizeof( dev->fbaoper ));
|
|
SR_WRITE_VALUE( file, SR_DEV_FBA_MASK, dev->fbamask, sizeof( dev->fbamask ));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Hercules resume */
|
|
/*-------------------------------------------------------------------*/
|
|
int fba_dasd_hresume(DEVBLK *dev, void *file)
|
|
{
|
|
int rc;
|
|
size_t key, len;
|
|
BYTE byte;
|
|
|
|
do {
|
|
SR_READ_HDR(file, key, len);
|
|
switch (key) {
|
|
case SR_DEV_FBA_BUFCUR:
|
|
SR_READ_VALUE(file, len, &rc, sizeof(rc));
|
|
rc = (dev->hnd->read) ? (dev->hnd->read)(dev, rc, &byte) : -1;
|
|
if ((int)rc < 0) return -1;
|
|
break;
|
|
case SR_DEV_FBA_BUFOFF:
|
|
SR_READ_VALUE(file, len, &dev->bufoff, sizeof(dev->bufoff));
|
|
break;
|
|
case SR_DEV_FBA_ORIGIN:
|
|
SR_READ_VALUE(file, len, &rc, sizeof(rc));
|
|
if ((U64)rc != dev->fbaorigin)
|
|
{
|
|
// "%1d:%04X FBA file %s: FBA origin mismatch: %d, expected %d,"
|
|
WRMSG( HHC00513, "E", LCSS_DEVNUM,
|
|
dev->filename, rc, (int)dev->fbaorigin );
|
|
return -1;
|
|
}
|
|
break;
|
|
case SR_DEV_FBA_NUMBLK:
|
|
SR_READ_VALUE(file, len, &rc, sizeof(rc));
|
|
if ((int)rc != dev->fbanumblk)
|
|
{
|
|
// "%1d:%04X FBA file %s: FBA numblk mismatch: %d, expected %d,"
|
|
WRMSG( HHC00514, "E", LCSS_DEVNUM,
|
|
dev->filename, rc, dev->fbanumblk );
|
|
return -1;
|
|
}
|
|
break;
|
|
case SR_DEV_FBA_RBA:
|
|
SR_READ_VALUE(file, len, &dev->fbarba, sizeof(dev->fbarba));
|
|
break;
|
|
case SR_DEV_FBA_END:
|
|
SR_READ_VALUE(file, len, &dev->fbaend, sizeof(dev->fbaend));
|
|
break;
|
|
case SR_DEV_FBA_DXBLKN:
|
|
SR_READ_VALUE(file, len, &dev->fbaxblkn, sizeof(dev->fbaxblkn));
|
|
break;
|
|
case SR_DEV_FBA_DXFIRST:
|
|
SR_READ_VALUE(file, len, &dev->fbaxfirst, sizeof(dev->fbaxfirst));
|
|
break;
|
|
case SR_DEV_FBA_DXLAST:
|
|
SR_READ_VALUE(file, len, &dev->fbaxlast, sizeof(dev->fbaxlast));
|
|
break;
|
|
case SR_DEV_FBA_LCBLK:
|
|
SR_READ_VALUE(file, len, &dev->fbalcblk, sizeof(dev->fbalcblk));
|
|
break;
|
|
case SR_DEV_FBA_LCNUM:
|
|
SR_READ_VALUE(file, len, &dev->fbalcnum, sizeof(dev->fbalcnum));
|
|
break;
|
|
case SR_DEV_FBA_BLKSIZ:
|
|
SR_READ_VALUE(file, len, &rc, sizeof(rc));
|
|
if ((int)rc != dev->fbablksiz)
|
|
{
|
|
// "%1d:%04X FBA file %s: FBA blksiz mismatch: %d, expected %d,"
|
|
WRMSG( HHC00515, "E", LCSS_DEVNUM,
|
|
dev->filename, rc, dev->fbablksiz );
|
|
return -1;
|
|
}
|
|
break;
|
|
case SR_DEV_FBA_XTDEF:
|
|
SR_READ_VALUE(file, len, &rc, sizeof(rc));
|
|
dev->fbaxtdef = rc;
|
|
break;
|
|
case SR_DEV_FBA_OPER:
|
|
SR_READ_VALUE(file, len, &dev->fbaoper, sizeof(dev->fbaoper));
|
|
break;
|
|
case SR_DEV_FBA_MASK:
|
|
SR_READ_VALUE(file, len, &dev->fbamask, sizeof(dev->fbamask));
|
|
break;
|
|
default:
|
|
SR_READ_SKIP(file, len);
|
|
break;
|
|
} /* switch (key) */
|
|
} while ((key & SR_DEV_MASK) == SR_DEV_FBA);
|
|
return 0;
|
|
}
|
|
|
|
DLL_EXPORT DEVHND fba_dasd_device_hndinfo = {
|
|
&fba_dasd_init_handler, /* Device Initialization */
|
|
&fba_dasd_execute_ccw, /* Device CCW execute */
|
|
&fba_dasd_close_device, /* Device Close */
|
|
&fba_dasd_query_device, /* Device Query */
|
|
NULL, /* Device Extended Query */
|
|
NULL, /* Device Start channel pgm */
|
|
&fba_dasd_end, /* Device End channel pgm */
|
|
NULL, /* Device Resume channel pgm */
|
|
&fba_dasd_end, /* Device Suspend channel pgm */
|
|
NULL, /* Device Halt channel pgm */
|
|
&fba_dasd_read_blkgrp, /* Device Read */
|
|
&fba_dasd_update_blkgrp, /* Device Write */
|
|
&fba_dasd_used, /* Device Query used */
|
|
NULL, /* Device Reserve */
|
|
NULL, /* Device Release */
|
|
NULL, /* Device Attention */
|
|
NULL, /* 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 */
|
|
&fba_dasd_hsuspend, /* Hercules suspend */
|
|
&fba_dasd_hresume /* Hercules resume */
|
|
};
|