mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-11 14:44:35 +02:00
Those of you who have added platform specific tests to Hercules (e.g. #if __APPLE__, #if __SOLARIS__, #if __FreeBSD, etc) PLEASE STOP DOING THAT! Design a configure.ac test for the needed feature or functionality instead (or add the platform specific test to ONLY hostopts.h) and then #define your own HAVE_FEATURE_ABCXYZ and use that instead. Refer to the "_TODO.txt" document for more information and the reason why this is important.
3077 lines
99 KiB
C
3077 lines
99 KiB
C
/* SHARED.C (c)Copyright Greg Smith, 2002-2012 */
|
|
/* Shared Device Server */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
#include "hstdinc.h"
|
|
|
|
#ifdef UNUSED_FUNCTION_WARNING
|
|
#pragma GCC diagnostic ignored "-Wunused-function"
|
|
#endif
|
|
|
|
#define _HERCULES_SHARED_C
|
|
#define _SHARED_C_
|
|
#define _HDASD_DLL_
|
|
#include "hercules.h"
|
|
#include "opcode.h"
|
|
#include "devtype.h"
|
|
|
|
#define FBA_BLKGRP_SIZE (120*512)
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Definitions for sense data format codes and message codes */
|
|
/*-------------------------------------------------------------------*/
|
|
#define FORMAT_0 0 /* Program or System Checks */
|
|
#define FORMAT_1 1 /* Device Equipment Checks */
|
|
#define FORMAT_2 2 /* 3990 Equipment Checks */
|
|
#define FORMAT_3 3 /* 3990 Control Checks */
|
|
#define FORMAT_4 4 /* Data Checks */
|
|
#define FORMAT_5 5 /* Data Check + Displacement */
|
|
#define FORMAT_6 6 /* Usage Stats/Overrun Errors*/
|
|
#define FORMAT_7 7 /* Device Control Checks */
|
|
#define FORMAT_8 8 /* Device Equipment Checks */
|
|
#define FORMAT_9 9 /* Device Rd/Wrt/Seek Checks */
|
|
#define FORMAT_F 15 /* Cache Storage Checks */
|
|
#define MESSAGE_0 0 /* Message 0 */
|
|
#define MESSAGE_1 1 /* Message 1 */
|
|
#define MESSAGE_2 2 /* Message 2 */
|
|
#define MESSAGE_3 3 /* Message 3 */
|
|
#define MESSAGE_4 4 /* Message 4 */
|
|
#define MESSAGE_5 5 /* Message 5 */
|
|
#define MESSAGE_6 6 /* Message 6 */
|
|
#define MESSAGE_7 7 /* Message 7 */
|
|
#define MESSAGE_8 8 /* Message 8 */
|
|
#define MESSAGE_9 9 /* Message 9 */
|
|
#define MESSAGE_A 10 /* Message A */
|
|
#define MESSAGE_B 11 /* Message B */
|
|
#define MESSAGE_C 12 /* Message C */
|
|
#define MESSAGE_D 13 /* Message D */
|
|
#define MESSAGE_E 14 /* Message E */
|
|
#define MESSAGE_F 15 /* Message F */
|
|
|
|
#if defined(OPTION_SHARED_DEVICES)
|
|
|
|
DEVHND shared_ckd_device_hndinfo;
|
|
DEVHND shared_fba_device_hndinfo;
|
|
|
|
static BYTE eighthexFF[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Update notify - called by device handlers for sharable devices
|
|
*-------------------------------------------------------------------*/
|
|
int shared_update_notify (DEVBLK *dev, int block)
|
|
{
|
|
int i, j; /* Indexes */
|
|
|
|
/* Return if no remotes are connected */
|
|
if (dev->shrdconn == 0)
|
|
return 0;
|
|
|
|
for (i = 0; i < SHARED_MAX_SYS; i++)
|
|
{
|
|
|
|
/* Ignore the entry if it doesn't exist or if it's ours
|
|
our if it's already maxed out */
|
|
if (dev->shrd[i] == NULL || dev->shrd[i]->id == dev->shioactive
|
|
|| dev->shrd[i]->purgen < 0)
|
|
continue;
|
|
|
|
/* Check if the block is already entered */
|
|
for (j = 0; j < dev->shrd[i]->purgen; j++)
|
|
if (fetch_fw(dev->shrd[i]->purge[j]) == (U32)block) break;
|
|
|
|
/* Add the block if it's not already there */
|
|
if (j >= dev->shrd[i]->purgen)
|
|
{
|
|
if (dev->shrd[i]->purgen >= SHARED_PURGE_MAX)
|
|
dev->shrd[i]->purgen = -1;
|
|
else
|
|
store_fw (dev->shrd[i]->purge[dev->shrd[i]->purgen++],
|
|
block);
|
|
shrdtrc(dev,"notify %d added for id=%d, n=%d\n",
|
|
block, dev->shrd[i]->id, dev->shrd[i]->purgen);
|
|
}
|
|
|
|
} /* for each possible remote system */
|
|
|
|
return 0;
|
|
|
|
} /* shared_update_notify */
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
* CKD init exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
int shared_ckd_init (DEVBLK *dev, int argc, char *argv[] )
|
|
{
|
|
int rc; /* Return code */
|
|
int i; /* Loop index */
|
|
int retry; /* 1=Connection being retried*/
|
|
char *ipname; /* Remote name or address */
|
|
char *port = NULL; /* Remote port */
|
|
char *rmtnum = NULL; /* Remote device number */
|
|
struct hostent *he; /* -> hostent structure */
|
|
char *kw; /* Argument keyword */
|
|
char *op; /* Argument operand */
|
|
BYTE c; /* Used for parsing */
|
|
char *cu = NULL; /* Specified control unit */
|
|
FWORD cyls; /* Remote number cylinders */
|
|
char *p, buf[1024]; /* Work buffer */
|
|
char *strtok_str = NULL; /* last position */
|
|
|
|
retry = dev->connecting;
|
|
|
|
/* Process the arguments */
|
|
if (!retry)
|
|
{
|
|
if (argc < 1 || strlen(argv[0]) >= sizeof(buf))
|
|
return -1;
|
|
strlcpy( buf, argv[0], sizeof(buf) );
|
|
|
|
/* First argument is `ipname:port:devnum' */
|
|
ipname = buf;
|
|
if (strchr(ipname,'/') || strchr(ipname,'\\'))
|
|
return -1;
|
|
p = strchr (buf, ':');
|
|
if (p)
|
|
{
|
|
*p = '\0';
|
|
port = p + 1;
|
|
p = strchr (port, ':');
|
|
}
|
|
if (p)
|
|
{
|
|
*p = '\0';
|
|
rmtnum = p + 1;
|
|
}
|
|
|
|
#if defined( HAVE_SYS_UN_H )
|
|
if ( strcmp (ipname, "localhost") == 0)
|
|
dev->localhost = 1;
|
|
else
|
|
#endif
|
|
{
|
|
if ( (he = gethostbyname (ipname)) == NULL )
|
|
return -1;
|
|
memcpy(&dev->rmtaddr, he->h_addr_list[0], sizeof(dev->rmtaddr));
|
|
}
|
|
|
|
if (port && strlen(port))
|
|
{
|
|
if (sscanf(port, "%hu%c", &dev->rmtport, &c) != 1)
|
|
return -1;
|
|
}
|
|
else
|
|
dev->rmtport = SHARED_DEFAULT_PORT;
|
|
|
|
if (rmtnum && strlen(rmtnum))
|
|
{
|
|
if (strlen (rmtnum) > 4
|
|
|| sscanf (rmtnum, "%hx%c", &dev->rmtnum, &c) != 1)
|
|
return -1;
|
|
}
|
|
else
|
|
dev->rmtnum = dev->devnum;
|
|
|
|
/* Process the remaining arguments */
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
if (strcasecmp ("readonly", argv[i]) == 0 ||
|
|
strcasecmp ("rdonly", argv[i]) == 0 ||
|
|
strcasecmp ("ro", argv[i]) == 0)
|
|
{
|
|
dev->ckdrdonly = 1;
|
|
continue;
|
|
}
|
|
if (strcasecmp ("fakewrite", argv[i]) == 0 ||
|
|
strcasecmp ("fakewrt", argv[i]) == 0 ||
|
|
strcasecmp ("fw", argv[i]) == 0)
|
|
{
|
|
dev->ckdfakewr = 1;
|
|
continue;
|
|
}
|
|
if (strlen (argv[i]) > 3
|
|
&& memcmp("cu=", argv[i], 3) == 0)
|
|
{
|
|
kw = strtok_r (argv[i], "=", &strtok_str );
|
|
op = strtok_r (NULL, " \t", &strtok_str );
|
|
cu = op;
|
|
continue;
|
|
}
|
|
#ifdef HAVE_LIBZ
|
|
if (strlen (argv[i]) > 5
|
|
&& memcmp("comp=", argv[i], 5) == 0)
|
|
{
|
|
kw = strtok_r (argv[i], "=", &strtok_str );
|
|
op = strtok_r (NULL, " \t", &strtok_str );
|
|
dev->rmtcomp = atoi (op);
|
|
if (dev->rmtcomp < 0 || dev->rmtcomp > 9)
|
|
dev->rmtcomp = 0;
|
|
continue;
|
|
}
|
|
#endif
|
|
WRMSG (HHC00700, "S", argv[i], i + 1);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Set suported compression */
|
|
dev->rmtcomps = 0;
|
|
#ifdef HAVE_LIBZ
|
|
dev->rmtcomps |= SHRD_LIBZ;
|
|
#endif
|
|
#ifdef CCKD_BZIP2
|
|
dev->rmtcomps |= SHRD_BZIP2;
|
|
#endif
|
|
|
|
/* Update the device handler vector */
|
|
dev->hnd = &shared_ckd_device_hndinfo;
|
|
|
|
dev->connecting = 1;
|
|
|
|
init_retry:
|
|
|
|
do {
|
|
rc = clientConnect (dev, retry);
|
|
if (rc < 0)
|
|
{
|
|
WRMSG (HHC00701, "W", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename);
|
|
if (retry) SLEEP(5);
|
|
}
|
|
} while (retry && rc < 0);
|
|
|
|
/* Return if unable to connect */
|
|
if (rc < 0) return 0;
|
|
|
|
dev->ckdnumfd = 1;
|
|
dev->ckdfd[0] = dev->fd;
|
|
|
|
/* Get the number of cylinders */
|
|
rc = clientRequest (dev, cyls, 4, SHRD_QUERY, SHRD_CKDCYLS, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc != 4)
|
|
{
|
|
WRMSG (HHC00702, "S", SSID_TO_LCSS(dev->ssid), dev->devnum);
|
|
return -1;
|
|
}
|
|
dev->ckdcyls = fetch_fw (cyls);
|
|
|
|
/* Get the device characteristics */
|
|
rc = clientRequest (dev, dev->devchar, sizeof(dev->devchar),
|
|
SHRD_QUERY, SHRD_DEVCHAR, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc == 0 || rc > (int)sizeof(dev->devchar))
|
|
{
|
|
WRMSG (HHC00703, "S", SSID_TO_LCSS(dev->ssid), dev->devnum);
|
|
return -1;
|
|
}
|
|
dev->numdevchar = rc;
|
|
|
|
/* Get number of heads from devchar */
|
|
dev->ckdheads = fetch_hw (dev->devchar + 14);
|
|
|
|
/* Calculate number of tracks */
|
|
dev->ckdtrks = dev->ckdcyls * dev->ckdheads;
|
|
dev->ckdhitrk[0] = dev->ckdtrks;
|
|
|
|
/* Check the device type */
|
|
if (dev->devtype == 0)
|
|
dev->devtype = fetch_hw (dev->devchar + 3);
|
|
else if (dev->devtype != fetch_hw (dev->devchar + 3))
|
|
{
|
|
WRMSG (HHC00704, "S", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->rmtnum, fetch_hw (dev->devchar + 3));
|
|
return -1;
|
|
}
|
|
|
|
/* Get the device id */
|
|
rc = clientRequest (dev, dev->devid, sizeof(dev->devid),
|
|
SHRD_QUERY, SHRD_DEVID, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc == 0 || rc > (int)sizeof(dev->devid))
|
|
{
|
|
WRMSG (HHC00705, "S", SSID_TO_LCSS(dev->ssid), dev->devnum);
|
|
return -1;
|
|
}
|
|
dev->numdevid = rc;
|
|
|
|
/* Indicate no active track */
|
|
dev->cache = dev->bufcur = -1;
|
|
dev->buf = NULL;
|
|
|
|
/* Set number of sense bytes */
|
|
dev->numsense = 32;
|
|
|
|
/* Locate the CKD dasd table entry */
|
|
dev->ckdtab = dasd_lookup (DASD_CKDDEV, NULL, dev->devtype, dev->ckdcyls);
|
|
if (dev->ckdtab == NULL)
|
|
{
|
|
WRMSG (HHC00706, "S", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->devtype);
|
|
return -1;
|
|
}
|
|
|
|
/* Set the track size */
|
|
dev->ckdtrksz = (dev->ckdtab->r1 + 511) & ~511;
|
|
|
|
/* Locate the CKD control unit dasd table entry */
|
|
dev->ckdcu = dasd_lookup (DASD_CKDCU, cu ? cu : dev->ckdtab->cu, 0, 0);
|
|
if (dev->ckdcu == NULL)
|
|
{
|
|
WRMSG (HHC00707, "S", SSID_TO_LCSS(dev->ssid), dev->devnum, cu ? cu : dev->ckdtab->cu);
|
|
return -1;
|
|
}
|
|
|
|
/* Set flag bit if 3990 controller */
|
|
if (dev->ckdcu->devt == 0x3990)
|
|
dev->ckd3990 = 1;
|
|
|
|
/* Clear the DPA */
|
|
memset(dev->pgid, 0, sizeof(dev->pgid));
|
|
|
|
/* Request the channel to merge data chained write CCWs into
|
|
a single buffer before passing data to the device handler */
|
|
dev->cdwmerge = 1;
|
|
|
|
/* Purge the cache */
|
|
clientPurge (dev, 0, NULL);
|
|
|
|
/* Log the device geometry */
|
|
if (!dev->batch)
|
|
WRMSG (HHC00708, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, dev->ckdcyls,
|
|
dev->ckdheads, dev->ckdtrks, dev->ckdtrksz);
|
|
|
|
dev->connecting = 0;
|
|
|
|
return 0;
|
|
} /* shared_ckd_init */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* CKD close exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int shared_ckd_close ( DEVBLK *dev )
|
|
{
|
|
/* Purge the cached entries */
|
|
clientPurge (dev, 0, NULL);
|
|
|
|
/* Disconnect and close */
|
|
if (dev->fd >= 0)
|
|
{
|
|
clientRequest (dev, NULL, 0, SHRD_DISCONNECT, 0, NULL, NULL);
|
|
close_socket (dev->fd);
|
|
dev->fd = -1;
|
|
}
|
|
|
|
return 0;
|
|
} /* shared_ckd_close */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* FBA init exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
int shared_fba_init (DEVBLK *dev, int argc, char *argv[] )
|
|
{
|
|
int rc; /* Return code */
|
|
int i; /* Loop index */
|
|
int retry; /* 1=Connection being retried*/
|
|
char *ipname; /* Remote name or address */
|
|
char *port = NULL; /* Remote port */
|
|
char *rmtnum = NULL; /* Remote device number */
|
|
struct hostent *he; /* -> hostent structure */
|
|
char *kw; /* Argument keyword */
|
|
char *op; /* Argument operand */
|
|
char c; /* Work for sscanf */
|
|
FWORD origin; /* FBA origin */
|
|
FWORD numblks; /* FBA number blocks */
|
|
FWORD blksiz; /* FBA block size */
|
|
char *p, buf[1024]; /* Work buffer */
|
|
#ifdef HAVE_LIBZ
|
|
char *strtok_str = NULL; /* last token */
|
|
#endif /*HAVE_LIBZ*/
|
|
|
|
retry = dev->connecting;
|
|
|
|
/* Process the arguments */
|
|
if (!retry)
|
|
{
|
|
|
|
kw = op = NULL;
|
|
|
|
if (argc < 1 || strlen(argv[0]) >= sizeof(buf))
|
|
return -1;
|
|
strlcpy( buf, argv[0], sizeof(buf) );
|
|
|
|
/* First argument is `ipname:port:devnum' */
|
|
ipname = buf;
|
|
p = strchr (buf, ':');
|
|
if (p)
|
|
{
|
|
*p = '\0';
|
|
port = p + 1;
|
|
p = strchr (port, ':');
|
|
}
|
|
if (p)
|
|
{
|
|
*p = '\0';
|
|
rmtnum = p + 1;
|
|
}
|
|
|
|
if ( (he = gethostbyname (ipname)) == NULL )
|
|
return -1;
|
|
memcpy(&dev->rmtaddr, he->h_addr_list[0], sizeof(dev->rmtaddr));
|
|
|
|
if (port)
|
|
{
|
|
if (sscanf(port, "%hu%c", &dev->rmtport, &c) != 1)
|
|
return -1;
|
|
}
|
|
else
|
|
dev->rmtport = SHARED_DEFAULT_PORT;
|
|
|
|
if (rmtnum)
|
|
{
|
|
if (strlen (rmtnum) > 4
|
|
|| sscanf (rmtnum, "%hx%c", &dev->rmtnum, &c) != 0)
|
|
return -1;
|
|
}
|
|
else
|
|
dev->rmtnum = dev->devnum;
|
|
|
|
/* Process the remaining arguments */
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
#ifdef HAVE_LIBZ
|
|
if (strlen (argv[i]) > 5
|
|
&& memcmp("comp=", argv[i], 5) == 0)
|
|
{
|
|
kw = strtok_r (argv[i], "=", &strtok_str );
|
|
op = strtok_r (NULL, " \t", &strtok_str );
|
|
dev->rmtcomp = atoi (op);
|
|
if (dev->rmtcomp < 0 || dev->rmtcomp > 9)
|
|
dev->rmtcomp = 0;
|
|
continue;
|
|
}
|
|
#endif
|
|
WRMSG (HHC00700, "S", argv[i], i + 1);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Set suported compression */
|
|
dev->rmtcomps = 0;
|
|
#ifdef HAVE_LIBZ
|
|
dev->rmtcomps |= SHRD_LIBZ;
|
|
#endif
|
|
#ifdef CCKD_BZIP2
|
|
dev->rmtcomps |= SHRD_BZIP2;
|
|
#endif
|
|
|
|
/* Update the device handler vector */
|
|
dev->hnd = &shared_fba_device_hndinfo;
|
|
|
|
dev->connecting = 1;
|
|
|
|
init_retry:
|
|
|
|
do {
|
|
rc = clientConnect (dev, retry);
|
|
if (rc < 0)
|
|
{
|
|
WRMSG (HHC00701, "W", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename);
|
|
if (retry) SLEEP(5);
|
|
}
|
|
} while (retry && rc < 0);
|
|
|
|
/* Return if unable to connect */
|
|
if (rc < 0) return 0;
|
|
|
|
/* Get the fba origin */
|
|
rc = clientRequest (dev, origin, 4, SHRD_QUERY, SHRD_FBAORIGIN, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc != 4)
|
|
{
|
|
WRMSG (HHC00709, "S", SSID_TO_LCSS(dev->ssid), dev->devnum);
|
|
return -1;
|
|
}
|
|
dev->fbaorigin = fetch_fw (origin);
|
|
|
|
/* Get the number of blocks */
|
|
rc = clientRequest (dev, numblks, 4, SHRD_QUERY, SHRD_FBANUMBLK, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc != 4)
|
|
{
|
|
WRMSG (HHC00710, "S", SSID_TO_LCSS(dev->ssid), dev->devnum);
|
|
return -1;
|
|
}
|
|
dev->fbanumblk = fetch_fw (numblks);
|
|
|
|
/* Get the block size */
|
|
rc = clientRequest (dev, blksiz, 4, SHRD_QUERY, SHRD_FBABLKSIZ, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc != 4)
|
|
{
|
|
WRMSG (HHC00711, "S", SSID_TO_LCSS(dev->ssid), dev->devnum);
|
|
return -1;
|
|
}
|
|
dev->fbablksiz = fetch_fw (blksiz);
|
|
dev->fbaend = (dev->fbaorigin + dev->fbanumblk) * dev->fbablksiz;
|
|
|
|
/* Get the device id */
|
|
rc = clientRequest (dev, dev->devid, sizeof(dev->devid),
|
|
SHRD_QUERY, SHRD_DEVID, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc == 0 || rc > (int)sizeof(dev->devid))
|
|
{
|
|
WRMSG (HHC00705, "S", SSID_TO_LCSS(dev->ssid), dev->devnum);
|
|
return -1;
|
|
}
|
|
dev->numdevid = rc;
|
|
|
|
/* Check the device type */
|
|
if (dev->devtype != fetch_hw (dev->devid + 4))
|
|
{
|
|
WRMSG (HHC00704, "S", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->rmtnum, fetch_hw (dev->devid + 4));
|
|
return -1;
|
|
}
|
|
|
|
/* Get the device characteristics */
|
|
rc = clientRequest (dev, dev->devchar, sizeof(dev->devchar),
|
|
SHRD_QUERY, SHRD_DEVCHAR, NULL, NULL);
|
|
if (rc < 0)
|
|
goto init_retry;
|
|
else if (rc == 0 || rc > (int)sizeof(dev->devchar))
|
|
{
|
|
WRMSG (HHC00703, "S", SSID_TO_LCSS(dev->ssid), dev->devnum);
|
|
return -1;
|
|
}
|
|
dev->numdevchar = rc;
|
|
|
|
/* Indicate no active track */
|
|
dev->cache = dev->bufcur = -1;
|
|
dev->buf = NULL;
|
|
|
|
/* Set number of sense bytes */
|
|
dev->numsense = 32;
|
|
|
|
/* Locate the FBA dasd table entry */
|
|
dev->fbatab = dasd_lookup (DASD_FBADEV, NULL, dev->devtype, dev->fbanumblk);
|
|
if (dev->fbatab == NULL)
|
|
{
|
|
WRMSG (HHC00706, "S", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->devtype);
|
|
return -1;
|
|
}
|
|
|
|
/* Purge the cache */
|
|
clientPurge (dev, 0, NULL);
|
|
|
|
/* Log the device geometry */
|
|
WRMSG (HHC00712, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, (int)dev->fbaorigin, dev->fbanumblk);
|
|
|
|
dev->connecting = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* FBA close exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int shared_fba_close (DEVBLK *dev)
|
|
{
|
|
/* Purge the cached entries */
|
|
clientPurge (dev, 0, NULL);
|
|
|
|
/* Disconnect and close */
|
|
if (dev->fd >= 0)
|
|
{
|
|
clientRequest (dev, NULL, 0, SHRD_DISCONNECT, 0, NULL, NULL);
|
|
close_socket (dev->fd);
|
|
dev->fd = -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Start I/O exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static void shared_start(DEVBLK *dev)
|
|
{
|
|
int rc; /* Return code */
|
|
U16 devnum; /* Cache device number */
|
|
int trk; /* Cache track number */
|
|
int code; /* Response code */
|
|
BYTE buf[SHARED_PURGE_MAX * 4]; /* Purge list */
|
|
|
|
shrdtrc(dev,"start cur %d cache %d\n",dev->bufcur,dev->cache);
|
|
|
|
/* Send the START request */
|
|
rc = clientRequest (dev, buf, sizeof(buf),
|
|
SHRD_START, 0, &code, NULL);
|
|
if (rc < 0)
|
|
{
|
|
WRMSG(HHC00713, "E", SSID_TO_LCSS(dev->ssid), dev->devnum);
|
|
clientPurge (dev, 0, NULL);
|
|
dev->cache = dev->bufcur = -1;
|
|
dev->buf = NULL;
|
|
return;
|
|
}
|
|
|
|
/* Check for purge */
|
|
if (code & SHRD_PURGE)
|
|
{
|
|
if (rc / 4 > SHARED_PURGE_MAX) rc = 0;
|
|
clientPurge (dev, rc / 4, buf);
|
|
}
|
|
|
|
/* Make previous active entry active again */
|
|
if (dev->cache >= 0)
|
|
{
|
|
cache_lock (CACHE_DEVBUF);
|
|
SHRD_CACHE_GETKEY (dev->cache, devnum, trk);
|
|
if (dev->devnum == devnum && dev->bufcur == trk)
|
|
cache_setflag(CACHE_DEVBUF, dev->cache, ~0, SHRD_CACHE_ACTIVE);
|
|
else
|
|
{
|
|
dev->cache = dev->bufcur = -1;
|
|
dev->buf = NULL;
|
|
}
|
|
cache_unlock (CACHE_DEVBUF);
|
|
}
|
|
} /* shared_start */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* End I/O exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static void shared_end (DEVBLK *dev)
|
|
{
|
|
int rc; /* Return code */
|
|
|
|
shrdtrc(dev,"end cur %d cache %d\n",dev->bufcur,dev->cache);
|
|
|
|
/* Write the previous active entry if it was updated */
|
|
if (dev->bufupd)
|
|
clientWrite (dev, dev->bufcur);
|
|
dev->bufupd = 0;
|
|
|
|
/* Mark the active entry inactive */
|
|
if (dev->cache >= 0)
|
|
{
|
|
cache_lock (CACHE_DEVBUF);
|
|
cache_setflag (CACHE_DEVBUF, dev->cache, ~SHRD_CACHE_ACTIVE, 0);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
}
|
|
|
|
/* Send the END request */
|
|
rc = clientRequest (dev, NULL, 0, SHRD_END, 0, NULL, NULL);
|
|
if (rc < 0)
|
|
{
|
|
WRMSG(HHC00714, "E", SSID_TO_LCSS(dev->ssid), dev->devnum);
|
|
clientPurge (dev, 0, NULL);
|
|
dev->cache = dev->bufcur = -1;
|
|
dev->buf = NULL;
|
|
return;
|
|
}
|
|
} /* shared_end */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Shared ckd read track exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int shared_ckd_read (DEVBLK *dev, int trk, BYTE *unitstat)
|
|
{
|
|
int rc; /* Return code */
|
|
int retries = 10; /* Number read retries */
|
|
int cache; /* Lookup index */
|
|
int lru; /* Available index */
|
|
int len; /* Response length */
|
|
int id; /* Response id */
|
|
BYTE *buf; /* Cache buffer */
|
|
BYTE code; /* Response code */
|
|
U16 devnum; /* Response device number */
|
|
BYTE hdr[SHRD_HDR_SIZE + 4]; /* Read request header */
|
|
|
|
/* Initialize the unit status */
|
|
*unitstat = 0;
|
|
|
|
/* Return if reading the same track image */
|
|
if (trk == dev->bufcur && dev->cache >= 0)
|
|
{
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = dev->ckdtrksz;
|
|
return 0;
|
|
}
|
|
|
|
shrdtrc(dev,"ckd_read trk %d\n",trk);
|
|
|
|
/* Write the previous active entry if it was updated */
|
|
if (dev->bufupd)
|
|
clientWrite (dev, dev->bufcur);
|
|
dev->bufupd = 0;
|
|
|
|
/* Reset buffer offsets */
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = dev->ckdtrksz;
|
|
|
|
cache_lock (CACHE_DEVBUF);
|
|
|
|
/* Inactivate the previous image */
|
|
if (dev->cache >= 0)
|
|
cache_setflag (CACHE_DEVBUF, dev->cache, ~SHRD_CACHE_ACTIVE, 0);
|
|
dev->cache = dev->bufcur = -1;
|
|
|
|
cache_retry:
|
|
|
|
/* Lookup the track in the cache */
|
|
cache = cache_lookup (CACHE_DEVBUF, SHRD_CACHE_SETKEY(dev->devnum, trk), &lru);
|
|
|
|
/* Process cache hit */
|
|
if (cache >= 0)
|
|
{
|
|
cache_setflag (CACHE_DEVBUF, cache, ~0, SHRD_CACHE_ACTIVE);
|
|
cache_unlock (CACHE_DEVBUF);
|
|
dev->cachehits++;
|
|
dev->cache = cache;
|
|
dev->buf = cache_getbuf (CACHE_DEVBUF, cache, 0);
|
|
dev->bufcur = trk;
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = dev->ckdtrksz;
|
|
dev->buflen = shared_ckd_trklen (dev, dev->buf);
|
|
dev->bufsize = cache_getlen (CACHE_DEVBUF, cache);
|
|
shrdtrc(dev,"ckd_read trk %d cache hit %d\n",trk,dev->cache);
|
|
return 0;
|
|
}
|
|
|
|
/* Special processing if no available cache entry */
|
|
if (lru < 0)
|
|
{
|
|
shrdtrc(dev,"ckd_read trk %d cache wait\n",trk);
|
|
dev->cachewaits++;
|
|
cache_wait (CACHE_DEVBUF);
|
|
goto cache_retry;
|
|
}
|
|
|
|
/* Process cache miss */
|
|
shrdtrc(dev,"ckd_read trk %d cache miss %d\n",trk,dev->cache);
|
|
dev->cachemisses++;
|
|
cache_setflag (CACHE_DEVBUF, lru, 0, SHRD_CACHE_ACTIVE|DEVBUF_TYPE_SCKD);
|
|
cache_setkey (CACHE_DEVBUF, lru, SHRD_CACHE_SETKEY(dev->devnum, trk));
|
|
cache_setage (CACHE_DEVBUF, lru);
|
|
buf = cache_getbuf (CACHE_DEVBUF, lru, dev->ckdtrksz);
|
|
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
read_retry:
|
|
|
|
/* Send the read request for the track to the remote host */
|
|
SHRD_SET_HDR (hdr, SHRD_READ, 0, dev->rmtnum, dev->rmtid, 4);
|
|
store_fw (hdr + SHRD_HDR_SIZE, trk);
|
|
rc = clientSend (dev, hdr, NULL, 0);
|
|
if (rc < 0)
|
|
{
|
|
ckd_build_sense (dev, SENSE_EC, 0, 0, FORMAT_1, MESSAGE_0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
WRMSG(HHC00715, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, trk);
|
|
return -1;
|
|
}
|
|
|
|
/* Read the track from the remote host */
|
|
rc = clientRecv (dev, hdr, buf, dev->ckdtrksz);
|
|
SHRD_GET_HDR (hdr, code, *unitstat, devnum, id, len);
|
|
if (rc < 0 || code & SHRD_ERROR)
|
|
{
|
|
if (rc < 0 && retries--) goto read_retry;
|
|
ckd_build_sense (dev, SENSE_EC, 0, 0, FORMAT_1, MESSAGE_0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
WRMSG(HHC00715, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, trk);
|
|
return -1;
|
|
}
|
|
|
|
/* Read the sense data if an i/o error occurred */
|
|
if (code & SHRD_IOERR)
|
|
clientRequest (dev, dev->sense, dev->numsense,
|
|
SHRD_SENSE, 0, NULL, NULL);
|
|
|
|
/* Read complete */
|
|
dev->cache = lru;
|
|
dev->buf = cache_getbuf (CACHE_DEVBUF, lru, 0);
|
|
dev->bufcur = trk;
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = dev->ckdtrksz;
|
|
dev->buflen = shared_ckd_trklen (dev, dev->buf);
|
|
dev->bufsize = cache_getlen (CACHE_DEVBUF, lru);
|
|
dev->buf[0] = 0;
|
|
|
|
return 0;
|
|
} /* shared_ckd_read */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Shared ckd write track exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int shared_ckd_write (DEVBLK *dev, int trk, int off,
|
|
BYTE *buf, int len, BYTE *unitstat)
|
|
{
|
|
int rc; /* Return code */
|
|
|
|
/* Immediately return if fake writing */
|
|
if (dev->ckdfakewr)
|
|
return len;
|
|
|
|
/* Error if opened read-only */
|
|
if (dev->ckdrdonly)
|
|
{
|
|
ckd_build_sense (dev, SENSE_EC, SENSE1_WRI, 0,
|
|
FORMAT_1, MESSAGE_0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
shrdtrc(dev,"ckd_write trk %d off %d len %d\n",trk,off,len);
|
|
|
|
/* If the track is not current then read it */
|
|
if (trk != dev->bufcur)
|
|
{
|
|
rc = (dev->hnd->read) (dev, trk, unitstat);
|
|
if (rc < 0)
|
|
{
|
|
dev->bufcur = dev->cache = -1;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Invalid track format if going past buffer end */
|
|
if (off + len > dev->bufoffhi)
|
|
{
|
|
ckd_build_sense (dev, 0, SENSE1_ITF, 0, 0, 0);
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
return -1;
|
|
}
|
|
|
|
/* Copy the data into the buffer */
|
|
if (buf) memcpy (dev->buf + off, buf, len);
|
|
|
|
/* Set low and high updated offsets */
|
|
if (!dev->bufupd || off < dev->bufupdlo)
|
|
dev->bufupdlo = off;
|
|
if (dev->bufoff + len > dev->bufupdhi)
|
|
dev->bufupdhi = off + len;
|
|
|
|
/* Indicate track image has been modified */
|
|
if (!dev->bufupd)
|
|
{
|
|
dev->bufupd = 1;
|
|
shared_update_notify (dev, trk);
|
|
}
|
|
|
|
return len;
|
|
} /* shared_ckd_write */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Return track image length
|
|
*-------------------------------------------------------------------*/
|
|
static int shared_ckd_trklen (DEVBLK *dev, BYTE *buf)
|
|
{
|
|
int sz; /* Size so far */
|
|
|
|
for (sz = CKDDASD_TRKHDR_SIZE;
|
|
memcmp (buf + sz, &eighthexFF, 8) != 0; )
|
|
{
|
|
/* add length of count, key, and data fields */
|
|
sz += CKDDASD_RECHDR_SIZE +
|
|
buf[sz+5] +
|
|
(buf[sz+6] << 8) + buf[sz+7];
|
|
if (sz > dev->ckdtrksz - 8) break;
|
|
}
|
|
|
|
/* add length for end-of-track indicator */
|
|
sz += CKDDASD_RECHDR_SIZE;
|
|
|
|
if (sz > dev->ckdtrksz)
|
|
sz = dev->ckdtrksz;
|
|
|
|
return sz;
|
|
}
|
|
|
|
#if defined( OPTION_SHARED_DEVICES ) && defined(FBA_SHARED)
|
|
/*-------------------------------------------------------------------
|
|
* Shared fba read block exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int shared_fba_read (DEVBLK *dev, int blkgrp, BYTE *unitstat)
|
|
{
|
|
int rc; /* Return code */
|
|
int retries = 10; /* Number read retries */
|
|
int i, o; /* Cache indexes */
|
|
BYTE code; /* Response code */
|
|
U16 devnum; /* Response device number */
|
|
int len; /* Response length */
|
|
int id; /* Response id */
|
|
BYTE hdr[SHRD_HDR_SIZE + 4]; /* Read request header */
|
|
|
|
|
|
/* Return if reading the same block group */
|
|
if (blkgrp >= 0 && blkgrp == dev->bufcur)
|
|
return 0;
|
|
|
|
shrdtrc(dev,"fba_read blkrp %d\n",blkgrp);
|
|
|
|
/* Write the previous active entry if it was updated */
|
|
if (dev->bufupd)
|
|
clientWrite (dev, dev->bufcur);
|
|
dev->bufupd = 0;
|
|
|
|
/* Reset buffer offsets */
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = FBA_BLKGRP_SIZE;
|
|
|
|
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;
|
|
|
|
cache_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, dev->cache, ~0, FBA_CACHE_ACTIVE);
|
|
cache_setage(CACHE_DEVBUF, dev->cache);
|
|
cache_unlock(CACHE_DEVBUF);
|
|
dev->cachehits++;
|
|
dev->cache = i;
|
|
dev->buf = cache_getbuf(CACHE_DEVBUF, dev->cache, 0);
|
|
dev->bufcur = blkgrp;
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = shared_fba_blkgrp_len (dev, blkgrp);
|
|
dev->buflen = shared_fba_blkgrp_len (dev, blkgrp);
|
|
dev->bufsize = cache_getlen(CACHE_DEVBUF, dev->cache);
|
|
shrdtrc(dev,"fba_read blkgrp %d cache hit %d\n",blkgrp,dev->cache);
|
|
return 0;
|
|
}
|
|
|
|
/* Wait if no available cache entry */
|
|
if (o < 0)
|
|
{
|
|
shrdtrc(dev,"fba_read blkgrp %d cache wait\n",blkgrp);
|
|
dev->cachewaits++;
|
|
cache_wait(CACHE_DEVBUF);
|
|
goto cache_retry;
|
|
}
|
|
|
|
/* Cache miss */
|
|
shrdtrc(dev,"fba_read blkgrp %d cache miss %d\n",blkgrp,dev->cache);
|
|
dev->cachemisses++;
|
|
cache_setflag(CACHE_DEVBUF, o, 0, FBA_CACHE_ACTIVE|DEVBUF_TYPE_SFBA);
|
|
cache_setkey (CACHE_DEVBUF, o, FBA_CACHE_SETKEY(dev->devnum, blkgrp));
|
|
cache_setage (CACHE_DEVBUF, o);
|
|
dev->buf = cache_getbuf(CACHE_DEVBUF, o, FBA_BLKGRP_SIZE);
|
|
|
|
cache_unlock (CACHE_DEVBUF);
|
|
|
|
read_retry:
|
|
|
|
/* Send the read request for the blkgrp to the remote host */
|
|
SHRD_SET_HDR (hdr, SHRD_READ, 0, dev->rmtnum, dev->rmtid, 4);
|
|
store_fw (hdr + SHRD_HDR_SIZE, blkgrp);
|
|
rc = clientSend (dev, hdr, NULL, 0);
|
|
if (rc < 0)
|
|
{
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
WRMSG(HHC00716, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, blkgrp);
|
|
return -1;
|
|
}
|
|
|
|
/* Read the blkgrp from the remote host */
|
|
rc = clientRecv (dev, hdr, dev->buf, FBA_BLKGRP_SIZE);
|
|
SHRD_GET_HDR (hdr, code, *unitstat, devnum, id, len);
|
|
if (rc < 0 || code & SHRD_ERROR)
|
|
{
|
|
if (rc < 0 && retries--) goto read_retry;
|
|
dev->sense[0] = SENSE_EC;
|
|
*unitstat = CSW_CE | CSW_DE | CSW_UC;
|
|
WRMSG(HHC00716, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, blkgrp);
|
|
return -1;
|
|
}
|
|
|
|
/* Read the sense data if an i/o error occurred */
|
|
if (code & SHRD_IOERR)
|
|
clientRequest (dev, dev->sense, dev->numsense,
|
|
SHRD_SENSE, 0, NULL, NULL);
|
|
|
|
dev->cache = o;
|
|
dev->buf = cache_getbuf(CACHE_DEVBUF, dev->cache, 0);
|
|
dev->buf[0] = 0;
|
|
dev->bufcur = blkgrp;
|
|
dev->bufoff = 0;
|
|
dev->bufoffhi = shared_fba_blkgrp_len (dev, blkgrp);
|
|
dev->buflen = shared_fba_blkgrp_len (dev, blkgrp);
|
|
dev->bufsize = cache_getlen(CACHE_DEVBUF, dev->cache);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Shared fba write block exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int shared_fba_write (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;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
/* Calculate length of an FBA block group */
|
|
/*-------------------------------------------------------------------*/
|
|
static int shared_fba_blkgrp_len (DEVBLK *dev, int blkgrp)
|
|
{
|
|
off_t offset; /* Offset of block group */
|
|
|
|
offset = blkgrp * FBA_BLKGRP_SIZE;
|
|
if (dev->fbaend - offset < FBA_BLKGRP_SIZE)
|
|
return (int)(dev->fbaend - offset);
|
|
else
|
|
return FBA_BLKGRP_SIZE;
|
|
}
|
|
|
|
#endif /* FBA_SHARED */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Shared usage exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int shared_used (DEVBLK *dev)
|
|
{
|
|
int rc; /* Return code */
|
|
FWORD usage; /* Usage buffer */
|
|
|
|
/* Get usage information */
|
|
rc = clientRequest (dev, usage, 4, SHRD_USED, 0, NULL, NULL);
|
|
if (rc != 4)
|
|
{
|
|
WRMSG (HHC00717, "E", SSID_TO_LCSS(dev->ssid), dev->devnum);
|
|
return -1;
|
|
}
|
|
return fetch_fw (usage);
|
|
} /* shared_used */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Shared reserve exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static void shared_reserve (DEVBLK *dev)
|
|
{
|
|
int rc; /* Return code */
|
|
|
|
/* Issue reserve request */
|
|
rc = clientRequest (dev, NULL, 0, SHRD_RESERVE, 0, NULL, NULL);
|
|
|
|
} /* shared_reserve */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Shared release exit (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static void shared_release (DEVBLK *dev)
|
|
{
|
|
int rc; /* Return code */
|
|
|
|
/* Issue release request */
|
|
rc = clientRequest (dev, NULL, 0, SHRD_RELEASE, 0, NULL, NULL);
|
|
|
|
} /* shared_release */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Write to host
|
|
*
|
|
* NOTE - writes are deferred until a switch occurs or the
|
|
* channel program ends. We are called from either the
|
|
* read exit or the end channel program exit.
|
|
*-------------------------------------------------------------------*/
|
|
static int clientWrite (DEVBLK *dev, int block)
|
|
{
|
|
int rc; /* Return code */
|
|
int retries = 10; /* Number write retries */
|
|
int len; /* Data length */
|
|
BYTE hdr[SHRD_HDR_SIZE + 2 + 4]; /* Write header */
|
|
BYTE code; /* Response code */
|
|
int status; /* Response status */
|
|
int id; /* Response identifier */
|
|
U16 devnum; /* Response device number */
|
|
BYTE errmsg[SHARED_MAX_MSGLEN+1];/* Error message */
|
|
|
|
/* Calculate length to write */
|
|
len = dev->bufupdhi - dev->bufupdlo;
|
|
if (len <= 0 || dev->bufcur < 0)
|
|
{
|
|
dev->bufupdlo = dev->bufupdhi = 0;
|
|
return 0;
|
|
}
|
|
|
|
shrdtrc(dev,"write rcd %d off %d len %d\n",block,dev->bufupdlo,len);
|
|
|
|
write_retry:
|
|
|
|
/* The write request contains a 2 byte offset and 4 byte id,
|
|
followed by the data */
|
|
SHRD_SET_HDR (hdr, SHRD_WRITE, 0, dev->rmtnum, dev->rmtid, len + 6);
|
|
store_hw (hdr + SHRD_HDR_SIZE, dev->bufupdlo);
|
|
store_fw (hdr + SHRD_HDR_SIZE + 2, block);
|
|
|
|
rc = clientSend (dev, hdr, dev->buf + dev->bufupdlo, len);
|
|
if (rc < 0)
|
|
{
|
|
WRMSG(HHC00718, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->bufcur);
|
|
dev->bufupdlo = dev->bufupdhi = 0;
|
|
clientPurge (dev, 0, NULL);
|
|
return -1;
|
|
}
|
|
|
|
/* Get the response */
|
|
rc = clientRecv (dev, hdr, errmsg, sizeof(errmsg));
|
|
SHRD_GET_HDR (hdr, code, status, devnum, id, len);
|
|
if (rc < 0 || (code & SHRD_ERROR) || (code & SHRD_IOERR))
|
|
{
|
|
if (rc < 0 && retries--) goto write_retry;
|
|
WRMSG(HHC00719, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->bufcur, code, status);
|
|
dev->bufupdlo = dev->bufupdhi = 0;
|
|
clientPurge (dev, 0, NULL);
|
|
return -1;
|
|
}
|
|
|
|
dev->bufupdlo = dev->bufupdhi = 0;
|
|
return rc;
|
|
} /* clientWrite */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Purge cache entries (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static void clientPurge (DEVBLK *dev, int n, void *buf)
|
|
{
|
|
cache_lock(CACHE_DEVBUF);
|
|
dev->rmtpurgen = n;
|
|
dev->rmtpurge = (FWORD *)buf;
|
|
cache_scan (CACHE_DEVBUF, clientPurgescan, dev);
|
|
cache_unlock(CACHE_DEVBUF);
|
|
}
|
|
static int clientPurgescan (int *answer, int ix, int i, void *data)
|
|
{
|
|
U16 devnum; /* Cached device number */
|
|
int trk; /* Cached track */
|
|
int p; /* Purge index */
|
|
DEVBLK *dev = data; /* -> device block */
|
|
|
|
UNREFERENCED(answer);
|
|
SHRD_CACHE_GETKEY(i, devnum, trk);
|
|
if (devnum == dev->devnum)
|
|
{
|
|
if (dev->rmtpurgen == 0) {
|
|
cache_release (ix, i, 0);
|
|
shrdtrc(dev,"purge %d\n",trk);
|
|
}
|
|
else
|
|
{
|
|
for (p = 0; p < dev->rmtpurgen; p++)
|
|
{
|
|
if (trk == (int)fetch_fw (dev->rmtpurge[p]))
|
|
{
|
|
shrdtrc(dev,"purge %d\n",trk);
|
|
cache_release (ix, i, 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
} /* clientPurge */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Connect to the server (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int clientConnect (DEVBLK *dev, int retry)
|
|
{
|
|
int rc; /* Return code */
|
|
struct sockaddr *server; /* -> server descriptor */
|
|
int flag; /* Flags (version | release) */
|
|
int len; /* Length server descriptor */
|
|
struct sockaddr_in iserver; /* inet server descriptor */
|
|
#if defined( HAVE_SYS_UN_H )
|
|
struct sockaddr_un userver; /* unix server descriptor */
|
|
#endif
|
|
int retries = 10; /* Number of retries */
|
|
HWORD id; /* Returned identifier */
|
|
HWORD comp; /* Returned compression parm */
|
|
|
|
do {
|
|
|
|
/* Close previous connection */
|
|
if (dev->fd >= 0) close_socket (dev->fd);
|
|
|
|
/* Get a socket */
|
|
if (dev->localhost)
|
|
{
|
|
#if defined( HAVE_SYS_UN_H )
|
|
dev->fd = dev->ckdfd[0] = socket (AF_UNIX, SOCK_STREAM, 0);
|
|
#else // !defined( HAVE_SYS_UN_H )
|
|
dev->fd = dev->ckdfd[0] = -1;
|
|
#endif // defined( HAVE_SYS_UN_H )
|
|
if (dev->fd < 0)
|
|
{
|
|
WRMSG (HHC00720, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "socket()", strerror(HSO_errno));
|
|
return -1;
|
|
}
|
|
#if defined( HAVE_SYS_UN_H )
|
|
userver.sun_family = AF_UNIX;
|
|
sprintf(userver.sun_path, "/tmp/hercules_shared.%d", dev->rmtport);
|
|
server = (struct sockaddr *)&userver;
|
|
len = sizeof(userver);
|
|
#endif // !defined( HAVE_SYS_UN_H )
|
|
}
|
|
else
|
|
{
|
|
dev->fd = dev->ckdfd[0] = socket (AF_INET, SOCK_STREAM, 0);
|
|
if (dev->fd < 0)
|
|
{
|
|
WRMSG (HHC00720, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "socket()", strerror(HSO_errno));
|
|
return -1;
|
|
}
|
|
iserver.sin_family = AF_INET;
|
|
iserver.sin_port = htons(dev->rmtport);
|
|
memcpy(&iserver.sin_addr.s_addr,&dev->rmtaddr,sizeof(struct in_addr));
|
|
server = (struct sockaddr *)&iserver;
|
|
len = sizeof(iserver);
|
|
}
|
|
|
|
/* Connect to the server */
|
|
store_hw (id, dev->rmtid);
|
|
rc = connect (dev->fd, server, len);
|
|
shrdtrc(dev,"connect rc=%d errno=%d\n",rc, HSO_errno);
|
|
if (rc >= 0)
|
|
{
|
|
if (!dev->batch)
|
|
WRMSG(HHC00721, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename);
|
|
|
|
/* Request device connection */
|
|
flag = (SHARED_VERSION << 4) | SHARED_RELEASE;
|
|
rc = clientRequest (dev, id, 2, SHRD_CONNECT, flag, NULL, &flag);
|
|
if (rc >= 0)
|
|
{
|
|
dev->rmtid = fetch_hw (id);
|
|
dev->rmtrel = flag & 0x0f;
|
|
}
|
|
|
|
/*
|
|
* Negotiate compression - top 4 bits have the compression
|
|
* algorithms we support (00010000 -> libz; 00100000 ->bzip2,
|
|
* 00110000 -> both) and the bottom 4 bits indicates the
|
|
* libz parm we want to use when sending data back & forth.
|
|
* If the server returns `0' back, then we won't use libz to
|
|
* compress data to the server. What the `compression
|
|
* algorithms we support' means is that if the data source is
|
|
* cckd or cfba then the server doesn't have to uncompress
|
|
* the data for us if we support the compression algorithm.
|
|
*/
|
|
|
|
if (rc >= 0 && (dev->rmtcomp || dev->rmtcomps))
|
|
{
|
|
rc = clientRequest (dev, comp, 2, SHRD_COMPRESS,
|
|
(dev->rmtcomps << 4) | dev->rmtcomp, NULL, NULL);
|
|
if (rc >= 0)
|
|
dev->rmtcomp = fetch_hw (comp);
|
|
}
|
|
|
|
}
|
|
else if (!retry)
|
|
WRMSG(HHC00722, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename, strerror(HSO_errno));
|
|
|
|
if (rc < 0 && retry) usleep (20000);
|
|
|
|
} while (retry && retries-- && rc < 0);
|
|
|
|
return rc;
|
|
|
|
} /* clientConnect */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Send request to host and get the response
|
|
*
|
|
* No data is sent on the request, buf gets the response.
|
|
* If an uncorrectable connection error occurs -1 is returned.
|
|
* Otherwise *code and *status is set from the response header
|
|
*
|
|
* Since `buf' may be NULL or not very long, response data is
|
|
* received in a temporary buffer. This enables us to receive
|
|
* an error message from the remote system.
|
|
*-------------------------------------------------------------------*/
|
|
static int clientRequest (DEVBLK *dev, BYTE *buf, int len, int cmd,
|
|
int flags, int *code, int *status)
|
|
{
|
|
int rc; /* Return code */
|
|
int retries = 10; /* Number retries */
|
|
BYTE rcode; /* Request return code */
|
|
BYTE rstatus; /* Request return status */
|
|
U16 rdevnum; /* Request return devnum */
|
|
int rid; /* Request return id */
|
|
int rlen; /* Request return length */
|
|
BYTE hdr[SHRD_HDR_SIZE]; /* Header */
|
|
BYTE temp[256]; /* Temporary buffer */
|
|
|
|
retry :
|
|
|
|
/* Send the request */
|
|
SHRD_SET_HDR(hdr, cmd, flags, dev->rmtnum, dev->rmtid, 0);
|
|
shrdtrc(dev,"client_request %2.2x %2.2x %2.2x %d\n",
|
|
cmd,flags,dev->rmtnum,dev->rmtid);
|
|
rc = clientSend (dev, hdr, NULL, 0);
|
|
if (rc < 0) return rc;
|
|
|
|
/* Receive the response */
|
|
rc = clientRecv (dev, hdr, temp, sizeof(temp));
|
|
|
|
/* Retry recv errors */
|
|
if (rc < 0)
|
|
{
|
|
if (cmd != SHRD_CONNECT && retries--)
|
|
{
|
|
SLEEP (1);
|
|
clientConnect (dev, 1);
|
|
goto retry;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* Set code and status */
|
|
SHRD_GET_HDR(hdr, rcode, rstatus, rdevnum, rid, rlen);
|
|
shrdtrc(dev,"client_response %2.2x %2.2x %2.2x %d %d\n",
|
|
rcode,rstatus,rdevnum,rid,rlen);
|
|
if (code) *code = rcode;
|
|
if (status) *status = rstatus;
|
|
|
|
/* Copy the data into the caller's buffer */
|
|
if (buf && len > 0 && rlen > 0)
|
|
memcpy (buf, temp, len < rlen ? len : rlen);
|
|
|
|
return rlen;
|
|
} /* clientRequest */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Send a request to the host
|
|
*
|
|
* `buf' may be NULL
|
|
* `buflen' is the length in `buf' (should be 0 if `buf' is NULL)
|
|
* `hdr' may contain additional data; this is detected by the
|
|
* difference between `buflen' and the length in the header
|
|
*
|
|
* If `buf' is adjacent to `hdr' then `buf' should be NULL
|
|
*
|
|
*-------------------------------------------------------------------*/
|
|
static int clientSend (DEVBLK *dev, BYTE *hdr, BYTE *buf, int buflen)
|
|
{
|
|
int rc; /* Return code */
|
|
BYTE cmd; /* Header command */
|
|
BYTE flag; /* Header flags */
|
|
U16 devnum; /* Header device nu */
|
|
int len; /* Header length */
|
|
int id; /* Header identifier */
|
|
int hdrlen; /* Header length + other data*/
|
|
int off; /* Offset to buffer data */
|
|
BYTE *sendbuf; /* Send buffer */
|
|
int sendlen; /* Send length */
|
|
BYTE cbuf[SHRD_HDR_SIZE + 65536]; /* Combined buffer */
|
|
|
|
/* Make buf, buflen consistent if no additional data to be sent */
|
|
if (buf == NULL) buflen = 0;
|
|
else if (buflen == 0) buf = NULL;
|
|
|
|
/* Calculate length of header, may contain additional data */
|
|
SHRD_GET_HDR(hdr, cmd, flag, devnum, id, len);
|
|
shrdtrc(dev,"client_send %2.2x %2.2x %2.2x %d %d\n",
|
|
cmd,flag,devnum,id,len);
|
|
hdrlen = SHRD_HDR_SIZE + (len - buflen);
|
|
off = len - buflen;
|
|
|
|
if (dev->fd < 0)
|
|
{
|
|
rc = clientConnect (dev, 1);
|
|
if (rc < 0) return -1;
|
|
}
|
|
|
|
#ifdef HAVE_LIBZ
|
|
/* Compress the buf */
|
|
if (dev->rmtcomp != 0
|
|
&& flag == 0 && off <= SHRD_COMP_MAX_OFF
|
|
&& buflen >= SHARED_COMPRESS_MINLEN)
|
|
{
|
|
unsigned long newlen;
|
|
newlen = 65536 - hdrlen;
|
|
memcpy (cbuf, hdr, hdrlen);
|
|
rc = compress2 (cbuf + hdrlen, &newlen,
|
|
buf, buflen, dev->rmtcomp);
|
|
if (rc == Z_OK && (int)newlen < buflen)
|
|
{
|
|
cmd |= SHRD_COMP;
|
|
flag = (SHRD_LIBZ << 4) | off;
|
|
hdr = cbuf;
|
|
hdrlen += newlen;
|
|
buf = NULL;
|
|
buflen = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Combine header and data unless there's no buffer */
|
|
if (buflen == 0)
|
|
{
|
|
sendbuf = hdr;
|
|
sendlen = hdrlen;
|
|
}
|
|
else
|
|
{
|
|
memcpy (cbuf, hdr, hdrlen);
|
|
memcpy (cbuf + hdrlen, buf, buflen);
|
|
sendbuf = cbuf;
|
|
sendlen = hdrlen + buflen;
|
|
}
|
|
|
|
SHRD_SET_HDR(sendbuf, cmd, flag, devnum, id, (U16)(sendlen - SHRD_HDR_SIZE));
|
|
|
|
if (cmd & SHRD_COMP)
|
|
shrdtrc(dev,"client_send %2.2x %2.2x %2.2x %d %d (compressed)\n",
|
|
cmd, flag, devnum, id, (int)(sendlen - SHRD_HDR_SIZE));
|
|
|
|
retry:
|
|
|
|
/* Send the header and data */
|
|
rc = send (dev->fd, sendbuf, sendlen, 0);
|
|
if (rc < 0)
|
|
{
|
|
rc = clientConnect (dev, 0);
|
|
if (rc >= 0) goto retry;
|
|
}
|
|
|
|
/* Process return code */
|
|
if (rc < 0)
|
|
{
|
|
WRMSG(HHC00723, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, cmd, flag, strerror(HSO_errno));
|
|
return -1;
|
|
}
|
|
|
|
return rc;
|
|
|
|
} /* clientSend */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Receive a response (client side)
|
|
*-------------------------------------------------------------------*/
|
|
static int clientRecv (DEVBLK *dev, BYTE *hdr, BYTE *buf, int buflen)
|
|
{
|
|
int rc; /* Return code */
|
|
BYTE code; /* Response code */
|
|
BYTE status; /* Response status */
|
|
U16 devnum; /* Response device number */
|
|
int id; /* Response identifier */
|
|
int len; /* Response length */
|
|
|
|
/* Clear the header to zeroes */
|
|
memset( hdr, 0, SHRD_HDR_SIZE );
|
|
|
|
/* Return error if not connected */
|
|
if (dev->fd < 0)
|
|
{
|
|
WRMSG(HHC00724, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->filename);
|
|
return -1;
|
|
}
|
|
|
|
/* Receive the header */
|
|
rc = recvData (dev->fd, hdr, buf, buflen, 0);
|
|
if (rc < 0)
|
|
{
|
|
if (rc != -ENOTCONN)
|
|
WRMSG(HHC00725, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, strerror(-rc));
|
|
return rc;
|
|
}
|
|
SHRD_GET_HDR(hdr, code, status, devnum, id, len);
|
|
|
|
shrdtrc(dev,"client_recv %2.2x %2.2x %2.2x %d %d\n",
|
|
code,status,devnum,id,len);
|
|
|
|
/* Handle remote logical error */
|
|
if (code & SHRD_ERROR)
|
|
{
|
|
WRMSG(HHC00726, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, code, status, buf);
|
|
len = 0;
|
|
}
|
|
|
|
/* Reset code/status if response was compressed */
|
|
if (len > 0 && code == SHRD_COMP)
|
|
{
|
|
code = SHRD_OK;
|
|
status = 0;
|
|
}
|
|
|
|
/* Reset the header */
|
|
SHRD_SET_HDR(hdr, code, status, devnum, id, len);
|
|
|
|
return len;
|
|
} /* clientRecv */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Receive data (server or client)
|
|
*-------------------------------------------------------------------*/
|
|
static int recvData(int sock, BYTE *hdr, BYTE *buf, int buflen, int server)
|
|
{
|
|
int rc; /* Return code */
|
|
int rlen; /* Data length to recv */
|
|
int recvlen; /* Total length */
|
|
BYTE *recvbuf; /* Receive buffer */
|
|
BYTE cmd; /* Header command */
|
|
BYTE flag; /* Header flags */
|
|
U16 devnum; /* Header device number */
|
|
int id; /* Header identifier */
|
|
int len; /* Header length */
|
|
int comp = 0; /* Compression type */
|
|
int off = 0; /* Offset to compressed data */
|
|
DEVBLK *dev = NULL; /* For `shrdtrc' */
|
|
BYTE cbuf[65536]; /* Compressed buffer */
|
|
|
|
|
|
/* Receive the header */
|
|
for (recvlen = 0; recvlen < (int)SHRD_HDR_SIZE; recvlen += rc)
|
|
{
|
|
rc = recv (sock, hdr + recvlen, SHRD_HDR_SIZE - recvlen, 0);
|
|
if (rc < 0)
|
|
return -HSO_errno;
|
|
else if (rc == 0)
|
|
return -HSO_ENOTCONN;
|
|
}
|
|
SHRD_GET_HDR (hdr, cmd, flag, devnum, id, len);
|
|
|
|
shrdtrc(dev,"recvData %2.2x %2.2x %2.2x %d %d\n",
|
|
cmd, flag, devnum, id, len);
|
|
|
|
/* Return if no data */
|
|
if (len == 0) return 0;
|
|
|
|
/* Check for compressed data */
|
|
if ((server && (cmd & SHRD_COMP))
|
|
|| (!server && cmd == SHRD_COMP))
|
|
{
|
|
comp = (flag & SHRD_COMP_MASK) >> 4;
|
|
off = flag & SHRD_COMP_OFF;
|
|
cmd &= ~SHRD_COMP;
|
|
flag = 0;
|
|
recvbuf = cbuf;
|
|
rlen = len;
|
|
}
|
|
else
|
|
{
|
|
recvbuf = buf;
|
|
rlen = buflen < len ? buflen : len;
|
|
}
|
|
|
|
/* Receive the data */
|
|
for (recvlen = 0; recvlen < rlen; recvlen += rc)
|
|
{
|
|
rc = recv (sock, recvbuf + recvlen, len - recvlen, 0);
|
|
if (rc < 0)
|
|
return -HSO_errno;
|
|
else if (rc == 0)
|
|
return -HSO_ENOTCONN;
|
|
}
|
|
|
|
/* Flush any remaining data */
|
|
for (; rlen < len; rlen += rc)
|
|
{
|
|
BYTE buf[256];
|
|
rc = recv (sock, buf, len - rlen < 256 ? len - rlen : 256, 0);
|
|
if (rc < 0)
|
|
return -HSO_errno;
|
|
else if (rc == 0)
|
|
return -HSO_ENOTCONN;
|
|
}
|
|
|
|
/* Check for compression */
|
|
if (comp == SHRD_LIBZ) {
|
|
#ifdef HAVE_LIBZ
|
|
unsigned long newlen;
|
|
|
|
if (off > 0)
|
|
memcpy (buf, cbuf, off);
|
|
newlen = buflen - off;
|
|
rc = uncompress(buf + off, &newlen, cbuf + off, len - off);
|
|
if (rc == Z_OK)
|
|
recvlen = (int)newlen + off;
|
|
else
|
|
{
|
|
WRMSG(HHC00727, "E", rc, off, len - off);
|
|
recvlen = -1;
|
|
}
|
|
#else
|
|
WRMSG(HHC00728, "E", "libz");
|
|
recvlen = -1;
|
|
#endif
|
|
}
|
|
else if (comp == SHRD_BZIP2)
|
|
{
|
|
#ifdef CCKD_BZIP2
|
|
unsigned int newlen;
|
|
|
|
if (off > 0)
|
|
memcpy (buf, cbuf, off);
|
|
|
|
newlen = buflen - off;
|
|
rc = BZ2_bzBuffToBuffDecompress((void *)(buf + off), &newlen, (void *)(cbuf + off), len - off, 0, 0);
|
|
if (rc == BZ_OK)
|
|
recvlen = (int)newlen + off;
|
|
else
|
|
{
|
|
WRMSG(HHC00727, "E", rc, off, len - off);
|
|
recvlen = -1;
|
|
}
|
|
#else
|
|
WRMSG(HHC00728, "E", "bzip2");
|
|
recvlen = -1;
|
|
#endif
|
|
}
|
|
|
|
if (recvlen > 0)
|
|
{
|
|
SHRD_SET_HDR (hdr, cmd, flag, devnum, id, recvlen);
|
|
if (comp)
|
|
shrdtrc(dev,"recvData %2.2x %2.2x %2.2x %d %d (uncompressed)\n",
|
|
cmd, flag, devnum, id, recvlen);
|
|
}
|
|
|
|
return recvlen;
|
|
|
|
} /* recvData */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Process a request (server side)
|
|
*-------------------------------------------------------------------*/
|
|
static void serverRequest (DEVBLK *dev, int ix, BYTE *hdr, BYTE *buf)
|
|
{
|
|
int rc; /* Return code */
|
|
int i; /* Loop index */
|
|
BYTE cmd; /* Header command */
|
|
BYTE flag; /* Header flags */
|
|
U16 devnum; /* Header device number */
|
|
int id; /* Header identifier */
|
|
int len; /* Header length */
|
|
int code; /* Response code */
|
|
int rcd; /* Record to read/write */
|
|
int off; /* Offset into record */
|
|
|
|
/* Extract header information */
|
|
SHRD_GET_HDR (hdr, cmd, flag, devnum, id, len);
|
|
|
|
shrdtrc(dev,"server_request [%d] %2.2x %2.2x %2.2x %d %d\n",
|
|
ix, cmd, flag, devnum, id, len);
|
|
|
|
dev->shrd[ix]->time = time (NULL);
|
|
|
|
switch (cmd) {
|
|
|
|
case SHRD_CONNECT:
|
|
if (dev->connecting)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_NOTINIT, cmd,
|
|
"device not initialized");
|
|
break;
|
|
}
|
|
if ((flag >> 4) != SHARED_VERSION)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_BADVERS, cmd,
|
|
"shared version mismatch");
|
|
break;
|
|
}
|
|
dev->shrd[ix]->release = flag & 0x0f;
|
|
SHRD_SET_HDR (hdr, 0, (SHARED_VERSION << 4) | SHARED_RELEASE, dev->devnum, id, 2);
|
|
store_hw (buf, id);
|
|
serverSend (dev, ix, hdr, buf, 2);
|
|
break;
|
|
|
|
case SHRD_DISCONNECT:
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 0);
|
|
serverSend (dev, ix, hdr, NULL, 0);
|
|
dev->shrd[ix]->disconnect = 1;
|
|
|
|
obtain_lock (&dev->lock);
|
|
|
|
/* Make the device available if this system active on it */
|
|
if (dev->shioactive == id)
|
|
{
|
|
if (!dev->suspended)
|
|
{
|
|
dev->busy = 0;
|
|
dev->shioactive = DEV_SYS_NONE;
|
|
}
|
|
else
|
|
dev->shioactive = DEV_SYS_LOCAL;
|
|
if (dev->shiowaiters)
|
|
signal_condition (&dev->shiocond);
|
|
}
|
|
|
|
release_lock (&dev->lock);
|
|
break;
|
|
|
|
case SHRD_START:
|
|
case SHRD_RESUME:
|
|
|
|
obtain_lock (&dev->lock);
|
|
|
|
/* If the device is suspended locally then grab it */
|
|
if (dev->shioactive == DEV_SYS_LOCAL && dev->suspended && !dev->reserved)
|
|
dev->shioactive = id;
|
|
|
|
/* Check if the device is busy */
|
|
if (dev->shioactive != id && dev->shioactive != DEV_SYS_NONE)
|
|
{
|
|
shrdtrc(dev,"server_request busy id=%d shioactive=%d reserved=%d\n",
|
|
id,dev->shioactive,dev->reserved);
|
|
/* If the `nowait' bit is on then respond `busy' */
|
|
if (flag & SHRD_NOWAIT)
|
|
{
|
|
release_lock (&dev->lock);
|
|
SHRD_SET_HDR (hdr, SHRD_BUSY, 0, dev->devnum, id, 0);
|
|
serverSend (dev, ix, hdr, NULL, 0);
|
|
break;
|
|
}
|
|
|
|
dev->shrd[ix]->waiting = 1;
|
|
|
|
/* Wait while the device is busy by the local system */
|
|
while (dev->shioactive == DEV_SYS_LOCAL && !dev->suspended)
|
|
{
|
|
dev->shiowaiters++;
|
|
wait_condition (&dev->shiocond, &dev->lock);
|
|
dev->shiowaiters--;
|
|
}
|
|
|
|
/* Return with the `waiting' bit on if busy by a remote system */
|
|
if (dev->shioactive != DEV_SYS_NONE && dev->shioactive != DEV_SYS_LOCAL)
|
|
{
|
|
release_lock (&dev->lock);
|
|
break;
|
|
}
|
|
|
|
dev->shrd[ix]->waiting = 0;
|
|
}
|
|
|
|
/* Make this system active on the device */
|
|
dev->shioactive = id;
|
|
dev->busy = 1;
|
|
#ifdef OPTION_SYNCIO
|
|
dev->syncio_active = dev->syncio_retry = 0;
|
|
#endif // OPTION_SYNCIO
|
|
sysblk.shrdcount++;
|
|
shrdtrc(dev,"server_request active id=%d\n", id);
|
|
|
|
release_lock(&dev->lock);
|
|
|
|
/* Call the i/o start or resume exit */
|
|
if (cmd == SHRD_START && dev->hnd->start)
|
|
(dev->hnd->start) (dev);
|
|
else if (cmd == SHRD_RESUME && dev->hnd->resume)
|
|
(dev->hnd->resume) (dev);
|
|
|
|
/* Get the purge list */
|
|
if (dev->shrd[ix]->purgen == 0)
|
|
code = len = 0;
|
|
else
|
|
{
|
|
code = SHRD_PURGE;
|
|
if (dev->shrd[ix]->purgen < 0)
|
|
len = 0;
|
|
else
|
|
len = 4 * dev->shrd[ix]->purgen;
|
|
}
|
|
|
|
/* Send the response */
|
|
SHRD_SET_HDR (hdr, code, 0, dev->devnum, id, len);
|
|
rc = serverSend (dev, ix, hdr, (BYTE *)dev->shrd[ix]->purge, len);
|
|
if (rc >= 0)
|
|
dev->shrd[ix]->purgen = 0;
|
|
break;
|
|
|
|
case SHRD_END:
|
|
case SHRD_SUSPEND:
|
|
/* Must be active on the device for this command */
|
|
if (dev->shioactive != id)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_NOTACTIVE, cmd,
|
|
"not active on this device");
|
|
break;
|
|
}
|
|
|
|
/* Call the I/O end/suspend exit */
|
|
if (cmd == SHRD_END && dev->hnd->end)
|
|
(dev->hnd->end) (dev);
|
|
else if (cmd == SHRD_SUSPEND && dev->hnd->suspend)
|
|
(dev->hnd->suspend) (dev);
|
|
|
|
obtain_lock (&dev->lock);
|
|
|
|
/* Make the device available if it's not reserved */
|
|
if (!dev->reserved)
|
|
{
|
|
/* If locally suspended then return the device to local */
|
|
if (dev->suspended)
|
|
{
|
|
dev->shioactive = DEV_SYS_LOCAL;
|
|
dev->busy = 1;
|
|
}
|
|
else
|
|
{
|
|
dev->shioactive = DEV_SYS_NONE;
|
|
dev->busy = 0;
|
|
}
|
|
|
|
/* Reset any `waiting' bits */
|
|
for (i = 0; i < SHARED_MAX_SYS; i++)
|
|
if (dev->shrd[i])
|
|
dev->shrd[i]->waiting = 0;
|
|
|
|
/* Notify any waiters */
|
|
if (dev->shiowaiters)
|
|
signal_condition (&dev->shiocond);
|
|
}
|
|
shrdtrc(dev,"server_request inactive id=%d\n", id);
|
|
|
|
release_lock (&dev->lock);
|
|
|
|
/* Send response back */
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 0);
|
|
serverSend (dev, ix, hdr, NULL, 0);
|
|
break;
|
|
|
|
case SHRD_RESERVE:
|
|
/* Must be active on the device for this command */
|
|
if (dev->shioactive != id)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_NOTACTIVE, cmd,
|
|
"not active on this device");
|
|
break;
|
|
}
|
|
|
|
obtain_lock (&dev->lock);
|
|
dev->reserved = 1;
|
|
release_lock (&dev->lock);
|
|
|
|
shrdtrc(dev,"server_request reserved id=%d\n", id);
|
|
|
|
/* Call the I/O reserve exit */
|
|
if (dev->hnd->reserve) (dev->hnd->reserve) (dev);
|
|
|
|
/* Send response back */
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 0);
|
|
serverSend (dev, ix, hdr, NULL, 0);
|
|
|
|
break;
|
|
|
|
case SHRD_RELEASE:
|
|
/* Must be active on the device for this command */
|
|
if (dev->shioactive != id)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_NOTACTIVE, cmd,
|
|
"not active on this device");
|
|
break;
|
|
}
|
|
|
|
/* Call the I/O release exit */
|
|
if (dev->hnd->release) (dev->hnd->release) (dev);
|
|
|
|
obtain_lock (&dev->lock);
|
|
dev->reserved = 0;
|
|
release_lock (&dev->lock);
|
|
|
|
shrdtrc(dev,"server_request released id=%d\n", id);
|
|
|
|
/* Send response back */
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 0);
|
|
serverSend (dev, ix, hdr, NULL, 0);
|
|
|
|
break;
|
|
|
|
case SHRD_READ:
|
|
/* Must be active on the device for this command */
|
|
if (dev->shioactive != id)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_NOTACTIVE, cmd,
|
|
"not active on this device");
|
|
break;
|
|
}
|
|
|
|
/* Set the compressions client is willing to accept */
|
|
dev->comps = dev->shrd[ix]->comps;
|
|
dev->comp = dev->compoff = 0;
|
|
|
|
/* Call the I/O read exit */
|
|
rcd = (int)fetch_fw (buf);
|
|
rc = (dev->hnd->read) (dev, rcd, &flag);
|
|
shrdtrc(dev,"server_request read rcd %d flag %2.2x rc=%d\n",
|
|
rcd, flag, rc);
|
|
|
|
if (rc < 0)
|
|
code = SHRD_IOERR;
|
|
else
|
|
{
|
|
code = dev->comp ? SHRD_COMP : 0;
|
|
flag = (dev->comp << 4) | dev->compoff;
|
|
}
|
|
|
|
/* Reset compression stuff */
|
|
dev->comps = dev->comp = dev->compoff = 0;
|
|
|
|
SHRD_SET_HDR (hdr, code, flag, dev->devnum, id, dev->buflen);
|
|
serverSend (dev, ix, hdr, dev->buf, dev->buflen);
|
|
|
|
break;
|
|
|
|
case SHRD_WRITE:
|
|
/* Must be active on the device for this command */
|
|
if (dev->shioactive != id)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_NOTACTIVE, cmd,
|
|
"not active on this device");
|
|
break;
|
|
}
|
|
|
|
/* Call the I/O write exit */
|
|
off = fetch_hw (buf);
|
|
rcd = fetch_fw (buf + 2);
|
|
|
|
rc = (dev->hnd->write) (dev, rcd, off, buf + 6, len - 6, &flag);
|
|
shrdtrc(dev,"server_request write rcd %d off %d len %d flag %2.2x rc=%d\n",
|
|
rcd, off, len - 6, flag, rc);
|
|
|
|
if (rc < 0)
|
|
code = SHRD_IOERR;
|
|
else
|
|
code = 0;
|
|
|
|
/* Send response back */
|
|
SHRD_SET_HDR (hdr, code, flag, dev->devnum, id, 0);
|
|
serverSend (dev, ix, hdr, NULL, 0);
|
|
|
|
break;
|
|
|
|
case SHRD_SENSE:
|
|
/* Must be active on the device for this command */
|
|
if (dev->shioactive != id)
|
|
{
|
|
serverError (dev, ix, SHRD_ERROR_NOTACTIVE, cmd,
|
|
"not active on this device");
|
|
break;
|
|
}
|
|
|
|
/* Send the sense */
|
|
SHRD_SET_HDR (hdr, 0, CSW_CE | CSW_DE, dev->devnum, id, dev->numsense);
|
|
serverSend (dev, ix, hdr, dev->sense, dev->numsense);
|
|
memset (dev->sense, 0, sizeof(dev->sense));
|
|
dev->sns_pending = 0;
|
|
break;
|
|
|
|
case SHRD_QUERY:
|
|
switch (flag) {
|
|
|
|
case SHRD_USED:
|
|
if (dev->hnd->used)
|
|
rc = (dev->hnd->used) (dev);
|
|
else
|
|
rc = 0;
|
|
store_fw (buf, rc);
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 4);
|
|
serverSend (dev, ix, hdr, buf, 4);
|
|
break;
|
|
|
|
case SHRD_DEVCHAR:
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, dev->numdevchar);
|
|
serverSend (dev, ix, hdr, dev->devchar, dev->numdevchar);
|
|
break;
|
|
|
|
case SHRD_DEVID:
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, dev->numdevid);
|
|
serverSend (dev, ix, hdr, dev->devid, dev->numdevid);
|
|
break;
|
|
|
|
case SHRD_CKDCYLS:
|
|
store_fw (buf, dev->ckdcyls);
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 4);
|
|
serverSend (dev, ix, hdr, buf, 4);
|
|
break;
|
|
|
|
case SHRD_FBAORIGIN:
|
|
store_fw (buf, dev->fbaorigin);
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 4);
|
|
serverSend (dev, ix, hdr, buf, 4);
|
|
break;
|
|
|
|
case SHRD_FBANUMBLK:
|
|
store_fw (buf, dev->fbanumblk);
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 4);
|
|
serverSend (dev, ix, hdr, buf, 4);
|
|
break;
|
|
|
|
case SHRD_FBABLKSIZ:
|
|
store_fw (buf, dev->fbablksiz);
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 4);
|
|
serverSend (dev, ix, hdr, buf, 4);
|
|
break;
|
|
|
|
default:
|
|
serverError (dev, ix, SHRD_ERROR_INVALID, cmd,
|
|
"invalid query request");
|
|
break;
|
|
} /* switch (flag) for SHRD_QUERY */
|
|
break;
|
|
|
|
case SHRD_COMPRESS:
|
|
#ifdef HAVE_LIBZ
|
|
dev->shrd[ix]->comp = (flag & 0x0f);
|
|
store_hw (buf, dev->shrd[ix]->comp);
|
|
#else
|
|
store_hw (buf, 0);
|
|
#endif
|
|
dev->shrd[ix]->comps = (flag & 0xf0) >> 4;
|
|
SHRD_SET_HDR (hdr, 0, 0, dev->devnum, id, 2);
|
|
serverSend (dev, ix, hdr, buf, 2);
|
|
break;
|
|
|
|
default:
|
|
serverError (dev, ix, SHRD_ERROR_INVALID, cmd,
|
|
"invalid request");
|
|
break;
|
|
} /* switch (cmd) */
|
|
} /* serverRequest */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Locate the SHRD block for a socket (server side)
|
|
*-------------------------------------------------------------------*/
|
|
static int serverLocate (DEVBLK *dev, int id, int *avail)
|
|
{
|
|
int i; /* Loop index */
|
|
|
|
if (avail) *avail = -1;
|
|
for (i = 0; i < SHARED_MAX_SYS; i++)
|
|
{
|
|
if (dev->shrd[i])
|
|
{
|
|
if (dev->shrd[i]->id == id)
|
|
return i;
|
|
}
|
|
else if (avail && *avail < 0)
|
|
*avail = i;
|
|
}
|
|
return -1;
|
|
} /* serverLocate */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Return a new Identifier (server side)
|
|
*-------------------------------------------------------------------*/
|
|
static int serverId (DEVBLK *dev)
|
|
{
|
|
int i; /* Loop index */
|
|
int id; /* Identifier */
|
|
|
|
do {
|
|
dev->shrdid += 1;
|
|
dev->shrdid &= 0xffff;
|
|
if (dev->shrdid == DEV_SYS_LOCAL
|
|
|| dev->shrdid == DEV_SYS_NONE)
|
|
dev->shrdid = 1;
|
|
id = dev->shrdid;
|
|
|
|
for (i = 0; i < SHARED_MAX_SYS; i++)
|
|
if (dev->shrd[i] && dev->shrd[i]->id == id)
|
|
break;
|
|
|
|
} while (i < SHARED_MAX_SYS);
|
|
|
|
return id;
|
|
} /* serverId */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Respond with an error message (server side)
|
|
*-------------------------------------------------------------------*/
|
|
static int serverError (DEVBLK *dev, int ix, int code, int status,
|
|
char *msg)
|
|
{
|
|
int rc; /* Return code */
|
|
size_t len; /* Message length */
|
|
BYTE hdr[SHRD_HDR_SIZE]; /* Header */
|
|
|
|
/* Get message length */
|
|
len = strlen(msg) + 1;
|
|
if (len > SHARED_MAX_MSGLEN)
|
|
len = SHARED_MAX_MSGLEN;
|
|
|
|
SHRD_SET_HDR (hdr, code, status, dev ? dev->devnum : 0,
|
|
ix < 0 ? 0 : dev->shrd[ix]->id, (U16)len);
|
|
|
|
shrdtrc(dev,"server_error %2.2x %2.2x: %s\n", code, status, msg);
|
|
|
|
rc = serverSend (dev, ix, hdr, (BYTE *)msg, (int)len);
|
|
return rc;
|
|
|
|
} /* serverError */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Send data (server side)
|
|
*-------------------------------------------------------------------*/
|
|
static int serverSend (DEVBLK *dev, int ix, BYTE *hdr, BYTE *buf,
|
|
int buflen)
|
|
{
|
|
int rc; /* Return code */
|
|
int sock; /* Socket number */
|
|
BYTE code; /* Header code */
|
|
BYTE status; /* Header status */
|
|
U16 devnum; /* Header device number */
|
|
int id; /* Header identifier */
|
|
int len; /* Header length */
|
|
int hdrlen; /* Header length + other data*/
|
|
BYTE *sendbuf = NULL; /* Send buffer */
|
|
int sendlen; /* Send length */
|
|
BYTE cbuf[SHRD_HDR_SIZE + 65536]; /* Combined buffer */
|
|
|
|
/* Make buf, buflen consistent if no additional data to be sent */
|
|
if (buf == NULL) buflen = 0;
|
|
else if (buflen == 0) buf = NULL;
|
|
|
|
/* Calculate length of header, may contain additional data */
|
|
SHRD_GET_HDR(hdr, code, status, devnum, id, len);
|
|
hdrlen = SHRD_HDR_SIZE + (len - buflen);
|
|
sendlen = hdrlen + buflen;
|
|
|
|
/* Check if buf is adjacent to the header */
|
|
if (buf && hdr + hdrlen == buf)
|
|
{
|
|
hdrlen += buflen;
|
|
buf = NULL;
|
|
buflen = 0;
|
|
}
|
|
|
|
/* Send only the header buffer if `buf' is empty */
|
|
if (buflen == 0) sendbuf = hdr;
|
|
|
|
/* Get socket number; if `ix' < 0 we don't have a device yet */
|
|
if (ix >= 0)
|
|
sock = dev->shrd[ix]->fd;
|
|
else
|
|
{
|
|
sock = -ix;
|
|
dev = NULL;
|
|
}
|
|
|
|
shrdtrc(dev,"server_send %2.2x %2.2x %2.2x %d %d\n",
|
|
code, status, devnum, id, len);
|
|
|
|
#ifdef HAVE_LIBZ
|
|
/* Compress the buf */
|
|
if (ix >= 0 && dev->shrd[ix]->comp != 0
|
|
&& code == SHRD_OK && status == 0
|
|
&& hdrlen - SHRD_HDR_SIZE <= SHRD_COMP_MAX_OFF
|
|
&& buflen >= SHARED_COMPRESS_MINLEN)
|
|
{
|
|
unsigned long newlen;
|
|
int off = hdrlen - SHRD_HDR_SIZE;
|
|
sendbuf = cbuf;
|
|
newlen = sizeof(cbuf) - hdrlen;
|
|
memcpy (cbuf, hdr, hdrlen);
|
|
rc = compress2 (cbuf + hdrlen, &newlen,
|
|
buf, buflen, dev->shrd[ix]->comp);
|
|
if (rc == Z_OK && (int)newlen < buflen)
|
|
{
|
|
/* Setup to use the compressed buffer */
|
|
sendlen = hdrlen + newlen;
|
|
buflen = 0;
|
|
code = SHRD_COMP;
|
|
status = (SHRD_LIBZ << 4) | off;
|
|
SHRD_SET_HDR (cbuf, code, status, devnum, id, newlen + off);
|
|
shrdtrc(dev,"server_send %2.2x %2.2x %2.2x %d %d (compressed)\n",
|
|
code,status,devnum,id,(int)newlen+off);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Build combined (hdr + data) buffer */
|
|
if (buflen > 0)
|
|
{
|
|
sendbuf = cbuf;
|
|
memcpy (cbuf, hdr, hdrlen);
|
|
memcpy (cbuf + hdrlen, buf, buflen);
|
|
}
|
|
|
|
/* Send the combined header and data */
|
|
rc = send (sock, sendbuf, sendlen, 0);
|
|
|
|
/* Process return code */
|
|
if (rc < 0)
|
|
{
|
|
WRMSG(HHC00729, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, id, strerror(HSO_errno));
|
|
dev->shrd[ix]->disconnect = 1;
|
|
}
|
|
|
|
return rc;
|
|
|
|
} /* serverSend */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Determine if a client can be disconnected (server side)
|
|
*-------------------------------------------------------------------*/
|
|
static int serverDisconnectable (DEVBLK *dev, int ix) {
|
|
|
|
if (dev->shrd[ix]->waiting || dev->shrd[ix]->pending
|
|
|| dev->shioactive == dev->shrd[ix]->id)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
} /* serverDisconnectable */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Disconnect a client (server side)
|
|
* dev->lock *must* be held
|
|
*-------------------------------------------------------------------*/
|
|
static void serverDisconnect (DEVBLK *dev, int ix) {
|
|
int id; /* Client identifier */
|
|
int i; /* Loop index */
|
|
|
|
id = dev->shrd[ix]->id;
|
|
|
|
//FIXME: Handle a disconnected busy client better
|
|
// Perhaps a disconnect timeout value... this will
|
|
// give the client time to reconnect.
|
|
|
|
/* If the device is active by the client then extricate it.
|
|
This is *not* a good situation */
|
|
if (dev->shioactive == id)
|
|
{
|
|
WRMSG(HHC00730, "W", SSID_TO_LCSS(dev->ssid), dev->devnum, id, dev->reserved ? "reserved" : "");
|
|
|
|
/* Call the I/O release exit if reserved by this client */
|
|
if (dev->reserved && dev->hnd->release)
|
|
(dev->hnd->release) (dev);
|
|
|
|
/* Call the channel program end exit */
|
|
if (dev->hnd->end)
|
|
(dev->hnd->end) (dev);
|
|
|
|
/* Reset any `waiting' bits */
|
|
for (i = 0; i < SHARED_MAX_SYS; i++)
|
|
if (dev->shrd[i])
|
|
dev->shrd[i]->waiting = 0;
|
|
|
|
/* Make the device available */
|
|
if (dev->suspended) {
|
|
dev->shioactive = DEV_SYS_LOCAL;
|
|
dev->busy = 1;
|
|
}
|
|
else
|
|
{
|
|
dev->shioactive = DEV_SYS_NONE;
|
|
dev->busy = 0;
|
|
}
|
|
|
|
/* Notify any waiters */
|
|
if (dev->shiowaiters)
|
|
signal_condition (&dev->shiocond);
|
|
}
|
|
|
|
WRMSG(HHC00731, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->shrd[ix]->ipaddr, id);
|
|
|
|
/* Release the SHRD block */
|
|
close_socket (dev->shrd[ix]->fd);
|
|
free (dev->shrd[ix]->ipaddr);
|
|
free (dev->shrd[ix]);
|
|
dev->shrd[ix] = NULL;
|
|
|
|
dev->shrdconn--;
|
|
} /* serverDisconnect */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Return client ip
|
|
*-------------------------------------------------------------------*/
|
|
static char *clientip (int sock)
|
|
{
|
|
int rc; /* Return code */
|
|
struct sockaddr_in client; /* Client address structure */
|
|
socklen_t namelen; /* Length of client structure*/
|
|
|
|
namelen = sizeof(client);
|
|
rc = getpeername (sock, (struct sockaddr *)&client, &namelen);
|
|
return inet_ntoa(client.sin_addr);
|
|
|
|
} /* clientip */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Find device by device number
|
|
*-------------------------------------------------------------------*/
|
|
static DEVBLK *findDevice (U16 devnum)
|
|
{
|
|
DEVBLK *dev; /* -> Device block */
|
|
|
|
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
|
|
if (dev->devnum == devnum) break;
|
|
return dev;
|
|
|
|
} /* findDevice */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Connect a new client
|
|
*-------------------------------------------------------------------*/
|
|
static void *serverConnect (void *psock)
|
|
{
|
|
int csock; /* Connection socket */
|
|
int rc; /* Return code */
|
|
BYTE cmd; /* Request command */
|
|
BYTE flag; /* Request flag */
|
|
U16 devnum; /* Request device number */
|
|
int id; /* Request id */
|
|
int len; /* Request data length */
|
|
int ix; /* Client index */
|
|
DEVBLK *dev=NULL; /* -> Device block */
|
|
time_t now; /* Current time */
|
|
fd_set selset; /* Read bit map for select */
|
|
int maxfd; /* Max fd for select */
|
|
struct timeval wait; /* Wait time for select */
|
|
BYTE hdr[SHRD_HDR_SIZE + 65536]; /* Header + buffer */
|
|
BYTE *buf = hdr + SHRD_HDR_SIZE; /* Buffer */
|
|
char *ipaddr = NULL; /* IP addr of connected peer */
|
|
char threadname[40];
|
|
|
|
csock = *(int*)psock;
|
|
free (psock);
|
|
ipaddr = clientip(csock);
|
|
|
|
shrdtrc(dev,"server_connect %s sock %d\n",ipaddr,csock);
|
|
|
|
rc = recvData(csock, hdr, buf, 65536, 1);
|
|
if (rc < 0)
|
|
{
|
|
WRMSG(HHC00732, "E", ipaddr);
|
|
close_socket (csock);
|
|
return NULL;
|
|
}
|
|
SHRD_GET_HDR (hdr, cmd, flag, devnum, id, len);
|
|
|
|
/* Error if not a connect request */
|
|
if (id == 0 && cmd != SHRD_CONNECT)
|
|
{
|
|
serverError (NULL, -csock, SHRD_ERROR_NOTCONN, cmd,
|
|
"not a connect request");
|
|
close_socket (csock);
|
|
return NULL;
|
|
}
|
|
|
|
/* Locate the device */
|
|
dev = findDevice (devnum);
|
|
|
|
/* Error if device not found */
|
|
if (dev == NULL)
|
|
{
|
|
serverError (NULL, -csock, SHRD_ERROR_NODEVICE, cmd,
|
|
"device not found");
|
|
close_socket (csock);
|
|
return NULL;
|
|
}
|
|
|
|
/* Obtain the device lock */
|
|
obtain_lock (&dev->lock);
|
|
|
|
/* Find an available slot for the connection */
|
|
rc = serverLocate (dev, id, &ix);
|
|
|
|
/* Error if already connected */
|
|
if (rc >= 0)
|
|
{
|
|
release_lock (&dev->lock);
|
|
serverError (NULL, -csock, SHRD_ERROR_NODEVICE, cmd,
|
|
"already connected");
|
|
close_socket (csock);
|
|
return NULL;
|
|
}
|
|
|
|
/* Error if no available slot */
|
|
if (ix < 0)
|
|
{
|
|
release_lock (&dev->lock);
|
|
serverError (NULL, -csock, SHRD_ERROR_NOTAVAIL, cmd,
|
|
"too many connections");
|
|
close_socket (csock);
|
|
return NULL;
|
|
}
|
|
|
|
/* Obtain SHRD block */
|
|
dev->shrd[ix] = calloc (sizeof(SHRD), 1);
|
|
|
|
/* Error if not obtained */
|
|
if (dev->shrd[ix] == NULL)
|
|
{
|
|
release_lock (&dev->lock);
|
|
serverError (NULL, -csock, SHRD_ERROR_NOMEM, cmd,
|
|
"calloc() failure");
|
|
close_socket (csock);
|
|
return NULL;
|
|
}
|
|
|
|
/* Initialize the SHRD block */
|
|
dev->shrd[ix]->pending = 1;
|
|
dev->shrd[ix]->havehdr = 1;
|
|
if (id == 0) id = serverId (dev);
|
|
dev->shrd[ix]->id = id;
|
|
dev->shrd[ix]->fd = csock;
|
|
dev->shrd[ix]->ipaddr = strdup(ipaddr);
|
|
dev->shrd[ix]->time = time (NULL);
|
|
dev->shrd[ix]->purgen = -1;
|
|
dev->shrdconn++;
|
|
SHRD_SET_HDR (dev->shrd[ix]->hdr, cmd, flag, devnum, id, len);
|
|
|
|
WRMSG (HHC00733, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, ipaddr, id);
|
|
|
|
/* Return if device thread already active */
|
|
if (dev->shrdtid)
|
|
{
|
|
if (dev->shrdwait)
|
|
{
|
|
signal_thread (dev->shrdtid, SIGUSR2);
|
|
}
|
|
release_lock (&dev->lock);
|
|
return NULL;
|
|
}
|
|
dev->shrdtid = thread_id();
|
|
|
|
/* This thread will be the shared device thread */
|
|
MSGBUF(threadname, "Shared device(%1d:%04X)", SSID_TO_LCSS(dev->ssid), dev->devnum);
|
|
WRMSG (HHC00100, "I", thread_id(), get_thread_priority(0), threadname);
|
|
|
|
while (dev->shrdconn)
|
|
{
|
|
FD_ZERO (&selset);
|
|
maxfd = -1;
|
|
|
|
/* Get the current time */
|
|
now = time (NULL);
|
|
|
|
for (ix = 0; ix < SHARED_MAX_SYS; ix++)
|
|
{
|
|
if (dev->shrd[ix])
|
|
{
|
|
/* Exit loop if pending and not waiting */
|
|
if (dev->shrd[ix]->pending && !dev->shrd[ix]->waiting)
|
|
break;
|
|
|
|
/* Disconnect if not a valid socket */
|
|
if ( !socket_is_socket( dev->shrd[ix]->fd ) )
|
|
dev->shrd[ix]->disconnect = 1;
|
|
|
|
/* See if the connection can be timed out */
|
|
else if (now - dev->shrd[ix]->time > SHARED_TIMEOUT
|
|
&& serverDisconnectable (dev, ix))
|
|
dev->shrd[ix]->disconnect = 1;
|
|
|
|
/* Disconnect if the disconnect bit is set */
|
|
if (dev->shrd[ix]->disconnect)
|
|
serverDisconnect (dev, ix);
|
|
|
|
/* Otherwise set the fd if not waiting */
|
|
else if (!dev->shrd[ix]->waiting)
|
|
{
|
|
FD_SET (dev->shrd[ix]->fd, &selset);
|
|
if (dev->shrd[ix]->fd >= maxfd)
|
|
maxfd = dev->shrd[ix]->fd + 1;
|
|
shrdtrc(dev,"select set %d id=%d\n",dev->shrd[ix]->fd,dev->shrd[ix]->id);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* Wait for a request if no pending requests */
|
|
if (ix >= SHARED_MAX_SYS)
|
|
{
|
|
/* Exit thread if nothing to select */
|
|
if (maxfd < 0) continue;
|
|
|
|
/* Wait for a file descriptor to become busy */
|
|
wait.tv_sec = 10; /*SHARED_SELECT_WAIT;*/
|
|
wait.tv_usec = 0;
|
|
release_lock (&dev->lock);
|
|
|
|
dev->shrdwait = 1;
|
|
rc = select ( maxfd, &selset, NULL, NULL, &wait );
|
|
dev->shrdwait = 0;
|
|
|
|
obtain_lock (&dev->lock);
|
|
|
|
shrdtrc(dev,"select rc %d\n",rc);
|
|
|
|
if (rc == 0) continue;
|
|
|
|
if (rc < 0 )
|
|
{
|
|
if (HSO_errno == HSO_EINTR || HSO_errno == HSO_EBADF) continue;
|
|
WRMSG(HHC00735, "E", "select()", strerror(HSO_errno));
|
|
break;
|
|
}
|
|
|
|
/* Find any pending requests */
|
|
for (ix = 0; ix < SHARED_MAX_SYS; ix++)
|
|
{
|
|
if (dev->shrd[ix]
|
|
&& FD_ISSET(dev->shrd[ix]->fd, &selset))
|
|
{
|
|
dev->shrd[ix]->pending = 1;
|
|
shrdtrc(dev,"select isset %d id=%d\n",dev->shrd[ix]->fd,dev->shrd[ix]->id);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* Found a pending request */
|
|
release_lock (&dev->lock);
|
|
|
|
shrdtrc(dev,"select ready %d id=%d\n",dev->shrd[ix]->fd,dev->shrd[ix]->id);
|
|
|
|
if (dev->shrd[ix]->havehdr)
|
|
{
|
|
/* Copy the saved start/resume packet */
|
|
memcpy (hdr, dev->shrd[ix]->hdr, SHRD_HDR_SIZE);
|
|
dev->shrd[ix]->havehdr = dev->shrd[ix]->waiting = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Read the request packet */
|
|
rc = recvData (dev->shrd[ix]->fd, hdr, buf, 65536, 1);
|
|
if (rc < 0)
|
|
{
|
|
WRMSG(HHC00734, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, dev->shrd[ix]->ipaddr, dev->shrd[ix]->id);
|
|
dev->shrd[ix]->disconnect = 1;
|
|
dev->shrd[ix]->pending = 0;
|
|
obtain_lock (&dev->lock);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* Process the request */
|
|
serverRequest (dev, ix, hdr, buf);
|
|
|
|
obtain_lock (&dev->lock);
|
|
|
|
/* If the `waiting' bit is on then the start/resume request
|
|
failed because the device is busy on some other remote
|
|
system. We only need to save the header because the data
|
|
is ignored for start/resume.
|
|
*/
|
|
if (dev->shrd[ix]->waiting)
|
|
{
|
|
memcpy (dev->shrd[ix]->hdr, hdr, SHRD_HDR_SIZE);
|
|
dev->shrd[ix]->havehdr = 1;
|
|
}
|
|
else
|
|
dev->shrd[ix]->pending = 0;
|
|
}
|
|
|
|
dev->shrdtid = 0;
|
|
release_lock (&dev->lock);
|
|
|
|
WRMSG(HHC00101, "I", thread_id(), get_thread_priority(0), threadname);
|
|
|
|
return NULL;
|
|
|
|
} /* serverConnect */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* General trace routine for shared devices
|
|
*-------------------------------------------------------------------*/
|
|
static void shrdtrc (DEVBLK *dev, char *msg, ...)
|
|
{
|
|
int dt;
|
|
struct timeval tv;
|
|
SHRD_TRACE s;
|
|
va_list vl;
|
|
|
|
dt = (dev != NULL && (dev->ccwtrace||dev->ccwstep));
|
|
if (dt == 0 && sysblk.shrdtrace == 0) return;
|
|
|
|
va_start(vl,msg);
|
|
gettimeofday(&tv, NULL);
|
|
sprintf ((char *)s,
|
|
"%6.6ld" "." "%6.6ld %4.4X:",
|
|
(long)tv.tv_sec, (long)tv.tv_usec, dev ? dev->devnum : 0);
|
|
vsnprintf ((char *)s + strlen(s), sizeof(s) - strlen(s),
|
|
msg, vl);
|
|
if (dt)
|
|
{
|
|
WRMSG(HHC00743, "I", s+14);
|
|
}
|
|
if (sysblk.shrdtrace)
|
|
{
|
|
SHRD_TRACE *p = sysblk.shrdtracep++;
|
|
if (p >= sysblk.shrdtracex)
|
|
{
|
|
p = sysblk.shrdtrace;
|
|
sysblk.shrdtracep = p + 1;
|
|
}
|
|
if (p) memcpy(p, s, sizeof(*p));
|
|
}
|
|
|
|
} /* shrdtrc */
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Shared device server
|
|
*-------------------------------------------------------------------*/
|
|
LOCK shrdlock;
|
|
COND shrdcond;
|
|
|
|
static void shared_device_manager_shutdown(void * unused)
|
|
{
|
|
UNREFERENCED(unused);
|
|
|
|
if(sysblk.shrdport)
|
|
{
|
|
sysblk.shrdport = 0;
|
|
|
|
if (sysblk.shrdtid)
|
|
{
|
|
signal_thread (sysblk.shrdtid, SIGUSR2);
|
|
|
|
obtain_lock(&shrdlock);
|
|
timed_wait_condition_relative_usecs(&shrdcond,&shrdlock,2*1000*1000,NULL);
|
|
release_lock(&shrdlock);
|
|
}
|
|
}
|
|
}
|
|
|
|
DLL_EXPORT void *shared_server (void *arg)
|
|
{
|
|
int rc = -32767; /* Return code */
|
|
int hi; /* Hi fd for select */
|
|
int lsock; /* inet socket for listening */
|
|
int usock; /* unix socket for listening */
|
|
int rsock; /* Ready socket */
|
|
int csock; /* Socket for conversation */
|
|
int *psock; /* Pointer to socket */
|
|
struct sockaddr_in server; /* Server address structure */
|
|
#if defined( HAVE_SYS_UN_H )
|
|
struct sockaddr_un userver; /* Unix address structure */
|
|
#endif
|
|
int optval; /* Argument for setsockopt */
|
|
fd_set selset; /* Read bit map for select */
|
|
TID tid; /* Negotiation thread id */
|
|
char threadname[40];
|
|
|
|
UNREFERENCED(arg);
|
|
|
|
MSGBUF(threadname, "Shared device server %d.%d", SHARED_VERSION, SHARED_RELEASE);
|
|
|
|
/* Display thread started message on control panel */
|
|
WRMSG (HHC00100, "I", thread_id(), get_thread_priority(0), threadname);
|
|
|
|
/* Obtain a internet socket */
|
|
lsock = socket (AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (lsock < 0)
|
|
{
|
|
WRMSG(HHC00735, "E", "socket()", strerror(HSO_errno));
|
|
return NULL;
|
|
}
|
|
|
|
/* Obtain a unix socket */
|
|
#if defined( HAVE_SYS_UN_H )
|
|
usock = socket (AF_UNIX, SOCK_STREAM, 0);
|
|
if (usock < 0)
|
|
{
|
|
WRMSG(HHC00735, "W", "socket()", strerror(HSO_errno));
|
|
}
|
|
#else
|
|
usock = -1;
|
|
#endif
|
|
|
|
/* Allow previous instance of socket to be reused */
|
|
optval = 1;
|
|
setsockopt (lsock, SOL_SOCKET, SO_REUSEADDR,
|
|
(GETSET_SOCKOPT_T*)&optval, sizeof(optval));
|
|
|
|
/* Prepare the sockaddr structure for the bind */
|
|
memset( &server, 0, sizeof(server) );
|
|
server.sin_family = AF_INET;
|
|
server.sin_addr.s_addr = INADDR_ANY;
|
|
server.sin_port = sysblk.shrdport;
|
|
server.sin_port = htons(server.sin_port);
|
|
|
|
/* Attempt to bind the internet socket to the port */
|
|
while (sysblk.shrdport)
|
|
{
|
|
rc = bind (lsock, (struct sockaddr *)&server, sizeof(server));
|
|
if (rc == 0 || HSO_errno != HSO_EADDRINUSE) break;
|
|
WRMSG (HHC00736, "W", sysblk.shrdport);
|
|
SLEEP(10);
|
|
} /* end while */
|
|
|
|
if (rc != 0 || !sysblk.shrdport)
|
|
{
|
|
WRMSG(HHC00735, "E", "bind()", strerror(HSO_errno));
|
|
close_socket(lsock); close_socket(usock);
|
|
return NULL;
|
|
}
|
|
|
|
#if defined( HAVE_SYS_UN_H )
|
|
/* Bind the unix socket */
|
|
if (usock >= 0)
|
|
{
|
|
userver.sun_family = AF_UNIX;
|
|
sprintf(userver.sun_path, "/tmp/hercules_shared.%d", sysblk.shrdport);
|
|
unlink(userver.sun_path);
|
|
fchmod (usock, 0700);
|
|
|
|
rc = bind (usock, (struct sockaddr *)&userver, sizeof(userver));
|
|
|
|
if (rc < 0)
|
|
{
|
|
WRMSG(HHC00735, "W", "bind()", strerror(errno));
|
|
close(usock);
|
|
usock = -1;
|
|
}
|
|
}
|
|
#endif // defined( HAVE_SYS_UN_H )
|
|
|
|
/* Put the sockets into listening state */
|
|
rc = listen (lsock, SHARED_MAX_SYS);
|
|
|
|
if (rc < 0)
|
|
{
|
|
WRMSG(HHC00735, "E", "listen()", strerror(HSO_errno));
|
|
close_socket(lsock); close_socket(usock);
|
|
return NULL;
|
|
}
|
|
|
|
if (usock >= 0)
|
|
{
|
|
rc = listen (usock, SHARED_MAX_SYS);
|
|
|
|
if (rc < 0)
|
|
{
|
|
WRMSG(HHC00735, "W", "listen()", strerror(HSO_errno));
|
|
close_socket(usock);
|
|
usock = -1;
|
|
}
|
|
}
|
|
|
|
csock = -1;
|
|
if (lsock < usock)
|
|
hi = usock + 1;
|
|
else
|
|
hi = lsock + 1;
|
|
|
|
WRMSG(HHC00737, "I", sysblk.shrdport);
|
|
|
|
initialize_lock (&shrdlock);
|
|
initialize_condition(&shrdcond);
|
|
|
|
hdl_adsc("shared_device_manager_shutdown",shared_device_manager_shutdown, NULL);
|
|
|
|
/* Handle connection requests and attention interrupts */
|
|
while (sysblk.shrdport)
|
|
{
|
|
/* Initialize the select parameters */
|
|
FD_ZERO (&selset);
|
|
FD_SET (lsock, &selset);
|
|
if (usock >= 0)
|
|
FD_SET (usock, &selset);
|
|
|
|
/* Wait for a file descriptor to become ready */
|
|
rc = select ( hi, &selset, NULL, NULL, NULL );
|
|
|
|
if (rc == 0) continue;
|
|
|
|
if (rc < 0 )
|
|
{
|
|
if (HSO_errno == HSO_EINTR) continue;
|
|
WRMSG(HHC00735, "E", "select()", strerror(HSO_errno));
|
|
break;
|
|
}
|
|
|
|
/* If a client connection request has arrived then accept it */
|
|
if (FD_ISSET(lsock, &selset))
|
|
rsock = lsock;
|
|
else if (usock >= 0 && FD_ISSET(usock, &selset))
|
|
rsock = usock;
|
|
else
|
|
rsock = -1;
|
|
|
|
if (rsock > 0)
|
|
{
|
|
/* Accept a connection and create conversation socket */
|
|
csock = accept (rsock, NULL, NULL);
|
|
if (csock < 0)
|
|
{
|
|
WRMSG(HHC00735, "E", "accept()", strerror(HSO_errno));
|
|
continue;
|
|
}
|
|
|
|
psock = malloc (sizeof (csock));
|
|
if (psock == NULL)
|
|
{
|
|
char buf[40];
|
|
MSGBUF(buf, "malloc(%d)", (int)sizeof(csock));
|
|
WRMSG(HHC00735, "E", buf, strerror(HSO_errno));
|
|
close_socket (csock);
|
|
continue;
|
|
}
|
|
*psock = csock;
|
|
|
|
/* Create a thread to complete the client connection */
|
|
rc = create_thread (&tid, DETACHED,
|
|
serverConnect, psock, "serverConnect");
|
|
if(rc)
|
|
{
|
|
WRMSG(HHC00102, "E", strerror(rc));
|
|
close_socket (csock);
|
|
}
|
|
|
|
} /* end if(rsock) */
|
|
|
|
|
|
} /* end while */
|
|
|
|
/* Close the listening sockets */
|
|
close_socket (lsock);
|
|
#if defined( HAVE_SYS_UN_H )
|
|
if (usock >= 0)
|
|
{
|
|
close_socket (usock);
|
|
unlink(userver.sun_path);
|
|
}
|
|
#endif
|
|
|
|
signal_condition(&shrdcond);
|
|
|
|
if ( !sysblk.shutdown )
|
|
hdl_rmsc(shared_device_manager_shutdown, NULL);
|
|
|
|
sysblk.shrdtid = 0;
|
|
|
|
WRMSG (HHC00101, "I", thread_id(), get_thread_priority(0), threadname);
|
|
|
|
return NULL;
|
|
|
|
} /* end function shared_server */
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Shared device command processor
|
|
*-------------------------------------------------------------------*/
|
|
DLL_EXPORT int shared_cmd(int argc, char *argv[], char *cmdline)
|
|
{
|
|
char buf[256];
|
|
char *kw, *op, c;
|
|
char *strtok_str = NULL;
|
|
|
|
UNREFERENCED(cmdline);
|
|
|
|
/* Get keyword and operand */
|
|
if (argc != 2 || strlen(argv[1]) > 255)
|
|
{
|
|
WRMSG (HHC00738, "E");
|
|
return 0;
|
|
}
|
|
strlcpy( buf, argv[1], sizeof(buf) );
|
|
kw = strtok_r (buf, "=", &strtok_str );
|
|
op = strtok_r (NULL, " \t", &strtok_str );
|
|
|
|
if (kw == NULL)
|
|
{
|
|
WRMSG (HHC00739, "E");
|
|
return 0;
|
|
}
|
|
|
|
if (strcasecmp(kw, "trace") == 0)
|
|
{
|
|
int n;
|
|
SHRD_TRACE *s, *p, *x, *i;
|
|
s = sysblk.shrdtrace;
|
|
p = sysblk.shrdtracep;
|
|
x = sysblk.shrdtracex;
|
|
n = sysblk.shrdtracen;
|
|
|
|
/* Get a new trace table if an operand was specified */
|
|
if (op)
|
|
{
|
|
if (sscanf (op, "%d%c", &n, &c) != 1)
|
|
{
|
|
WRMSG (HHC00740, "E", op);
|
|
return 0;
|
|
}
|
|
if (s != NULL)
|
|
{
|
|
sysblk.shrdtrace = sysblk.shrdtracex = sysblk.shrdtracep = NULL;
|
|
SLEEP (1);
|
|
free (s);
|
|
}
|
|
sysblk.shrdtrace = sysblk.shrdtracex = sysblk.shrdtracep = NULL;
|
|
sysblk.shrdtracen = 0;
|
|
if (n > 0)
|
|
{
|
|
s = calloc( (size_t)n, sizeof(SHRD_TRACE) );
|
|
if (s == NULL)
|
|
{
|
|
char buf[40];
|
|
MSGBUF(buf, "calloc(%d, %d)", (int)n, (int)sizeof(SHRD_TRACE) );
|
|
WRMSG (HHC00735, "E", buf, strerror(errno));
|
|
return 0;
|
|
}
|
|
sysblk.shrdtracen = n;
|
|
sysblk.shrdtrace = sysblk.shrdtracep = s;
|
|
sysblk.shrdtracex = s + n;
|
|
}
|
|
return 0;
|
|
}
|
|
/* Print the trace table */
|
|
sysblk.shrdtrace = sysblk.shrdtracex = sysblk.shrdtracep = NULL;
|
|
i = p;
|
|
SLEEP(1);
|
|
do {
|
|
if (i[0] != '\0') WRMSG(HHC00743, "I", (char *)i);
|
|
if (++i >= x) i = s;
|
|
} while (i != p);
|
|
memset(s, 0, n * sizeof(SHRD_TRACE) );
|
|
sysblk.shrdtrace = s;
|
|
sysblk.shrdtracep = s;
|
|
sysblk.shrdtracex = x;
|
|
sysblk.shrdtracen = n;
|
|
}
|
|
else
|
|
{
|
|
WRMSG (HHC00741, "E", kw);
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DEVHND shared_ckd_device_hndinfo = {
|
|
&shared_ckd_init, /* Device Initialisation */
|
|
&ckddasd_execute_ccw, /* Device CCW execute */
|
|
&shared_ckd_close, /* Device Close */
|
|
&ckddasd_query_device, /* Device Query */
|
|
NULL, /* Device Extended Query */
|
|
&shared_start, /* Device Start channel pgm */
|
|
&shared_end, /* Device End channel pgm */
|
|
&shared_start, /* Device Resume channel pgm */
|
|
&shared_end, /* Device Suspend channel pgm */
|
|
NULL, /* Device Halt channel pgm */
|
|
&shared_ckd_read, /* Device Read */
|
|
&shared_ckd_write, /* Device Write */
|
|
&shared_used, /* Device Query used */
|
|
&shared_reserve, /* Device Reserve */
|
|
&shared_release, /* 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 */
|
|
&ckddasd_hsuspend, /* Hercules suspend */
|
|
&ckddasd_hresume /* Hercules resume */
|
|
};
|
|
|
|
DEVHND shared_fba_device_hndinfo = {
|
|
&shared_fba_init, /* Device Initialisation */
|
|
&fbadasd_execute_ccw, /* Device CCW execute */
|
|
&shared_fba_close, /* Device Close */
|
|
&fbadasd_query_device, /* Device Query */
|
|
NULL, /* Device Extended Query */
|
|
&shared_start, /* Device Start channel pgm */
|
|
&shared_end, /* Device End channel pgm */
|
|
&shared_start, /* Device Resume channel pgm */
|
|
&shared_end, /* Device Suspend channel pgm */
|
|
NULL, /* Device Halt channel pgm */
|
|
&shared_ckd_read, /* Device Read */
|
|
&shared_ckd_write, /* Device Write */
|
|
&shared_used, /* Device Query used */
|
|
&shared_reserve, /* Device Reserve */
|
|
&shared_release, /* 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 */
|
|
&fbadasd_hsuspend, /* Hercules suspend */
|
|
&fbadasd_hresume /* Hercules resume */
|
|
};
|
|
|
|
#else // !defined( OPTION_SHARED_DEVICES )
|
|
|
|
int shared_update_notify (DEVBLK *dev, int block)
|
|
{
|
|
UNREFERENCED(dev);
|
|
UNREFERENCED(block);
|
|
return 0;
|
|
}
|
|
int shared_ckd_init (DEVBLK *dev, int argc, char *argv[] )
|
|
{
|
|
UNREFERENCED(dev);
|
|
UNREFERENCED(argc);
|
|
UNREFERENCED(argv);
|
|
return -1;
|
|
}
|
|
int shared_fba_init (DEVBLK *dev, int argc, char *argv[] )
|
|
{
|
|
UNREFERENCED(dev);
|
|
UNREFERENCED(argc);
|
|
UNREFERENCED(argv);
|
|
return -1;
|
|
}
|
|
void *shared_server (void *arg)
|
|
{
|
|
UNREFERENCED(arg);
|
|
WRMSG (HHC00742, "E");
|
|
return NULL;
|
|
}
|
|
int shared_cmd(int argc, char *argv[], char *cmdline)
|
|
{
|
|
UNREFERENCED(argc);
|
|
UNREFERENCED(argv);
|
|
UNREFERENCED(cmdline);
|
|
WRMSG (HHC00742, "E");
|
|
return 0;
|
|
}
|
|
#endif /* defined(OPTION_SHARED_DEVICES) */
|