Files
gottfriedleibniz d79382bbfe Run codespell on README, build, and source files. (#824)
* 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
2026-04-10 11:01:50 -07:00

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 */
};