Files
org-hyperion-cules/ctcadpt.c
2014-08-04 06:56:23 -07:00

2546 lines
91 KiB
C

/* CTCADPT.C (c) Copyright James A. Pierson, 2002-2012 */
/* (c) Copyright Roger Bowler, 2000-2012 */
/* (c) Copyright Willem Konynenberg, 2000-2009 */
/* (c) Copyright Vic Cross, 2001-2009 */
/* Hercules Channel-to-Channel Emulation Support */
/* */
/* Released under "The Q Public License Version 1" */
/* (http://www.hercules-390.org/herclic.html) as modifications to */
/* Hercules. */
// Hercules Channel-to-Channel Emulation Support
// ====================================================================
//
// vmnet - (C) Copyright Willem Konynenberg, 2000-2009
// CTCT - (C) Copyright Vic Cross, 2001-2009
// CTCE (C) Copyright Peter J. Jansen, 2014
//
// Notes:
// This module contains the remaining CTC emulation modes that
// have not been moved to seperate modules. There is also logic
// to allow old style 3088 device definitions for compatibility
// and may be removed in a future release.
//
// Please read README.NETWORKING for more info.
//
#include "hstdinc.h"
#define _CTCADPT_C_
#define _HENGINE_DLL_
#include "hercules.h"
#include "devtype.h"
#include "ctcadpt.h"
#include "opcode.h"
#include "devtype.h"
// ====================================================================
// Declarations
// ====================================================================
static int CTCT_Init( DEVBLK *dev, int argc, char *argv[] );
static void CTCT_Read( DEVBLK* pDEVBLK, U32 sCount,
BYTE* pIOBuf, BYTE* pUnitStat,
U32* pResidual, BYTE* pMore );
static void CTCT_Write( DEVBLK* pDEVBLK, U32 sCount,
BYTE* pIOBuf, BYTE* pUnitStat,
U32* pResidual );
static void* CTCT_ListenThread( void* argp );
static int CTCE_Init( DEVBLK *dev, int argc, char *argv[] );
static void CTCE_Send( DEVBLK* pDEVBLK, U32 sCount,
BYTE* pIOBuf, BYTE* pUnitStat,
U32* pResidual );
static void* CTCE_RecvThread( void* argp );
static void* CTCE_ListenThread( void* argp );
static int VMNET_Init( DEVBLK *dev, int argc, char *argv[] );
static int VMNET_Write( DEVBLK *dev, BYTE *iobuf,
U32 count, BYTE *unitstat );
static int VMNET_Read( DEVBLK *dev, BYTE *iobuf,
U32 count, BYTE *unitstat );
// --------------------------------------------------------------------
// Definitions for CTC general data blocks
// --------------------------------------------------------------------
typedef struct _CTCG_PARMBLK
{
int listenfd;
struct sockaddr_in addr;
DEVBLK* dev;
}
CTCG_PARMBLK;
// --------------------------------------------------------------------
// CTCE Send-Receive Socket Prefix at the start of the DEVBLK buf
// --------------------------------------------------------------------
typedef struct _CTCE_SOKPFX
{
BYTE CmdReg; /* CTCE command register */
BYTE FsmSta; /* CTCE FSM state */
U16 sCount; /* CTCE sCount copy */
U16 PktSeq; /* CTCE Packet Sequence ID */
U16 SndLen; /* CTCE Packet Sent Length */
}
CTCE_SOKPFX;
// --------------------------------------------------------------------
// CTCE Equivalent of CTCG_PARMBLK
// --------------------------------------------------------------------
typedef struct _CTCE_PARMBLK
{
int listenfd[2]; /* [0] = read, [1] = write */
u_int ctceWrPort; /* 0 = read, 1 = write */
struct sockaddr_in addr;
DEVBLK* dev;
}
CTCE_PARMBLK;
// --------------------------------------------------------------------
// CTCE Constants (generated by a small REXX script)
// --------------------------------------------------------------------
static char *CTCE_CmdStr[14] = {
"PRE" , // 0 = 00 = Prepare
"CTL" , // 1 = 01 = Control
"RED" , // 2 = 02 = Read
"WRT" , // 3 = 03 = Write
"SCB" , // 4 = 04 = Sense Command Byte
"???" , // 5 = 05 = Not Used
"RBK" , // 6 = 06 = Read Backward
"WEF" , // 7 = 07 = Write End Of File
"NOP" , // 8 = 10 = No Operation
"SEM" , // 9 = 11 = Set Extended Mode
"SAS" , // 10 = 12 = Sense Adapter State
"SID" , // 11 = 13 = Sense ID
"RCD" , // 12 = 14 = Read Configuration Data
"???" // 13 = 15 = Invalid Command Code
};
static BYTE CTCE_Cmd[256] = {
13, 3, 2, 8,10, 3, 2, 1,13, 3, 2, 8, 6, 3, 2, 1,
13, 3, 2, 8, 4, 3, 2, 1,13, 3, 2, 8, 6, 3, 2, 1,
13, 3, 2, 8,13, 3, 2, 1,13, 3, 2, 8, 6, 3, 2, 1,
13, 3, 2, 8, 4, 3, 2, 1,13, 3, 2, 8, 6, 3, 2, 1,
13, 3, 2,13,13, 3, 2, 1,13, 3, 2,13, 6, 3, 2, 1,
13, 3, 2,13, 4, 3, 2, 1,13, 3, 2,13, 6, 3, 2, 1,
13, 3, 2,13,13, 3, 2, 1,13, 3, 2,13, 6, 3, 2, 1,
13, 3, 2,13, 4, 3, 2, 1,13, 3, 2,13, 6, 3, 2, 1,
13, 7, 2, 8,13, 7, 2, 1,13, 7, 2, 8, 6, 7, 2, 1,
13, 7, 2, 8, 4, 7, 2, 1,13, 7, 2, 8, 6, 7, 2, 1,
13, 7, 2, 8,13, 7, 2, 1,13, 7, 2, 8, 6, 7, 2, 1,
13, 7, 2, 8, 4, 7, 2, 1,13, 7, 2, 8, 6, 7, 2, 1,
13, 7, 2, 9,13, 7, 2, 1,13, 7, 2,13, 6, 7, 2, 1,
13, 7, 2,13, 4, 7, 2, 1,13, 7, 2,13, 6, 7, 2, 1,
13, 7, 2, 0,11, 7, 2, 1,13, 7, 2,13, 6, 7, 2, 1,
13, 7, 2,13, 4, 7, 2, 1,13, 7, 2,13, 6, 7, 2, 1
};
#define IS_CTCE_CCW_PRE(c) ((CTCE_Cmd[c]==0))
#define IS_CTCE_CCW_CTL(c) ((CTCE_Cmd[c]==1))
#define IS_CTCE_CCW_RED(c) ((CTCE_Cmd[c]==2))
#define IS_CTCE_CCW_WRT(c) ((CTCE_Cmd[c]==3))
#define IS_CTCE_CCW_SCB(c) ((CTCE_Cmd[c]==4))
#define IS_CTCE_CCW_RBK(c) ((CTCE_Cmd[c]==6))
#define IS_CTCE_CCW_WEF(c) ((CTCE_Cmd[c]==7))
#define IS_CTCE_CCW_NOP(c) ((CTCE_Cmd[c]==8))
#define IS_CTCE_CCW_SEM(c) ((CTCE_Cmd[c]==9))
#define IS_CTCE_CCW_SAS(c) ((CTCE_Cmd[c]==10))
#define IS_CTCE_CCW_SID(c) ((CTCE_Cmd[c]==11))
#define IS_CTCE_CCW_RCD(c) ((CTCE_Cmd[c]==12))
#define IS_CTCE_CCW_RDY(c) ((CTCE_Cmd[c]<10))
#define IS_CTCE_CCW_RDA(c) (((CTCE_Cmd[c]&0xFB)==2)) /* Read or Read Backward */
#define IS_CTCE_CCW_WRA(c) (((CTCE_Cmd[c]&0xFB)==3)) /* Write or Write EOF */
/* Macros for classifying CTC states */
/* These are numbered 0 thru 7 as per the */
/* column numbers 0-3 and 4-7 in the table */
/* in section 2.13 in SA22-7203-00 by IBM */
#define IS_CTCE_YWP(c) (((c)&0x07)==0x00)
#define IS_CTCE_YWC(c) (((c)&0x07)==0x01)
#define IS_CTCE_YWR(c) (((c)&0x07)==0x02)
#define IS_CTCE_YWW(c) (((c)&0x07)==0x03)
#define IS_CTCE_YAV(c) (((c)&0x07)==0x04)
#define IS_CTCE_YNR(c) (((c)&0x07)==0x05)
#define IS_CTCE_XWK(c) (((c)&0x07)==0x06)
#define IS_CTCE_XIP(c) (((c)&0x07)==0x07)
/* This last one is useful, tests for either */
/* the 0 (YWP) or 4 (YAV) whilst READY */
#define IS_CTCE_YAP(c) (((c)&0x83)==0x00)
/* And the corresponding SET macros for these */
#define SET_CTCE_YWP(c) (c&=&0x0F8)
#define SET_CTCE_YWC(c) (c=(((c)&0xF8)|0x01))
#define SET_CTCE_YWR(c) (c=(((c)&0xF8)|0x02))
#define SET_CTCE_YWW(c) (c=(((c)&0xF8)|0x03))
#define SET_CTCE_YAV(c) (c=(((c)&0xF8)|0x04))
#define SET_CTCE_YNR(c) (c=(((c)&0xF8)|0x05))
#define SET_CTCE_XWK(c) (c=(((c)&0xF8)|0x06))
#define SET_CTCE_XIP(c) (c|=0x07))
/* Some additional flags are also present. */
#define IS_CTCE_NRDY(c) (((c)&0x80)==0x80)
#define IS_CTCE_WEOF(c) (((c)&0x40)==0x40)
#define IS_CTCE_MATCH(c) (((c)&0x20)==0x20)
#define IS_CTCE_ATTN(c) (((c)&0x10)==0x10)
/* And the corresponding SET macros for these */
#define SET_CTCE_NRDY(c) (c|=0x80)
#define SET_CTCE_WEOF(c) (c|=0x40)
#define SET_CTCE_MATCH(c) (c|=0x20)
#define SET_CTCE_ATTN(c) (c|=0x10)
/* And the corresponding CLeaR macros */
#define CLR_CTCE_NRDY(c) (c&=~0x80)
#define CLR_CTCE_WEOF(c) (c&=~0x40)
#define CLR_CTCE_MATCH(c) (c&=~0x20)
#define CLR_CTCE_ATTN(c) (c&=~0x10)
/* To CLeaR all flags */
#define CLR_CTCE_ALLF(c) (c&=~0xF0)
/* Enhanced CTCT processing is selected by */
/* omitting default MTU bufsize CTCE_MTU_MIN, */
/* or by specifying a larger number. The */
/* default is equal to sizeof(CTCE_SOKPFX) + */
/* sizeof(U16) (sCount = 2) + 32K, where 32K */
/* is the largest data sCount seen used by */
/* CTC programs. If that number would be too */
/* small one day, error message HHC05022S */
/* would instruct the user to specify an */
/* increased MTU bufsize in the device */
/* configuration statement. */
#define CTCE_MTU_MIN ( (int)( 32768 + sizeof(CTCE_SOKPFX) + sizeof(U16 /* sCount */) ) )
// --------------------------------------------------------------------
// Device Handler Information Block
// --------------------------------------------------------------------
DEVHND ctcadpt_device_hndinfo =
{
&CTCX_Init, /* Device Initialisation */
&CTCX_ExecuteCCW, /* Device CCW execute */
&CTCX_Close, /* Device Close */
&CTCX_Query, /* Device Query */
NULL, /* Device Extended Query */
NULL, /* Device Start channel pgm */
NULL, /* Device End channel pgm */
NULL, /* Device Resume channel pgm */
NULL, /* Device Suspend channel pgm */
NULL, /* Device Halt channel pgm */
NULL, /* Device Read */
NULL, /* Device Write */
NULL, /* Device Query used */
NULL, /* Device Reserve */
NULL, /* Device Release */
NULL, /* Device Attention */
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 */
NULL, /* Hercules suspend */
NULL /* Hercules resume */
};
DEVHND ctct_device_hndinfo =
{
&CTCT_Init, /* Device Initialisation */
&CTCX_ExecuteCCW, /* Device CCW execute */
&CTCX_Close, /* Device Close */
&CTCX_Query, /* Device Query */
NULL, /* Device Extended Query */
NULL, /* Device Start channel pgm */
NULL, /* Device End channel pgm */
NULL, /* Device Resume channel pgm */
NULL, /* Device Suspend channel pgm */
NULL, /* Device Halt channel pgm */
NULL, /* Device Read */
NULL, /* Device Write */
NULL, /* Device Query used */
NULL, /* Device Reserve */
NULL, /* Device Release */
NULL, /* Device Attention */
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 */
NULL, /* Hercules suspend */
NULL /* Hercules resume */
};
DEVHND ctce_device_hndinfo =
{
&CTCE_Init, /* Device Initialisation */
&CTCX_ExecuteCCW, /* Device CCW execute */
&CTCX_Close, /* Device Close */
&CTCX_Query, /* Device Query */
NULL, /* Device Extended Query */
NULL, /* Device Start channel pgm */
NULL, /* Device End channel pgm */
NULL, /* Device Resume channel pgm */
NULL, /* Device Suspend channel pgm */
NULL, /* Device Halt channel pgm */
NULL, /* Device Read */
NULL, /* Device Write */
NULL, /* Device Query used */
NULL, /* Device Reserve */
NULL, /* Device Release */
NULL, /* Device Attention */
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 */
NULL, /* Hercules suspend */
NULL /* Hercules resume */
};
DEVHND vmnet_device_hndinfo =
{
&VMNET_Init, /* Device Initialisation */
&CTCX_ExecuteCCW, /* Device CCW execute */
&CTCX_Close, /* Device Close */
&CTCX_Query, /* Device Query */
NULL, /* Device Extended Query */
NULL, /* Device Start channel pgm */
NULL, /* Device End channel pgm */
NULL, /* Device Resume channel pgm */
NULL, /* Device Suspend channel pgm */
NULL, /* Device Halt channel pgm */
NULL, /* Device Read */
NULL, /* Device Write */
NULL, /* Device Query used */
NULL, /* Device Reserve */
NULL, /* Device Release */
NULL, /* Device Attention */
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 */
NULL, /* Hercules suspend */
NULL /* Hercules resume */
};
extern DEVHND ctci_device_hndinfo;
extern DEVHND lcs_device_hndinfo;
// ====================================================================
// Primary Module Entry Points
// ====================================================================
// --------------------------------------------------------------------
// Device Initialization Handler (Generic)
// --------------------------------------------------------------------
int CTCX_Init( DEVBLK* pDEVBLK, int argc, char *argv[] )
{
pDEVBLK->devtype = 0x3088;
pDEVBLK->excps = 0;
// The first argument is the device emulation type
if( argc < 1 )
{
WRMSG (HHC00915, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum );
return -1;
}
if((pDEVBLK->hnd = hdl_ghnd(argv[0])))
{
if(pDEVBLK->hnd->init == &CTCX_Init)
return -1;
free(pDEVBLK->typname);
pDEVBLK->typname = strdup(argv[0]);
return (pDEVBLK->hnd->init)( pDEVBLK, --argc, ++argv );
}
WRMSG (HHC00970, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, argv[0]);
return -1;
}
// -------------------------------------------------------------------
// Query the device definition (Generic)
// -------------------------------------------------------------------
void CTCX_Query( DEVBLK* pDEVBLK,
char** ppszClass,
int iBufLen,
char* pBuffer )
{
BEGIN_DEVICE_CLASS_QUERY( "CTCA", pDEVBLK, ppszClass, iBufLen, pBuffer );
snprintf( pBuffer, iBufLen-1, "%s IO[%" I64_FMT "u]", pDEVBLK->filename, pDEVBLK->excps );
}
// -------------------------------------------------------------------
// Close the device (Generic)
// -------------------------------------------------------------------
int CTCX_Close( DEVBLK* pDEVBLK )
{
// Close the device file (if not already closed)
if( pDEVBLK->fd >= 0 )
{
if (socket_is_socket( pDEVBLK->fd ))
close_socket( pDEVBLK->fd );
else
close( pDEVBLK->fd );
pDEVBLK->fd = -1; // indicate we're now closed
}
// Also for the CTCE RecvThread socket if applicable
if( ( pDEVBLK->ctctype == CTC_CTCE ) && pDEVBLK->ctcefd >= 0 )
{
if (socket_is_socket( pDEVBLK->ctcefd ))
close_socket( pDEVBLK->ctcefd );
else
close( pDEVBLK->ctcefd );
pDEVBLK->ctcefd = -1;
}
return 0;
}
// -------------------------------------------------------------------
// Execute a Channel Command Word (Generic)
// -------------------------------------------------------------------
void CTCX_ExecuteCCW( DEVBLK* pDEVBLK, BYTE bCode,
BYTE bFlags, BYTE bChained,
U32 sCount, BYTE bPrevCode,
int iCCWSeq, BYTE* pIOBuf,
BYTE* pMore, BYTE* pUnitStat,
U32* pResidual )
{
int iNum; // Number of bytes to move
BYTE bOpCode; // CCW opcode with modifier
// bits masked off
UNREFERENCED( bFlags );
UNREFERENCED( bChained );
UNREFERENCED( bPrevCode );
UNREFERENCED( iCCWSeq );
// Intervention required if the device file is not open
if( ( pDEVBLK->fd < 0 || ( ( pDEVBLK->ctctype == CTC_CTCE ) && pDEVBLK->ctcefd < 0 ) ) &&
!IS_CCW_SENSE( bCode ) &&
!IS_CCW_CONTROL( bCode ) )
{
pDEVBLK->sense[0] = SENSE_IR;
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
return;
}
// Enhanced CTCT processing is implemented as an addition to
// the already existing older code.
if( pDEVBLK->ctctype == CTC_CTCE )
{
// Changes to DEVBLK are lock protected as the CTCT_RecvThread
// might update as well, but, due to the way actually existing
// software uses CTC devices, this may not be needed at all.
obtain_lock( &pDEVBLK->lock );
// Copy control command byte in x command register
pDEVBLK->ctcexCmd = bCode;
// Any command different from READ and RDBACK will reset the WEOF flag.
if( !IS_CTCE_CCW_RDA( bCode ) )
{
CLR_CTCE_WEOF( pDEVBLK->ctcexState );
}
}
// Mask off the modifier bits in the CCW bOpCode
if( ( bCode & 0x07 ) == 0x07 )
bOpCode = 0x07;
else if( ( bCode & 0x03 ) == 0x02 )
bOpCode = 0x02;
else if( ( bCode & 0x0F ) == 0x0C )
bOpCode = 0x0C;
else if( ( bCode & 0x03 ) == 0x01 )
bOpCode = pDEVBLK->ctcxmode ? ( bCode & 0x83 ) : 0x01;
else if( ( bCode & 0x1F ) == 0x14 )
bOpCode = 0x14;
else if( ( bCode & 0x47 ) == 0x03 )
bOpCode = 0x03;
else if( ( bCode & 0xC7 ) == 0x43 )
bOpCode = 0x43;
else
bOpCode = bCode;
// Process depending on CCW bOpCode
switch (bOpCode)
{
case 0x01: // 0MMMMM01 WRITE
//------------------------------------------------------------
// WRITE
//------------------------------------------------------------
// Return normal status if CCW count is zero
if( sCount == 0 )
{
*pUnitStat = CSW_CE | CSW_DE;
break;
}
// Write data and set unit status and residual byte count
switch( pDEVBLK->ctctype )
{
case CTC_CTCT:
CTCT_Write( pDEVBLK, sCount, pIOBuf, pUnitStat, pResidual );
break;
case CTC_VMNET:
*pResidual = sCount - VMNET_Write( pDEVBLK, pIOBuf,
sCount, pUnitStat );
break;
case CTC_CTCE:
// Enhanced CTC processing:
// A write command is accepted if we are available and in
// states YWR or YWP or YAV (the latter two equals YAP).
if( IS_CTCE_YWR( pDEVBLK->ctcexState ) ||
IS_CTCE_YAP( pDEVBLK->ctcexState ) )
{
// If available then we send the ATTN flag.
if( IS_CTCE_YAV( pDEVBLK->ctcexState ) )
{
SET_CTCE_ATTN( pDEVBLK->ctcexState );
}
// In state YWP or YAV (= YAP) then we move to state YWW.
if( IS_CTCE_YAP( pDEVBLK->ctcexState ) )
{
SET_CTCE_YWW( pDEVBLK->ctcexState );
}
// Otherwise we received a matching complementary command.
else
{
SET_CTCE_MATCH( pDEVBLK->ctcexState );
}
// Data and state info must be sent to the other (y-) side.
CTCE_Send( pDEVBLK, sCount, pIOBuf, pUnitStat, pResidual );
// The above only proceeds when the matching read command
// completes or was already done, unitistat and residual
// will be set, so we just return to state available.
SET_CTCE_YAV( pDEVBLK->ctcexState );
}
break;
}
break;
case 0x81: // 1MMMMM01 WEOF
//------------------------------------------------------------
// WRITE EOF
//------------------------------------------------------------
// Enhanced CTC processing write EOF command is accepted if
// in states YWR or YWP or YAV (the latter two equals YAP).
if( ( pDEVBLK->ctctype == CTC_CTCE ) &&
( IS_CTCE_YWR( pDEVBLK->ctcexState ) ||
IS_CTCE_YAP( pDEVBLK->ctcexState ) ) )
{
// This command is a matching complementary command for read.
if( IS_CTCE_YWR( pDEVBLK->ctcexState ) )
SET_CTCE_MATCH( pDEVBLK->ctcexState );
// We then inform the other (y-)side we received a WEOF.
CTCE_Send( pDEVBLK, sCount, pIOBuf, pUnitStat, pResidual );
// And we always return to state available.
SET_CTCE_YAV( pDEVBLK->ctcexState );
break;
}
// Non-Enhanced CTC processing is retained.
// Return normal status
*pUnitStat = CSW_CE | CSW_DE;
break;
case 0x02: // MMMMMM10 READ
case 0x0C: // MMMM1100 RDBACK
// -----------------------------------------------------------
// READ & READ BACKWARDS
// -----------------------------------------------------------
// Read data and set unit status and residual byte count
switch( pDEVBLK->ctctype )
{
case CTC_CTCT:
CTCT_Read( pDEVBLK, sCount, pIOBuf, pUnitStat, pResidual, pMore );
break;
case CTC_VMNET:
*pResidual = sCount - VMNET_Read( pDEVBLK, pIOBuf,
sCount, pUnitStat );
break;
case CTC_CTCE:
// Enhanced CTCT processing:
// If WEOF is set on our side whilst we are available and
// the other side also available or in the W(D)P state, then
// the read command will be rejected with unit exception.
if( IS_CTCE_WEOF( pDEVBLK->ctcexState ) &&
IS_CTCE_YAP ( pDEVBLK->ctcexState ) )
{
*pResidual = 0;
*pUnitStat = CSW_CE | CSW_DE | CSW_UX ;
}
// A read command is accepted if we are available and in
// states YWW or YWP or YAV (the latter two equals YAP).
else if( IS_CTCE_YWW( pDEVBLK->ctcexState ) ||
IS_CTCE_YAP( pDEVBLK->ctcexState ) )
{
// If available then we send the ATTN flag.
if( IS_CTCE_YAV( pDEVBLK->ctcexState ) )
{
SET_CTCE_ATTN( pDEVBLK->ctcexState );
}
// In state YWP or YAV (= YAP) then we move to state YWR.
if( IS_CTCE_YAP( pDEVBLK->ctcexState ) )
{
SET_CTCE_YWR( pDEVBLK->ctcexState );
}
// Otherwise we received a matching complementary command.
else
{
SET_CTCE_MATCH( pDEVBLK->ctcexState );
}
// Data and state info must be sent to the other (y-) side,
// proceeding only when the matching write command completes
// or was already done, unitstat and residual will be set.
CTCE_Send( pDEVBLK, sCount, pIOBuf, pUnitStat, pResidual );
}
// And we return to state available.
SET_CTCE_YAV( pDEVBLK->ctcexState );
break;
}
break;
case 0x07: // MMMMM111 CTL
// -----------------------------------------------------------
// CONTROL
// -----------------------------------------------------------
// CTC Enhanced processing.
if( pDEVBLK->ctctype == CTC_CTCE )
{
// The control command is accepted if we are available and
// the other side is also available or in the YWP state.
if( IS_CTCE_YAP( pDEVBLK->ctcexState ) )
{
// If available then we send the ATTN flag.
if( IS_CTCE_YAV( pDEVBLK->ctcexState ) )
SET_CTCE_ATTN( pDEVBLK->ctcexState );
// We always end up in the YWC state.
SET_CTCE_YWC( pDEVBLK->ctcexState );
// And then we always notify the other (y-)side.
CTCE_Send( pDEVBLK, sCount, pIOBuf, pUnitStat, pResidual );
// The above only proceeds when the matching SCB command
// completes, unitistat and residual will be set,
// and then we just return to available.
SET_CTCE_YAV( pDEVBLK->ctcexState );
}
break;
}
// Non-Enhanced CTC processing is retained.
*pUnitStat = CSW_CE | CSW_DE;
break;
case 0x03: // M0MMM011 NOP
// -----------------------------------------------------------
// CONTROL NO-OPERATON
// -----------------------------------------------------------
*pUnitStat = CSW_CE | CSW_DE;
break;
case 0x43: // 00XXX011 SBM
// -----------------------------------------------------------
// SET BASIC MODE
// -----------------------------------------------------------
// Command reject if in basic mode
if( pDEVBLK->ctcxmode == 0 )
{
pDEVBLK->sense[0] = SENSE_CR;
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
break;
}
// Reset extended mode and return normal status
pDEVBLK->ctcxmode = 0;
*pResidual = 0;
*pUnitStat = CSW_CE | CSW_DE;
break;
case 0xC3: // 11000011 SEM
// -----------------------------------------------------------
// SET EXTENDED MODE
// -----------------------------------------------------------
pDEVBLK->ctcxmode = 1;
*pResidual = 0;
*pUnitStat = CSW_CE | CSW_DE;
break;
case 0xE3: // 11100011
// -----------------------------------------------------------
// PREPARE (PREP)
// -----------------------------------------------------------
*pUnitStat = CSW_CE | CSW_DE;
break;
case 0x14: // XXX10100 SCB
// -----------------------------------------------------------
// SENSE COMMAND BYTE
// -----------------------------------------------------------
// Enhanced CTC processing.
if( pDEVBLK->ctctype == CTC_CTCE )
{
// If we were awaiting this SCB, i.e. if we are in the
// YWC state, then we signal matching command received.
if( IS_CTCE_YWC( pDEVBLK->ctcexState ) )
{
SET_CTCE_MATCH( pDEVBLK->ctcexState );
// We complete the above signalling (if any) to the other (y-)side.
CTCE_Send( pDEVBLK, sCount, NULL, pUnitStat, pResidual );
// And only then do we return to state YAV.
SET_CTCE_YAV( pDEVBLK->ctcexState );
}
*pIOBuf = pDEVBLK->ctceyCmdSCB;
*pResidual = sCount - 1;
*pUnitStat = CSW_CE | CSW_DE;
if( pDEVBLK->ccwtrace || pDEVBLK->ccwstep )
{
// "%1d:%04X CTCE: SCB executed: CB=%2.2X (x=%2.2X y=%2.2X)"
WRMSG( HHC05014, "I", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum,
*pIOBuf, pDEVBLK->ctcexState, pDEVBLK->ctceyState );
}
break;
}
// Non-Enhanced CTC processing is retained.
*pUnitStat = CSW_CE | CSW_DE;
break;
case 0x04: // 00000100 SENSE
// -----------------------------------------------------------
// SENSE
// -----------------------------------------------------------
// Command reject if in basic mode
if( pDEVBLK->ctcxmode == 0 )
{
pDEVBLK->sense[0] = SENSE_CR;
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
break;
}
// Calculate residual byte count
iNum = ( sCount < pDEVBLK->numsense ) ?
sCount : pDEVBLK->numsense;
*pResidual = sCount - iNum;
if( sCount < pDEVBLK->numsense )
*pMore = 1;
// Copy device sense bytes to channel I/O buffer
memcpy( pIOBuf, pDEVBLK->sense, iNum );
// Clear the device sense bytes
memset( pDEVBLK->sense, 0, sizeof( pDEVBLK->sense ) );
// Return unit status
*pUnitStat = CSW_CE | CSW_DE;
break;
case 0xE4: // 11100100 SID
// -----------------------------------------------------------
// SENSE ID
// -----------------------------------------------------------
// Calculate residual byte count
iNum = ( sCount < pDEVBLK->numdevid ) ?
sCount : pDEVBLK->numdevid;
*pResidual = sCount - iNum;
if( sCount < pDEVBLK->numdevid )
*pMore = 1;
// Copy device identifier bytes to channel I/O buffer
memcpy( pIOBuf, pDEVBLK->devid, iNum );
// Return unit status
*pUnitStat = CSW_CE | CSW_DE;
break;
default:
// ------------------------------------------------------------
// INVALID OPERATION
// ------------------------------------------------------------
// Set command reject sense byte, and unit check status
pDEVBLK->sense[0] = SENSE_CR;
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
}
// Enhanced CTC processing.
if( pDEVBLK->ctctype == CTC_CTCE )
{
// Clear all flags when in state YAP
if( IS_CTCE_YAP( pDEVBLK->ctcexState ) )
{
CLR_CTCE_ALLF( pDEVBLK->ctcexState );
}
release_lock( &pDEVBLK->lock );
}
}
// ====================================================================
// CTCT Support
// ====================================================================
//
// CTCT_Init
//
static int CTCT_Init( DEVBLK *dev, int argc, char *argv[] )
{
char str[80]; // Thread name
int rc; // Return code
int mtu; // MTU size (binary)
int lport; // Listen port (binary)
int rport; // Destination port (binary)
char* listenp; // Listening port number
char* remotep; // Destination port number
char* mtusize; // MTU size (characters)
char* remaddr; // Remote IP address
struct in_addr ipaddr; // Work area for IP address
BYTE c; // Character work area
TID tid; // Thread ID for server
CTCG_PARMBLK parm; // Parameters for the server
char address[20]=""; // temp space for IP address
dev->devtype = 0x3088;
dev->excps = 0;
dev->ctctype = CTC_CTCT;
SetSIDInfo( dev, 0x3088, 0x08, 0x3088, 0x01 );
// Check for correct number of arguments
if (argc != 4)
{
WRMSG (HHC00915, "E", SSID_TO_LCSS(dev->ssid), dev->devnum );
return -1;
}
// The first argument is the listening port number
listenp = *argv++;
if( strlen( listenp ) > 5 ||
sscanf( listenp, "%u%c", &lport, &c ) != 1 ||
lport < 1024 || lport > 65534 )
{
WRMSG(HHC00916, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "port number", listenp );
return -1;
}
// The second argument is the IP address or hostname of the
// remote side of the point-to-point link
remaddr = *argv++;
if( inet_aton( remaddr, &ipaddr ) == 0 )
{
struct hostent *hp;
if( ( hp = gethostbyname( remaddr ) ) != NULL )
{
memcpy( &ipaddr, hp->h_addr, hp->h_length );
strlcpy( address, inet_ntoa( ipaddr ), sizeof(address) );
remaddr = address;
}
else
{
WRMSG(HHC00916, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "IP address", remaddr );
return -1;
}
}
// The third argument is the destination port number
remotep = *argv++;
if( strlen( remotep ) > 5 ||
sscanf( remotep, "%u%c", &rport, &c ) != 1 ||
rport < 1024 || rport > 65534 )
{
WRMSG(HHC00916, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "port number", remotep );
return -1;
}
// The fourth argument is the maximum transmission unit (MTU) size
mtusize = *argv;
if( strlen( mtusize ) > 5 ||
sscanf( mtusize, "%u%c", &mtu, &c ) != 1 ||
mtu < 46 || mtu > 65536 )
{
WRMSG(HHC00916, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "MTU size", mtusize );
return -1;
}
// Set the device buffer size equal to the MTU size
dev->bufsize = mtu;
// Initialize the file descriptor for the socket connection
// It's a little confusing, but we're using a couple of the
// members of the server paramter structure to initiate the
// outgoing connection. Saves a couple of variable declarations,
// though. If we feel strongly about it, we can declare separate
// variables...
// make a TCP socket
parm.listenfd = socket( AF_INET, SOCK_STREAM, 0 );
if( parm.listenfd < 0 )
{
WRMSG (HHC00900, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "socket()", strerror( HSO_errno ) );
CTCX_Close( dev );
return -1;
}
// bind socket to our local port
// (might seem like overkill, and usually isn't done, but doing this
// bind() to the local port we configure gives the other end a chance
// at validating the connection request)
memset( &(parm.addr), 0, sizeof( parm.addr ) );
parm.addr.sin_family = AF_INET;
parm.addr.sin_port = htons(lport);
parm.addr.sin_addr.s_addr = htonl(INADDR_ANY);
rc = bind( parm.listenfd,
(struct sockaddr *)&parm.addr,
sizeof( parm.addr ) );
if( rc < 0 )
{
WRMSG( HHC00900, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "bind()", strerror( HSO_errno ) );
CTCX_Close( dev );
return -1;
}
// initiate a connection to the other end
memset( &(parm.addr), 0, sizeof( parm.addr ) );
parm.addr.sin_family = AF_INET;
parm.addr.sin_port = htons(rport);
parm.addr.sin_addr = ipaddr;
rc = connect( parm.listenfd,
(struct sockaddr *)&parm.addr,
sizeof( parm.addr ) );
// if connection was not successful, start a server
if( rc < 0 )
{
// used to pass parameters to the server thread
CTCG_PARMBLK* arg;
WRMSG(HHC00971, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, remaddr, remotep );
// probably don't need to do this, not sure...
close_socket( parm.listenfd );
parm.listenfd = socket( AF_INET, SOCK_STREAM, 0 );
if( parm.listenfd < 0 )
{
WRMSG(HHC00900, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "socket()", strerror( HSO_errno ) );
CTCX_Close( dev );
return -1;
}
// set up the listening port
memset( &(parm.addr), 0, sizeof( parm.addr ) );
parm.addr.sin_family = AF_INET;
parm.addr.sin_port = htons(lport);
parm.addr.sin_addr.s_addr = htonl(INADDR_ANY);
if( bind( parm.listenfd,
(struct sockaddr *)&parm.addr,
sizeof( parm.addr ) ) < 0 )
{
WRMSG(HHC00900, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "bind()", strerror( HSO_errno ) );
CTCX_Close( dev );
return -1;
}
if( listen( parm.listenfd, 1 ) < 0 )
{
WRMSG(HHC00900, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "listen()", strerror( HSO_errno ) );
CTCX_Close( dev );
return -1;
}
// we are listening, so create a thread to accept connection
arg = malloc( sizeof( CTCG_PARMBLK ) );
memcpy( arg, &parm, sizeof( parm ) );
arg->dev = dev;
snprintf(str,sizeof(str),"CTCT %4.4X ListenThread",dev->devnum);
str[sizeof(str)-1]=0;
rc = create_thread( &tid, JOINABLE, CTCT_ListenThread, arg, str );
if(rc)
WRMSG(HHC00102, "E", strerror(rc));
}
else // successfully connected (outbound) to the other end
{
WRMSG(HHC00972, "I", SSID_TO_LCSS(dev->ssid), dev->devnum, remaddr, remotep );
dev->fd = parm.listenfd;
}
// for cosmetics, since we are successfully connected or serving,
// fill in some details for the panel.
snprintf( dev->filename, sizeof(dev->filename), "%s:%s", remaddr, remotep );
dev->filename[sizeof(dev->filename)-1] = '\0';
return 0;
}
//
// CTCT_Write
//
static void CTCT_Write( DEVBLK* pDEVBLK, U32 sCount,
BYTE* pIOBuf, BYTE* pUnitStat,
U32* pResidual )
{
PCTCIHDR pFrame; // -> Frame header
PCTCISEG pSegment; // -> Segment in buffer
U16 sOffset; // Offset of next frame
U16 sSegLen; // Current segment length
U16 sDataLen; // Length of IP Frame data
int iPos; // Offset into buffer
U16 i; // Array subscript
int rc; // Return code
BYTE szStackID[33]; // VSE IP stack identity
U32 iStackCmd; // VSE IP stack command
// Check that CCW count is sufficient to contain block header
if( sCount < sizeof( CTCIHDR ) )
{
WRMSG(HHC00906, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, sCount );
pDEVBLK->sense[0] = SENSE_DC;
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
return;
}
// Fix-up frame pointer
pFrame = (PCTCIHDR)pIOBuf;
// Extract the frame length from the header
FETCH_HW( sOffset, pFrame->hwOffset );
// Check for special VSE TCP/IP stack command packet
if( sOffset == 0 && sCount == 40 )
{
// Extract the 32-byte stack identity string
for( i = 0;
i < sizeof( szStackID ) - 1 && i < sCount - 4;
i++)
szStackID[i] = guest_to_host( pIOBuf[i+4] );
szStackID[i] = '\0';
// Extract the stack command word
FETCH_FW( iStackCmd, *((FWORD*)&pIOBuf[36]) );
// Display stack command and discard the packet
WRMSG(HHC00907, "I", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, szStackID, iStackCmd );
*pUnitStat = CSW_CE | CSW_DE;
*pResidual = 0;
return;
}
// Check for special L/390 initialization packet
if( sOffset == 0 )
{
// Return normal status and discard the packet
*pUnitStat = CSW_CE | CSW_DE;
*pResidual = 0;
return;
}
// Adjust the residual byte count
*pResidual -= sizeof( CTCIHDR );
// Process each segment in the buffer
for( iPos = sizeof( CTCIHDR );
iPos < sOffset;
iPos += sSegLen )
{
// Check that the segment is fully contained within the block
if( iPos + sizeof( CTCISEG ) > sOffset )
{
WRMSG(HHC00908, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, iPos );
pDEVBLK->sense[0] = SENSE_DC;
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
return;
}
// Fix-up segment header in the I/O buffer
pSegment = (PCTCISEG)(pIOBuf + iPos);
// Extract the segment length from the segment header
FETCH_HW( sSegLen, pSegment->hwLength );
// Check that the segment length is valid
if( ( sSegLen < sizeof( CTCISEG ) ) ||
( (U32)iPos + sSegLen > sOffset ) ||
( (U32)iPos + sSegLen > sCount ) )
{
WRMSG(HHC00909, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, sSegLen, iPos );
pDEVBLK->sense[0] = SENSE_DC;
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
return;
}
// Calculate length of IP frame data
sDataLen = sSegLen - sizeof( CTCISEG );
// Trace the IP packet before sending
if( pDEVBLK->ccwtrace || pDEVBLK->ccwstep )
{
WRMSG(HHC00934, "I", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pDEVBLK->filename );
if( pDEVBLK->ccwtrace )
packet_trace( pSegment->bData, sDataLen, '>' );
}
// Write the IP packet
rc = write_socket( pDEVBLK->fd, pSegment->bData, sDataLen );
if( rc < 0 )
{
WRMSG(HHC00936, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pDEVBLK->filename,
strerror( HSO_errno ) );
pDEVBLK->sense[0] = SENSE_EC;
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
return;
}
// Adjust the residual byte count
*pResidual -= sSegLen;
// We are done if current segment satisfies CCW count
if( (U32)iPos + sSegLen == sCount )
{
*pResidual -= sSegLen;
*pUnitStat = CSW_CE | CSW_DE;
return;
}
}
// Set unit status and residual byte count
*pUnitStat = CSW_CE | CSW_DE;
*pResidual = 0;
}
//
// CTCT_Read
//
static void CTCT_Read( DEVBLK* pDEVBLK, U32 sCount,
BYTE* pIOBuf, BYTE* pUnitStat,
U32* pResidual, BYTE* pMore )
{
PCTCIHDR pFrame = NULL; // -> Frame header
PCTCISEG pSegment = NULL; // -> Segment in buffer
fd_set rfds; // Read FD_SET
int iRetVal; // Return code from 'select'
int iLength = 0;
static struct timeval tv; // Timeout time for 'select'
// Limit how long we should wait for data to come in
FD_ZERO( &rfds );
FD_SET( pDEVBLK->fd, &rfds );
tv.tv_sec = CTC_READ_TIMEOUT_SECS;
tv.tv_usec = 0;
iRetVal = select( pDEVBLK->fd + 1, &rfds, NULL, NULL, &tv );
switch( iRetVal )
{
case 0:
*pUnitStat = CSW_CE | CSW_DE | CSW_UC | CSW_SM;
pDEVBLK->sense[0] = 0;
return;
case -1:
if( HSO_errno == HSO_EINTR )
return;
WRMSG(HHC00973, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pDEVBLK->filename,
strerror( HSO_errno ) );
pDEVBLK->sense[0] = SENSE_EC;
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
return;
default:
break;
}
// Read an IP packet from the TUN device
iLength = read_socket( pDEVBLK->fd, pDEVBLK->buf, pDEVBLK->bufsize );
// Check for other error condition
if( iLength < 0 )
{
WRMSG(HHC00973, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pDEVBLK->filename,
strerror( HSO_errno ) );
pDEVBLK->sense[0] = SENSE_EC;
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
return;
}
// Trace the packet received from the TUN device
if( pDEVBLK->ccwtrace || pDEVBLK->ccwstep )
{
WRMSG(HHC00913, "I", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, iLength, "TUN" );
packet_trace( pDEVBLK->buf, iLength, '<' );
}
// Fix-up Frame pointer
pFrame = (PCTCIHDR)pIOBuf;
// Fix-up Segment pointer
pSegment = (PCTCISEG)( pIOBuf + sizeof( CTCIHDR ) );
// Initialize segment
memset( pSegment, 0, iLength + sizeof( CTCISEG ) );
// Update next frame offset
STORE_HW( pFrame->hwOffset,
(U16)(iLength + sizeof( CTCIHDR ) + sizeof( CTCISEG )) );
// Store segment length
STORE_HW( pSegment->hwLength, (U16)(iLength + sizeof( CTCISEG )) );
// Store Frame type
STORE_HW( pSegment->hwType, ETH_TYPE_IP );
// Copy data
memcpy( pSegment->bData, pDEVBLK->buf, iLength );
// Fix-up frame pointer and terminate block
pFrame = (PCTCIHDR)( pIOBuf + sizeof( CTCIHDR ) +
sizeof( CTCISEG ) + iLength );
STORE_HW( pFrame->hwOffset, 0x0000 );
// Calculate #of bytes returned including two slack bytes
iLength += sizeof( CTCIHDR ) + sizeof( CTCISEG ) + 2;
if( sCount < (U32)iLength )
{
*pMore = 1;
*pResidual = 0;
iLength = sCount;
}
else
{
*pMore = 0;
*pResidual -= iLength;
}
// Set unit status
*pUnitStat = CSW_CE | CSW_DE;
}
//
// CTCT_ListenThread
//
static void* CTCT_ListenThread( void* argp )
{
int connfd;
socklen_t servlen;
char str[80];
CTCG_PARMBLK parm;
// set up the parameters passed via create_thread
parm = *((CTCG_PARMBLK*) argp);
free( argp );
for( ; ; )
{
servlen = sizeof(parm.addr);
// await a connection
connfd = accept( parm.listenfd,
(struct sockaddr *)&parm.addr,
&servlen );
MSGBUF( str, "%s:%d",
inet_ntoa( parm.addr.sin_addr ),
ntohs( parm.addr.sin_port ) );
if( strcmp( str, parm.dev->filename ) != 0 )
{
WRMSG(HHC00974, "E", SSID_TO_LCSS(parm.dev->ssid), parm.dev->devnum,
parm.dev->filename, str);
close_socket( connfd );
}
else
{
parm.dev->fd = connfd;
}
// Ok, so having done that we're going to loop back to the
// accept(). This was meant to handle the connection failing
// at the other end; this end will be ready to accept another
// connection. Although this will happen, I'm sure you can
// see the possibility for bad things to occur (eg if another
// Hercules tries to connect). This will also be fixed RSN.
}
UNREACHABLE_CODE();
}
// ====================================================================
// VMNET Support -- written by Willem Konynenberg
// ====================================================================
/*-------------------------------------------------------------------*/
/* Definitions for SLIP encapsulation */
/*-------------------------------------------------------------------*/
#define SLIP_END 0300
#define SLIP_ESC 0333
#define SLIP_ESC_END 0334
#define SLIP_ESC_ESC 0335
/*-------------------------------------------------------------------*/
/* Functions to support vmnet written by Willem Konynenberg */
/*-------------------------------------------------------------------*/
static int start_vmnet(DEVBLK *dev, DEVBLK *xdev, int argc, char *argv[])
{
int sockfd[2];
int r, i;
char *ipaddress;
if (argc < 2) {
WRMSG (HHC00915, "E", SSID_TO_LCSS(dev->ssid), dev->devnum);
return -1;
}
ipaddress = argv[0];
argc--;
argv++;
if (socketpair (AF_UNIX, SOCK_STREAM, 0, sockfd) < 0) {
WRMSG (HHC00900, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "socketpair()", strerror(errno));
return -1;
}
r = fork ();
if (r < 0) {
WRMSG (HHC00900, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "fork()", strerror(errno));
return -1;
} else if (r == 0) {
/* child */
close (0);
close (1);
dup (sockfd[1]);
dup (sockfd[1]);
r = (sockfd[0] > sockfd[1]) ? sockfd[0] : sockfd[1];
for (i = 3; i <= r; i++) {
close (i);
}
/* the ugly cast is to silence a compiler warning due to const */
execv (argv[0], (EXECV_ARG2_ARGV_T)argv);
exit (1);
}
close (sockfd[1]);
dev->fd = sockfd[0];
xdev->fd = sockfd[0];
/* We just blindly copy these out in the hope vmnet will pick them
* up correctly. I don't feel like implementing a complete login
* scripting facility here...
*/
write(dev->fd, ipaddress, (u_int)strlen(ipaddress));
write(dev->fd, "\n", 1);
return 0;
}
static int VMNET_Init(DEVBLK *dev, int argc, char *argv[])
{
U16 xdevnum; /* Pair device devnum */
DEVBLK *xdev; /* Pair device */
int rc;
U16 lcss;
dev->devtype = 0x3088;
dev->excps = 0;
/* parameters for network CTC are:
* devnum of the other CTC device of the pair
* ipaddress
* vmnet command line
*
* CTC adapters are used in pairs, one for READ, one for WRITE.
* The vmnet is only initialised when both are initialised.
*/
if (argc < 3) {
WRMSG(HHC00915, "E", SSID_TO_LCSS(dev->ssid), dev->devnum);
return -1;
}
rc=parse_single_devnum(argv[0],&lcss,&xdevnum);
if (rc<0)
{
WRMSG(HHC00916, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "device number", argv[0]);
return -1;
}
xdev = find_device_by_devnum(lcss,xdevnum);
if (xdev != NULL) {
if (start_vmnet(dev, xdev, argc - 1, &argv[1]))
return -1;
}
strlcpy(dev->filename, "vmnet", sizeof(dev->filename) );
/* Set the control unit type */
/* Linux/390 currently only supports 3088 model 2 CTCA and ESCON */
dev->ctctype = CTC_VMNET;
SetSIDInfo( dev, 0x3088, 0x08, 0x3088, 0x01 );
/* Initialize the device dependent fields */
dev->ctcpos = 0;
dev->ctcrem = 0;
/* Set length of buffer */
/* This size guarantees we can write a full iobuf of 65536
* as a SLIP packet in a single write. Probably overkill... */
dev->bufsize = 65536 * 2 + 1;
return 0;
}
static int VMNET_Write(DEVBLK *dev, BYTE *iobuf, U32 count, BYTE *unitstat)
{
U32 blklen = (iobuf[0]<<8) | iobuf[1];
U32 pktlen;
BYTE *p = iobuf + 2;
BYTE *buffer = dev->buf;
U32 len = 0, rem;
if (count < blklen) {
WRMSG (HHC00975, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "block", count, blklen);
blklen = count;
}
while (p < iobuf + blklen) {
pktlen = (p[0]<<8) | p[1];
rem = iobuf + blklen - p;
if (rem < pktlen) {
WRMSG (HHC00975, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "packet", rem, pktlen);
pktlen = rem;
}
if (pktlen < 6) {
WRMSG (HHC00975, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "packet", pktlen, 6);
pktlen = 6;
}
pktlen -= 6;
p += 6;
while (pktlen--) {
switch (*p) {
case SLIP_END:
buffer[len++] = SLIP_ESC;
buffer[len++] = SLIP_ESC_END;
break;
case SLIP_ESC:
buffer[len++] = SLIP_ESC;
buffer[len++] = SLIP_ESC_ESC;
break;
default:
buffer[len++] = *p;
break;
}
p++;
}
buffer[len++] = SLIP_END;
write(dev->fd, buffer, len); /* should check error conditions? */
len = 0;
}
*unitstat = CSW_CE | CSW_DE;
return count;
}
static int bufgetc(DEVBLK *dev, int blocking)
{
BYTE *bufp = dev->buf + dev->ctcpos, *bufend = bufp + dev->ctcrem;
int n;
if (bufp >= bufend) {
if (blocking == 0) return -1;
do {
n = read(dev->fd, dev->buf, dev->bufsize);
if (n <= 0) {
if (n == 0) {
/* VMnet died on us. */
WRMSG (HHC00976, "E", SSID_TO_LCSS(dev->ssid), dev->devnum);
/* -2 will cause an error status to be set */
return -2;
}
if( n == EINTR )
return -3;
WRMSG (HHC00900, "E", SSID_TO_LCSS(dev->ssid), dev->devnum, "read()", strerror(errno));
SLEEP(2);
}
} while (n <= 0);
dev->ctcrem = n;
bufend = &dev->buf[n];
dev->ctclastpos = dev->ctclastrem = dev->ctcpos = 0;
bufp = dev->buf;
}
dev->ctcpos++;
dev->ctcrem--;
return *bufp;
}
static void setblkheader(BYTE *iobuf, int buflen)
{
iobuf[0] = (buflen >> 8) & 0xFF;
iobuf[1] = buflen & 0xFF;
}
static void setpktheader(BYTE *iobuf, int packetpos, int packetlen)
{
iobuf[packetpos] = (packetlen >> 8) & 0xFF;
iobuf[packetpos+1] = packetlen & 0xFF;
iobuf[packetpos+2] = 0x08;
iobuf[packetpos+3] = 0;
iobuf[packetpos+4] = 0;
iobuf[packetpos+5] = 0;
}
/* read data from the CTC connection.
* If a packet overflows the iobuf or the read buffer runs out, there are
* 2 possibilities:
* - block has single packet: continue reading packet, drop bytes,
* then return truncated packet.
* - block has multiple packets: back up on last packet and return
* what we have. Do this last packet in the next IO.
*/
static int VMNET_Read(DEVBLK *dev, BYTE *iobuf, U32 count, BYTE *unitstat)
{
int c; /* next byte to process */
U32 len = 8; /* length of block */
U32 lastlen = 2; /* block length at last pckt */
dev->ctclastpos = dev->ctcpos;
dev->ctclastrem = dev->ctcrem;
while (1) {
c = bufgetc(dev, lastlen == 2);
if (c < 0) {
if(c == -3)
return 0;
/* End of input buffer. Return what we have. */
setblkheader (iobuf, lastlen);
dev->ctcpos = dev->ctclastpos;
dev->ctcrem = dev->ctclastrem;
*unitstat = CSW_CE | CSW_DE | (c == -2 ? CSW_UX : 0);
return lastlen;
}
switch (c) {
case SLIP_END:
if (len > 8) {
/* End of packet. Set up for next. */
setpktheader (iobuf, lastlen, len-lastlen);
dev->ctclastpos = dev->ctcpos;
dev->ctclastrem = dev->ctcrem;
lastlen = len;
len += 6;
}
break;
case SLIP_ESC:
c = bufgetc(dev, lastlen == 2);
if (c < 0) {
if(c == -3)
return 0;
/* End of input buffer. Return what we have. */
setblkheader (iobuf, lastlen);
dev->ctcpos = dev->ctclastpos;
dev->ctcrem = dev->ctclastrem;
*unitstat = CSW_CE | CSW_DE | (c == -2 ? CSW_UX : 0);
return lastlen;
}
switch (c) {
case SLIP_ESC_END:
c = SLIP_END;
break;
case SLIP_ESC_ESC:
c = SLIP_ESC;
break;
}
/* FALLTHRU */
default:
if (len < count) {
iobuf[len++] = c;
} else if (lastlen > 2) {
/* IO buffer is full and we have data to return */
setblkheader (iobuf, lastlen);
dev->ctcpos = dev->ctclastpos;
dev->ctcrem = dev->ctclastrem;
*unitstat = CSW_CE | CSW_DE | (c == -2 ? CSW_UX : 0);
return lastlen;
} /* else truncate end of very large single packet... */
}
}
}
/*-------------------------------------------------------------------*/
/* End of VMNET functions written by Willem Konynenberg */
/*-------------------------------------------------------------------*/
// ====================================================================
// CTCE Support
// ====================================================================
// CTC Enhanced
// ============
// Enhanced CTC functionality is designed to emulate real
// 3088 CTC Adapter hardware, using a pair of TCP sockets
// with a likewise configured Hercules instance on a
// different PC (or same PC). The new device type is CTCE.
// The implementation is based mostly on an IBM publication,
// "ESCON Channel-to-Channel Adapter", SA22-7203-00, although
// no claim for completeness of this implemenation is feasible.
// The CTCE configuration is similar to the CTCT device. The
// MTU bufsize parameter is optional, but when specified must be
// >= CTCE_MTU_MIN (=32778). This is the default value when omitted.
// (Please note that 32778 = sizeof(CTCE_SOKPFX) + sizeof(sCount) + 32K,
// with 32K the maximum sCount experienced in CTC CCW programs.)
// CTCE requires an even-odd pair of port numbers per device side
// but only the even port numbers are to be configured; the odd
// numbers are just derived by adding 1 to the (configured) even
// port numbers. The socket connection pairs cross-connect, the
// arrows showing the send->receive direction :
//
// x-lport-even -> y-rport-odd
// x-lport-odd <- y-rport-even
//
// A sample CTCE device configuration is shown below:
//
// Hercules PC Host A with IP address 192.168.1.100 :
//
// 0E40 CTCE 30880 192.168.1.200 30880
// 0E41 CTCE 30882 192.168.1.200 30882
//
// Hercules PC Host B with IP address 192.168.1.200 :
//
// 0E40 CTCE 30880 192.168.1.100 30880
// 0E41 CTCE 30882 192.168.1.100 30882
//
// CTCE_Init
//
static int CTCE_Init( DEVBLK *dev, int argc, char *argv[] )
{
char str[80]; // Thread name
int rc; // Return code
int mtu; // MTU size (binary)
int lport; // Listen port (binary)
int rport; // Destination port (binary)
char* listenp; // Listening port number
char* remotep; // Destination port number
char* mtusize; // MTU size (characters)
char* remaddr; // Remote IP address
struct in_addr ipaddr; // Work area for IP address
BYTE c; // Character work area
TID tid; // Thread ID for server
TID tid2; // Thread ID for read thread
u_int ctceWrPort = 0; // 0=read port, 1=write port
int ctceSmlBin; // Small size (binary)
char* ctceSmlChr; // Small size (characters)
CTCE_PARMBLK parm; // Parameters for the server
char address[20]=""; // temp space for IP address
dev->devtype = 0x3088;
dev->ctctype = CTC_CTCE;
SetSIDInfo( dev, 0x3088, 0x08, 0x3088, 0x01 );
// Enhanced CTC needs extended mode from the start.
dev->ctcxmode = 1;
// Mark both socket file descriptors as not yet connected.
dev->fd = -1;
dev->ctcefd = -1;
// Check for correct number of arguments
if( (argc < 3) && (argc > 5) )
{
// "%1d:%04X CTCE: Incorrect number of parameters"
WRMSG( HHC05000, "E", SSID_TO_LCSS( dev->ssid ), dev->devnum );
return -1;
}
// The first argument is the listening port number
// which for CTCE must be an even port number.
listenp = *argv++;
if( strlen( listenp ) > 5 ||
sscanf( listenp, "%u%c", &lport, &c ) != 1 ||
lport < 1024 || lport > 65534 )
{
// "%1d:%04X CTCE: Invalid port number: %s"
WRMSG( HHC05003, "E", SSID_TO_LCSS( dev->ssid ), dev->devnum,
listenp );
return -1;
}
if( lport % 2 )
{
// "%1d:%04X CTCE: local port number not even: %s"
WRMSG( HHC05012, "E", SSID_TO_LCSS( dev->ssid ), dev->devnum,
listenp );
return -1;
}
// The second argument is the IP address or hostname of the
// remote side of the point-to-point link
remaddr = *argv++;
if( inet_aton( remaddr, &ipaddr ) == 0 )
{
struct hostent *hp;
if( ( hp = gethostbyname( remaddr ) ) != NULL )
{
memcpy( &ipaddr, hp->h_addr, hp->h_length );
strcpy( address, inet_ntoa( ipaddr ) );
remaddr = address;
}
else
{
// "%1d:%04X CTCE: Invalid IP address %s"
WRMSG( HHC05004, "E", SSID_TO_LCSS( dev->ssid ), dev->devnum,
remaddr );
return -1;
}
}
// The third argument is the destination port number
// which for CTCE must be an even port number.
remotep = *argv++;
if( strlen( remotep ) > 5 ||
sscanf( remotep, "%u%c", &rport, &c ) != 1 ||
rport < 1024 || rport > 65534 )
{
// "%1d:%04X CTCE: Invalid port number: %s"
WRMSG( HHC05003, "E", SSID_TO_LCSS( dev->ssid ), dev->devnum,
remotep );
return -1;
}
if( rport % 2 )
{
// "%1d:%04X CTCE: remote port number not even: %s"
WRMSG( HHC05013, "E", SSID_TO_LCSS( dev->ssid ), dev->devnum,
remotep );
return -1;
}
// Enhanced CTC default MTU bufsize is CTCE_MTU_MIN.
if( argc < 4 )
{
mtu = CTCE_MTU_MIN;
}
else
{
// The fourth argument is the maximum transmission unit (MTU) size
mtusize = *argv;
if( strlen( mtusize ) > 5 ||
sscanf( mtusize, "%u%c", &mtu, &c ) != 1 ||
mtu < CTCE_MTU_MIN || mtu > 65536 )
{
// "%1d:%04X CTCE: Invalid MTU size %s, allowed range is %d to 65536"
WRMSG( HHC05005, "E", SSID_TO_LCSS( dev->ssid ), dev->devnum,
mtusize, CTCE_MTU_MIN );
return -1;
}
}
// Enhanced CTCT only supports an optional 5th parameter,
// the Small MTU size, which defaults to the minimum size
// of the TCP/IP packets exchanged: CTCE_SOKPFX.
ctceSmlBin = sizeof(CTCE_SOKPFX);
if( argc == 5 )
{
ctceSmlChr = *(++argv);
if( strlen( ctceSmlChr ) > 5 ||
sscanf( ctceSmlChr, "%u%c", &ctceSmlBin, &c ) != 1 ||
ctceSmlBin < (int)sizeof(CTCE_SOKPFX) || ctceSmlBin > mtu )
{
ctceSmlBin = sizeof(CTCE_SOKPFX);
// "%1d:%04X CTCE: Invalid Small MTU size %s ignored"
WRMSG( HHC05006, "W", SSID_TO_LCSS( dev->ssid ), dev->devnum,
ctceSmlChr );
}
}
dev->ctceSndSml = ctceSmlBin;
// Set the device buffer size equal to the MTU size
dev->bufsize = mtu;
// Initialize the file descriptor for the socket connection
// It's a little confusing, but we're using a couple of the
// members of the server paramter structure to initiate the
// outgoing connection. Saves a couple of variable declarations,
// though. If we feel strongly about it, we can declare separate
// variables...
// Enhanced CTCT will require a second pass for the odd port number.
for(ctceWrPort = 0; ctceWrPort <= 1; ctceWrPort++ )
{
// make a TCP socket
parm.listenfd[ctceWrPort] = socket( AF_INET, SOCK_STREAM, 0 );
if( parm.listenfd[ctceWrPort] < 0 )
{
// "%1d:%04X CTCE: Error creating socket: %s"
WRMSG( HHC05007, "E", SSID_TO_LCSS( dev->ssid ), dev->devnum,
strerror( HSO_errno ) );
CTCX_Close( dev );
return -1;
}
// bind socket to our local port
// (might seem like overkill, and usually isn't done, but doing this
// bind() to the local port we configure gives the other end a chance
// at validating the connection request)
memset( &(parm.addr), 0, sizeof( parm.addr ) );
parm.addr.sin_family = AF_INET;
parm.addr.sin_port = htons(lport + ctceWrPort);
parm.addr.sin_addr.s_addr = htonl(INADDR_ANY);
rc = bind( parm.listenfd[ctceWrPort],
(struct sockaddr *)&parm.addr,
sizeof( parm.addr ) );
if( rc < 0 )
{
// "%1d:%04X CTCE: Error binding to socket (port %d): %s"
WRMSG( HHC05008, "E", SSID_TO_LCSS( dev->ssid ), dev->devnum,
lport + ctceWrPort, strerror( HSO_errno ) );
CTCX_Close( dev );
return -1;
}
// initiate a connection to the other end
memset( &(parm.addr), 0, sizeof( parm.addr ) );
parm.addr.sin_family = AF_INET;
// the even (=read) port must connect to the odd (=write) port
// at the other side and vice-versa
parm.addr.sin_port = htons(rport + ( ( ctceWrPort + 1 ) % 2 ) );
parm.addr.sin_addr = ipaddr;
rc = connect( parm.listenfd[ctceWrPort],
(struct sockaddr *)&parm.addr,
sizeof( parm.addr ) );
// if connection was not successful, start a server
if( rc < 0 )
{
// used to pass parameters to the server thread
CTCE_PARMBLK* arg;
// "%1d:%04X CTCE: Waiting for connection :%d %s %s:%d"
WRMSG( HHC05015, "I", SSID_TO_LCSS( dev->ssid ), dev->devnum,
lport + ctceWrPort, ctceWrPort == 0 ? "->" : "<-",
remaddr, rport + ( ( ctceWrPort + 1 ) % 2 ) );
// probably don't need to do this, not sure...
close_socket( parm.listenfd[ctceWrPort] );
parm.listenfd[ctceWrPort] = socket( AF_INET, SOCK_STREAM, 0 );
if( parm.listenfd[ctceWrPort] < 0 )
{
// "%1d:%04X CTCE: Error creating socket: %s"
WRMSG( HHC05007, "E", SSID_TO_LCSS( dev->ssid ), dev->devnum,
strerror( HSO_errno ) );
CTCX_Close( dev );
return -1;
}
// set up the listening port
memset( &(parm.addr), 0, sizeof( parm.addr ) );
parm.addr.sin_family = AF_INET;
parm.addr.sin_port = htons(lport + ctceWrPort) ;
parm.addr.sin_addr.s_addr = htonl(INADDR_ANY);
if( bind( parm.listenfd[ctceWrPort],
(struct sockaddr *)&parm.addr,
sizeof( parm.addr ) ) < 0 )
{
// "%1d:%04X CTCE: Error binding to socket (port %d): %s"
WRMSG( HHC05008, "E", SSID_TO_LCSS( dev->ssid ), dev->devnum,
lport + ctceWrPort, strerror( HSO_errno ) );
CTCX_Close( dev );
return -1;
}
if( listen( parm.listenfd[ctceWrPort], 1 ) < 0 )
{
// "%1d:%04X CTCE: Error on call to listen (port=%d): %s"
WRMSG( HHC05009, "E", SSID_TO_LCSS( dev->ssid ), dev->devnum,
lport + ctceWrPort, strerror( HSO_errno ) );
CTCX_Close( dev );
return -1;
}
// we are listening, so create a thread to accept connection
arg = malloc( sizeof( CTCE_PARMBLK ) );
memcpy( arg, &parm, sizeof( parm ) );
arg->dev = dev;
arg->ctceWrPort = ctceWrPort;
snprintf(str,sizeof(str),"CTCE %4.4X ListenThread %d",dev->devnum, ctceWrPort);
str[sizeof(str)-1]=0;
create_thread( &tid, JOINABLE, CTCE_ListenThread, arg, str );
}
else // successfully connected (outbound) to the other end
{
// "%1d:%04X CTCE: Established connection :%d %s %s:%d"
WRMSG( HHC05016, "I", SSID_TO_LCSS( dev->ssid ), dev->devnum,
lport + ctceWrPort, ctceWrPort == 0 ? "->" : "<-",
remaddr, rport + ( ( ctceWrPort + 1 ) % 2 ) );
// The even local port (form the config) is for writing
if( ctceWrPort == 0 )
{
dev->fd = parm.listenfd[ctceWrPort];
}
else
{
// The next odd local port (form the config) is for reading
dev->ctcefd = parm.listenfd[ctceWrPort];
// This side is ready to start receiving and sending so we
// start a read thread to do the receiving part; identical
// code will be found in the CTCT_ListenThread after a
// successful connect was accepted there.
snprintf(str,sizeof(str),"CTCE %4.4X RecvThread %d",dev->devnum, ctceWrPort);
str[sizeof(str)-1]=0;
create_thread( &tid2, JOINABLE, CTCE_RecvThread, dev, str );
}
}
// for cosmetics, since we are successfully connected or serving,
// fill in some details for the panel.
// Also used for connection verification in CTCE_ListenThread
sprintf( dev->filename, "%s:%d", remaddr, rport + ( ( ctceWrPort + 1 ) % 2 ) );
}
// Enhanced CTC adapter intiialization for command register and CB.
dev->ctcexCmd = 0x00;
dev->ctceyCmd = 0x00;
dev->ctceyCmdSCB = 0x00;
// Enhanced CTC adapter sides are state-aware, with initial
// state "Available" = YAV which corresponds to column 5 in
// the table 2.13 in SA22-7203-00, i.e. we consider both
// x- and y-side READY from the start. ALL Flags are cleared.
CLR_CTCE_ALLF( dev->ctcexState );
SET_CTCE_YAV ( dev->ctcexState );
CLR_CTCE_ALLF( dev->ctceyState );
SET_CTCE_YAV ( dev->ctceyState );
// Initialize the 12 bits Send->Recv packet sequence ID with
// the leftmost 4 bits C of the CCUU devnum at this side,
// which helps distinguishing same-host traffic if the
// Send-Recv side CCUU's have a diffent leftmost C.
dev->ctcePktSeq = dev->devnum & 0xF000;
// Initialize the CTC lock and condition used to signal
// reception of a command matching the dependent one.
initialize_lock( &dev->ctceEventLock );
initialize_condition( &dev->ctceEvent );
return 0;
}
//
// CTCE_ListenThread
//
static void* CTCE_ListenThread( void* argp )
{
int connfd;
socklen_t servlen;
char str[80];
CTCE_PARMBLK parm;
TID tid2; // Thread ID for read thread
// set up the parameters passed via create_thread
parm = *((CTCE_PARMBLK*) argp);
free( argp );
for( ; ; )
{
servlen = sizeof(parm.addr);
// await a connection
connfd = accept( parm.listenfd[parm.ctceWrPort],
(struct sockaddr *)&parm.addr,
&servlen );
sprintf( str, "%s:%d",
inet_ntoa( parm.addr.sin_addr ),
ntohs( parm.addr.sin_port ) - (parm.ctceWrPort + 1) % 2 );
if( strcmp( str, parm.dev->filename ) != 0 )
{
// "%1d:%04X CTCE: Incorrect client or config error: config=%s+%d, connecting client=%s"
WRMSG( HHC05001, "E", SSID_TO_LCSS( parm.dev->ssid ), parm.dev->devnum,
parm.dev->filename, parm.ctceWrPort, str);
close_socket( connfd );
}
else
{
// The even local port (as in the config) is for writing
if( parm.ctceWrPort == 0 )
{
parm.dev->fd = connfd;
}
else
{
// The next odd local port is for reading
parm.dev->ctcefd = connfd;
// This side is ready to start receiving and sending so we
// start a read thread to do the receiving part; identical
// code will be found in the CTCT_ListenThread after a
// successful connect was accepted there
snprintf(str,sizeof(str),"CTCE %1d:%04X RecvThread %d",
SSID_TO_LCSS( parm.dev->ssid ),parm.dev->devnum, parm.ctceWrPort);
str[sizeof(str)-1]=0;
create_thread( &tid2, JOINABLE, CTCE_RecvThread, parm.dev, str );
}
}
// Ok, so having done that we're going to loop back to the
// accept(). This was meant to handle the connection failing
// at the other end; this end will be ready to accept another
// connection. Although this will happen, I'm sure you can
// see the possibility for bad things to occur (eg if another
// Hercules tries to connect). This will also be fixed RSN.
}
UNREACHABLE_CODE();
}
//
// CTCE_Send
//
static void CTCE_Send( DEVBLK* pDEVBLK, U32 sCount,
BYTE* pIOBuf, BYTE* pUnitStat,
U32* pResidual )
{
CTCE_SOKPFX *pSokBuf; // overlay for buf inside DEVBLK
int rc; // Return code
int i; // temp counter
U32 XORChk = 0; // XOR of sent buffer for checking
BYTE *pXOR = (BYTE*)&XORChk; // -> XORChk
BYTE *pBuf = pDEVBLK->buf; // temp pointer inside buf
pSokBuf = (CTCE_SOKPFX*) pDEVBLK->buf;
pSokBuf->CmdReg = pDEVBLK->ctcexCmd;
pSokBuf->FsmSta = pDEVBLK->ctcexState;
pSokBuf->sCount = sCount;
pSokBuf->PktSeq = ++pDEVBLK->ctcePktSeq;
pSokBuf->SndLen = pDEVBLK->ctceSndSml;
// We only ever Send if the sockets are connected.
if( ( pDEVBLK->fd < 0) || ( pDEVBLK->ctcefd < 0) )
return ;
// Only a (non-WEOF) write command data includes sending the IOBuf.
if( IS_CTCE_CCW_WRT( pDEVBLK->ctcexCmd ) )
{
memcpy( pDEVBLK->buf + sizeof(CTCE_SOKPFX), pIOBuf, sCount );
// Increase the SndLen if the sCount is too large.
if( pSokBuf->SndLen < ( sCount + sizeof(CTCE_SOKPFX) ) )
pSokBuf->SndLen = ( sCount + sizeof(CTCE_SOKPFX) );
//1 if( pDEVBLK->ccwstep )
//1 packet_trace( pIOBuf, sCount );
// If bufsize (init from the MTU parameter) is not large enough
// then we will have a severe error as the CTC will not connect.
if( pDEVBLK->bufsize < pSokBuf->SndLen )
{
// "%1d:%04X CTCE: bufsize parameter %d is too small; increase at least to %d"
WRMSG( HHC05022, "S", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum,
pDEVBLK->bufsize, pSokBuf->SndLen );
}
}
// Write the all of this to the other (y-)side.
rc = write_socket( pDEVBLK->fd, pDEVBLK->buf, pSokBuf->SndLen );
// Trace the IP packet just sent if needed.
if( pDEVBLK->ccwtrace || pDEVBLK->ccwstep )
{
XORChk = 0;
pBuf = pDEVBLK->buf;
for(i = 0; i < pSokBuf->SndLen; i++)
{
if( (i % 4) == 0 )
pXOR = (BYTE*)&XORChk;
*pXOR++ ^= *pBuf++;
}
// "%1d:%04X CTCE: Send %4.4X->%s %s=%2.2X x=%2.2X y=%2.2X l=%4.4X k=%8.8X"
WRMSG( HHC05023, "I", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum,
pSokBuf->PktSeq, pDEVBLK->filename,
CTCE_CmdStr[CTCE_Cmd[pDEVBLK->ctcexCmd]], pDEVBLK->ctcexCmd,
pDEVBLK->ctcexState, pDEVBLK->ctceyState, sCount, XORChk ) ;
if( pDEVBLK->ccwtrace )
packet_trace( pDEVBLK->buf, pSokBuf->SndLen, '>' );
}
if( rc < 0 )
{
// "%1d:%04X CTCE: Error writing to %s: %s"
WRMSG( HHC05011, "E", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum,
pDEVBLK->filename, strerror( HSO_errno ) );
pDEVBLK->sense[0] = SENSE_EC;
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
return;
}
// The ATTN flag signals entering a Working(D) state. If this was
// because of a READ command then we need to wait for the matching
// WRITE command from the other (y-)side to arrive.
// Please note that initially we believed such processing to be
// needed for READ and CONTROL commands as well, but this turned out
// not to be the case. Even stronger, we have not yet seen a READ
// command BEFORE the matching WRITE command, thus this processing
// is perhaps never needed at all ...
// (PJJ, April 2014)
else if( IS_CTCE_ATTN( pDEVBLK->ctcexState ) &&
IS_CTCE_CCW_RED( pDEVBLK->ctcexCmd ) )
// IS_CCW_READ( pDEVBLK->ctcexCmd ) )
{
struct timespec waittime;
struct timeval now;
// Any request to signal attention has now been sent.
CLR_CTCE_ATTN( pDEVBLK->ctcexState );
gettimeofday( &now, NULL );
waittime.tv_sec = now.tv_sec + CTC_READ_TIMEOUT_SECS;
waittime.tv_nsec = now.tv_usec * 1000;
obtain_lock( &pDEVBLK->ctceEventLock );
rc = timed_wait_condition( &pDEVBLK->ctceEvent,
&pDEVBLK->ctceEventLock,
&waittime );
release_lock( &pDEVBLK->ctceEventLock );
// Trace the reception of the matching command, or the wait timeout (RC=138).
if( pDEVBLK->ccwtrace || pDEVBLK->ccwstep )
{
// "%1d:%04X CTCE: Send %4.4X->%s %s=%2.2X x=%2.2X y=%2.2X: wait RC=%d"
WRMSG( HHC05024, "W", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum,
pSokBuf->PktSeq, pDEVBLK->filename,
CTCE_CmdStr[CTCE_Cmd[pDEVBLK->ctcexCmd]], pDEVBLK->ctcexCmd,
pDEVBLK->ctcexState, pDEVBLK->ctceyState, rc ) ;
}
// First we check for Halt or Clear Subchannel
if( rc == ETIMEDOUT || rc == EINTR )
{
// check for halt condition
if( pDEVBLK->scsw.flag2 & SCSW2_FC_HALT ||
pDEVBLK->scsw.flag2 & SCSW2_FC_CLEAR )
{
if( pDEVBLK->ccwtrace || pDEVBLK->ccwstep )
{
// "%1d:%04X CTCE: Halt or Clear Recognized"
WRMSG( HHC05017, "I", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum );
}
*pUnitStat = CSW_CE | CSW_DE;
*pResidual = sCount;
}
// Other timeouts or errors should not occur.
else
{
*pUnitStat = CSW_CE | CSW_DE | CSW_UC | CSW_SM;
pDEVBLK->sense[0] = 0;
}
return;
}
// A WRITE EOF command from the other side will have resulted
// in the WEOF flag being set. If this was a matching command
// for a READ then unit exception needs to be included.
else if( IS_CTCE_WEOF( pDEVBLK->ctcexState ) )
{
*pResidual = 0;
*pUnitStat = CSW_CE | CSW_DE | CSW_UX;
if( pDEVBLK->ccwtrace || pDEVBLK->ccwstep )
{
// "%1d:%04X CTCE: Recv %4.4X<-%s %s=%2.2X x=%2.2X y=%2.2X: WEOF -> UX"
WRMSG( HHC05025, "I", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum,
pSokBuf->PktSeq, pDEVBLK->filename,
CTCE_CmdStr[CTCE_Cmd[pDEVBLK->ctcexCmd]], pDEVBLK->ctcexCmd,
pDEVBLK->ctcexState, pDEVBLK->ctceyState ) ;
}
return;
}
}
// Reset the attention signal flag.
CLR_CTCE_ATTN( pDEVBLK->ctcexState );
// If the command (by now matched) was a READ command, then the
// other (y-)side data is available in the DEVBLK buf, so we
// can copy it into the IO channel buffer and compute residual.
// if( IS_CCW_READ( pDEVBLK->ctcexCmd ) )
if( IS_CTCE_CCW_RED( pDEVBLK->ctcexCmd ) )
{
// The actual length of data transferred is the minimum of
// the current READ sCount, and the original WRITE sCount
// which is recorded immediately following the CTCE_SOKPFX.
pSokBuf->sCount =
( sCount <= *(U16*)( pDEVBLK->buf + sizeof(CTCE_SOKPFX) ) )
? sCount : *(U16*)( pDEVBLK->buf + sizeof(CTCE_SOKPFX) );
// Immediately followed by the WRITE data previously received.
memcpy( pIOBuf, pDEVBLK->buf + sizeof(CTCE_SOKPFX) + sizeof(pSokBuf->sCount),
pSokBuf->sCount ) ;
*pResidual = sCount - pSokBuf->sCount;
//1 if( pDEVBLK->ccwstep )
//1 packet_trace( pIOBuf, sCount );
}
else
*pResidual = 0;
// if( IS_CCW_WRITE( pDEVBLK->ctcexCmd ) )
if( IS_CTCE_CCW_WRA( pDEVBLK->ctcexCmd ) )
*pUnitStat = 0;
else
*pUnitStat = CSW_CE | CSW_DE;
return;
}
//
// CTCE_RecvThread
//
static void* CTCE_RecvThread( void* argp )
{
DEVBLK *pDEVBLK = (DEVBLK*) argp; // device block pointer
CTCE_SOKPFX *pSokBuf; // overlay for buf inside DEVBLK
ssize_t iLength = 0;
BYTE *buf; //-> Device recv data buffer
U64 ctcePktSeq = 0; // Recvd Packet Sequence ID
U64 ctceBytCnt = 0; // Recvd Byte Count
int i; // temp counter
int rc; // device_attention RC
U32 XORChk = 0; // XOR of sent buffer for checking
BYTE *pXOR = (BYTE*)&XORChk; // -> XORChk
BYTE *pBuf = pDEVBLK->buf; // temp pointer inside buf
// "%1d:%04X CTCE: Read thread started for %s (bufsize=%d,%d)"
WRMSG( HHC05018, "I", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum,
pDEVBLK->filename, pDEVBLK->bufsize, pDEVBLK->ctceSndSml );
// avoid having to lock the DEVBLK whilst awaiting data to arrive via read_socket
buf = malloc( pDEVBLK->bufsize );
pSokBuf = (CTCE_SOKPFX*)buf;
// This thread will loop until we receive a zero-length packet
for( ; ; )
{
// We read whatever the other (y-)side of the CTC has sent us,
// which by now won't block until the complete bufsize is received.
iLength = read_socket( pDEVBLK->ctcefd, buf, pDEVBLK->ctceSndSml );
// Followed by the receiving the rest if the default SndLen was too small.
if( ( pDEVBLK->ctceSndSml < pSokBuf->SndLen ) && ( iLength != 0 ) )
iLength += read_socket( pDEVBLK->ctcefd, buf + pDEVBLK->ctceSndSml,
pSokBuf->SndLen - pDEVBLK->ctceSndSml );
// In case we are closing down this thread can end.
if( iLength == 0 )
{
// And the other end can then close down as well.
CTCX_Close( pDEVBLK );
// We report some statistics.
// "%1d:%04X CTCE: Zero length read from %s"
WRMSG( HHC05020, "I", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum,
pDEVBLK->filename );
// "%1d:%04X CTCE: %d MB received in %d packets"
WRMSG( HHC05021, "I", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum,
ctceBytCnt / 1048576 , ctcePktSeq );
free( buf );
return NULL; // make compiler happy
}
// Changes to DEVBLK must be lock protected as other threads might update as well.
obtain_lock( &pDEVBLK->lock );
// Check for other error condition
if( iLength < 0 )
{
// "%1d:%04X CTCE: Error reading from %s: %s"
WRMSG( HHC05010, "E", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum,
pDEVBLK->filename, strerror( HSO_errno ) );
pDEVBLK->sense[0] = SENSE_EC;
pDEVBLK->scsw.unitstat = CSW_CE | CSW_DE | CSW_UC;
}
else
{
ctcePktSeq += 1 ;
ctceBytCnt += iLength ;
pDEVBLK->ctceyCmd = pSokBuf->CmdReg;
pDEVBLK->ctceyState = pSokBuf->FsmSta;
// Trace the packet received from the other side of the CTC
if( pDEVBLK->ccwtrace || pDEVBLK->ccwstep )
{
XORChk = 0;
pBuf = buf;
for(i = 0; i < iLength; i++)
{
if( (i % 4) == 0 )
pXOR = (BYTE*)&XORChk;
*pXOR++ ^= *pBuf++;
}
// "%1d:%04X CTCE: Recv %4.4X<-%s %s=%2.2X x=%2.2X y=%2.2X l=%4.4X k=%8.8X"
WRMSG( HHC05026, "I", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum,
pSokBuf->PktSeq, pDEVBLK->filename,
CTCE_CmdStr[CTCE_Cmd[pDEVBLK->ctceyCmd]], pDEVBLK->ctceyCmd,
pDEVBLK->ctcexState, pDEVBLK->ctceyState, pSokBuf->sCount, XORChk ) ;
if( pDEVBLK->ccwtrace )
packet_trace( buf, iLength, '<' );
}
// Only if the other (y-)side sent us a write command will
// we copy the socket buffer into the device buffer.
if( IS_CTCE_CCW_WRT( pDEVBLK->ctceyCmd ) )
{
// We retain the sCount of this WRITE command for later
// comparison against the matching READ command, ahead
// of the data itself following CTCE_SOKPFX.
*(U16*)( pDEVBLK->buf + sizeof(CTCE_SOKPFX) ) = pSokBuf->sCount ;
memcpy( pDEVBLK->buf + sizeof(CTCE_SOKPFX) + sizeof(pSokBuf->sCount) ,
buf + sizeof(CTCE_SOKPFX), pSokBuf->sCount );
}
// If the other side sent us a WRITE EOF command
// then we just set the WEOF flag on our side.
if( IS_CTCE_CCW_WEF( pDEVBLK->ctceyCmd ) )
{
SET_CTCE_WEOF( pDEVBLK->ctcexState );
// But only if this does NOT match a Working(D) Read command will it remain set.
if( ( pDEVBLK->ccwtrace || pDEVBLK->ccwstep ) && (!IS_CTCE_MATCH( pDEVBLK->ctceyState ) ) )
{
// "%1d:%04X CTCE: Recv %4.4X<-%s %s=%2.2X x=%2.2X y=%2.2X: WEOF ->set"
WRMSG( HHC05027, "I", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum,
pSokBuf->PktSeq, pDEVBLK->filename,
CTCE_CmdStr[CTCE_Cmd[pDEVBLK->ctceyCmd]], pDEVBLK->ctceyCmd,
pDEVBLK->ctcexState, pDEVBLK->ctceyState ) ;
}
}
// If we were sent this command because the other side was
// not ready yet, then we need to signal a device end and
// reset SENSE[0] bits 1 (SENSE_IR) and 7 (SENSE_OC).
// However, as stated earlier, this NOT READY handling did
// not work and was effectively disabled by initializing
// both adapter sides as being READY from the start.
if( IS_CTCE_NRDY( pDEVBLK->ctceyState ) )
{
pDEVBLK->sense[0] &= ~(SENSE_IR | SENSE_OC);
release_lock( &pDEVBLK->lock );
device_attention( pDEVBLK, CSW_DE );
obtain_lock( &pDEVBLK->lock );
}
// If the other (y-)side sent us a command that put them
// in the Working(D) state asking us to signal attention
// then we copy their state into ours. We also copy the
// y command register for the SCB command to read from.
else if( IS_CTCE_ATTN( pDEVBLK->ctceyState ) )
{
CLR_CTCE_ATTN( pDEVBLK->ctceyState );
pDEVBLK->ctcexState = pDEVBLK->ctceyState;
pDEVBLK->ctceyCmdSCB = pDEVBLK->ctceyCmd;
// Then we signal that attention. The release and
// obtain lock is only needed because device_attention
// also obtains and releases the lock, which under
// Unix causes HHCCP017I eventually. The retry
// attempts were discovered to actually occur as
// sometimes RC=1, about 6 times per hour.
release_lock( &pDEVBLK->lock );
rc = device_attention( pDEVBLK, CSW_ATTN );
// Non-zero RC will be reported and re-tried (except RC=3).
for( i = 1; ( rc != 0 ) && ( i < 10 ); i++ )
{
// "%1d:%04X CTCE: Recv %4.4X<-%s %s=%2.2X x=%2.2X y=%2.2X: ATTN(%d) RC=%d"
WRMSG( HHC05028, "E", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum,
pSokBuf->PktSeq, pDEVBLK->filename,
CTCE_CmdStr[CTCE_Cmd[pDEVBLK->ctceyCmd]], pDEVBLK->ctceyCmd,
pDEVBLK->ctcexState, pDEVBLK->ctceyState, i, rc );
if( rc == 3 )
{
rc = 0;
}
else
{
usleep(1000);
rc = device_attention( pDEVBLK, CSW_DE );
}
}
obtain_lock( &pDEVBLK->lock );
}
// If the other (y-)side sent us a matching command then
// the Working(D) state will cease into YAV state but
// CTCT_Send is awaiting us to signal that condition.
// We clear the SCB command buffer input.
else if( IS_CTCE_MATCH( pDEVBLK->ctceyState ) )
{
CLR_CTCE_MATCH( pDEVBLK->ctceyState );
pDEVBLK->ctceyCmdSCB = 0;
obtain_lock( &pDEVBLK->ctceEventLock );
signal_condition( &pDEVBLK->ctceEvent );
release_lock( &pDEVBLK->ctceEventLock );
}
}
release_lock( &pDEVBLK->lock );
}
UNREACHABLE_CODE();
}