mirror of
https://github.com/SDL-Hercules-390/hyperion.git
synced 2026-04-10 22:24:32 +02:00
1. HHC00922 s/b under control of debug flag, not ccw tracing 2. HHC00950 and HHC00952 are rarely interesting 3. Additional debugging messages HHC00969 and HHC00977 4. Prevent HHC00965 message flood by limiting it to only once every few seconds at most
2905 lines
101 KiB
C
2905 lines
101 KiB
C
/* CTC_LCS.C (c) Copyright James A. Pierson, 2002-2012 */
|
|
/* (c) Copyright "Fish" (David B. Trout), 2002-2011 */
|
|
/* Hercules LAN Channel Station Support */
|
|
/* */
|
|
/* Released under "The Q Public License Version 1" */
|
|
/* (http://www.hercules-390.org/herclic.html) as modifications to */
|
|
/* Hercules. */
|
|
|
|
#include "hstdinc.h"
|
|
|
|
/* jbs 10/27/2007 added _SOLARIS_ */
|
|
#if !defined(__SOLARIS__)
|
|
|
|
#include "hercules.h"
|
|
#include "ctcadpt.h"
|
|
#include "tuntap.h"
|
|
#include "opcode.h"
|
|
#include "herc_getopt.h"
|
|
|
|
//#define ENABLE_TRACING_STMTS 1 // (Fish: DEBUGGING)
|
|
//#include "dbgtrace.h" // (Fish: DEBUGGING)
|
|
//#define NO_LCS_OPTIMIZE // (Fish: DEBUGGING) (MSVC only)
|
|
//#define LCS_TIMING_DEBUG // (Fish: DEBUG speed/timing)
|
|
#define LCS_NO_950_952 // (Fish: DEBUGGING: HHC00950 and HHC00952 are rarely interesting)
|
|
|
|
#if defined( _MSVC_ ) && defined( NO_LCS_OPTIMIZE )
|
|
#pragma optimize( "", off ) // disable optimizations for reliable breakpoints
|
|
#endif
|
|
|
|
#if defined( LCS_TIMING_DEBUG )
|
|
#define PTT_LCS_TIMING_DEBUG PTT
|
|
#else
|
|
#define PTT_LCS_TIMING_DEBUG __noop
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/* CCW Codes 0x03 & 0xC3 are immediate commands */
|
|
|
|
static BYTE CTC_Immed_Commands [256] =
|
|
{
|
|
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
|
|
0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, /* 00 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 10 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 20 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 30 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 50 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 60 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 70 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 80 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 90 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* A0 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* B0 */
|
|
0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, /* C0 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* D0 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* E0 */
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* F0 */
|
|
};
|
|
|
|
// ====================================================================
|
|
// Declarations
|
|
// ====================================================================
|
|
|
|
static void LCS_Startup ( PLCSDEV pLCSDEV, PLCSCMDHDR pCmdFrame );
|
|
static void LCS_Shutdown ( PLCSDEV pLCSDEV, PLCSCMDHDR pCmdFrame );
|
|
static void LCS_StartLan ( PLCSDEV pLCSDEV, PLCSCMDHDR pCmdFrame );
|
|
static void LCS_StopLan ( PLCSDEV pLCSDEV, PLCSCMDHDR pCmdFrame );
|
|
static void LCS_QueryIPAssists( PLCSDEV pLCSDEV, PLCSCMDHDR pCmdFrame );
|
|
static void LCS_LanStats ( PLCSDEV pLCSDEV, PLCSCMDHDR pCmdFrame );
|
|
static void LCS_DefaultCmdProc( PLCSDEV pLCSDEV, PLCSCMDHDR pCmdFrame );
|
|
|
|
static void* LCS_PortThread( void* arg /*PLCSPORT pLCSPORT */ );
|
|
|
|
static int LCS_EnqueueEthFrame( PLCSDEV pLCSDEV, BYTE bPort,
|
|
BYTE* pData, size_t iSize );
|
|
|
|
static int LCS_EnqueueReplyFrame( PLCSDEV pLCSDEV, PLCSCMDHDR pReply,
|
|
size_t iSize );
|
|
|
|
static int BuildOAT( char* pszOATName, PLCSBLK pLCSBLK );
|
|
static char* ReadOAT( char* pszOATName, FILE* fp, char* pszBuff );
|
|
static int ParseArgs( DEVBLK* pDEVBLK, PLCSBLK pLCSBLK,
|
|
int argc, char** argv );
|
|
|
|
// ====================================================================
|
|
// Helper macros
|
|
// ====================================================================
|
|
|
|
#define INIT_REPLY_FRAME( reply, pCmdFrame ) \
|
|
\
|
|
memset( &(reply), 0, sizeof( reply ) ); \
|
|
memcpy( &(reply), (pCmdFrame), sizeof( LCSCMDHDR )); \
|
|
STORE_HW( (reply).bLCSCmdHdr.hwReturnCode, 0x0000 )
|
|
|
|
#define ENQUEUE_REPLY_FRAME( pLCSDEV, reply ) \
|
|
\
|
|
while \
|
|
(1 \
|
|
&& LCS_EnqueueReplyFrame( (pLCSDEV), (PLCSCMDHDR) &(reply), \
|
|
sizeof( reply )) != 0 \
|
|
&& (pLCSDEV)->pLCSBLK->Port[(pLCSDEV)->bPort].fd != -1 \
|
|
&& !(pLCSDEV)->pLCSBLK->Port[(pLCSDEV)->bPort].fCloseInProgress \
|
|
) \
|
|
usleep( CTC_DELAY_USECS )
|
|
|
|
// ====================================================================
|
|
// find_group_device
|
|
// ====================================================================
|
|
|
|
static DEVBLK * find_group_device(DEVGRP *group, U16 devnum)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < group->acount; i++)
|
|
if( group->memdev[i]->devnum == devnum )
|
|
return group->memdev[i];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// ====================================================================
|
|
// LCS_Init
|
|
// ====================================================================
|
|
|
|
int LCS_Init( DEVBLK* pDEVBLK, int argc, char *argv[] )
|
|
{
|
|
PLCSBLK pLCSBLK;
|
|
PLCSDEV pLCSDev;
|
|
int i;
|
|
|
|
struct in_addr addr; // Work area for addresses
|
|
|
|
pDEVBLK->devtype = 0x3088;
|
|
|
|
pDEVBLK->excps = 0;
|
|
|
|
// Return when an existing group has been joined but is still incomplete
|
|
if(!group_device(pDEVBLK, 0) && pDEVBLK->group)
|
|
return 0;
|
|
|
|
// We need to create a group, and as such determine the number of devices
|
|
if(!pDEVBLK->group)
|
|
{
|
|
|
|
// Housekeeping
|
|
pLCSBLK = malloc( sizeof( LCSBLK ) );
|
|
if( !pLCSBLK )
|
|
{
|
|
char buf[40];
|
|
MSGBUF(buf, "malloc(%d)", (int)sizeof(LCSBLK));
|
|
// "%1d:%04X CTC: error in function %s: %s"
|
|
WRMSG( HHC00900, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, buf, strerror(errno) );
|
|
return -1;
|
|
}
|
|
memset( pLCSBLK, 0, sizeof( LCSBLK ) );
|
|
|
|
for( i = 0; i < LCS_MAX_PORTS; i++ )
|
|
{
|
|
memset( &pLCSBLK->Port[i], 0, sizeof ( LCSPORT ) );
|
|
|
|
pLCSBLK->Port[i].bPort = i;
|
|
pLCSBLK->Port[i].pLCSBLK = pLCSBLK;
|
|
|
|
// Initialize locking and event mechanisms
|
|
initialize_lock( &pLCSBLK->Port[i].PortDataLock );
|
|
initialize_lock( &pLCSBLK->Port[i].PortEventLock );
|
|
initialize_condition( &pLCSBLK->Port[i].PortEvent );
|
|
}
|
|
|
|
// Parse configuration file statement
|
|
if( ParseArgs( pDEVBLK, pLCSBLK, argc, (char**)argv ) != 0 )
|
|
{
|
|
free( pLCSBLK );
|
|
pLCSBLK = NULL;
|
|
return -1;
|
|
}
|
|
|
|
if( pLCSBLK->pszOATFilename )
|
|
{
|
|
// If an OAT file was specified, Parse it and build the
|
|
// OAT table.
|
|
if( BuildOAT( pLCSBLK->pszOATFilename, pLCSBLK ) != 0 )
|
|
{
|
|
free( pLCSBLK );
|
|
pLCSBLK = NULL;
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, build an OAT based on the address specified
|
|
// in the config file with an assumption of IP mode.
|
|
pLCSBLK->pDevices = malloc( sizeof( LCSDEV ) );
|
|
|
|
memset( pLCSBLK->pDevices, 0, sizeof( LCSDEV ) );
|
|
|
|
if( pLCSBLK->pszIPAddress )
|
|
{
|
|
pLCSBLK->pDevices->pszIPAddress = strdup( pLCSBLK->pszIPAddress );
|
|
inet_aton( pLCSBLK->pDevices->pszIPAddress, &addr );
|
|
pLCSBLK->pDevices->lIPAddress = addr.s_addr; // (network byte order)
|
|
pLCSBLK->pDevices->bType = LCSDEV_TYPE_NONE;
|
|
}
|
|
else
|
|
pLCSBLK->pDevices->bType = LCSDEV_TYPE_PRIMARY;
|
|
|
|
pLCSBLK->pDevices->sAddr = pDEVBLK->devnum;
|
|
pLCSBLK->pDevices->bMode = LCSDEV_MODE_IP;
|
|
pLCSBLK->pDevices->bPort = 0;
|
|
pLCSBLK->pDevices->pNext = NULL;
|
|
|
|
pLCSBLK->icDevices = 2;
|
|
}
|
|
|
|
// Now we must create the group
|
|
if(!group_device(pDEVBLK, pLCSBLK->icDevices))
|
|
{
|
|
pDEVBLK->group->grp_data = pLCSBLK;
|
|
return 0;
|
|
}
|
|
else
|
|
pDEVBLK->group->grp_data = pLCSBLK;
|
|
|
|
}
|
|
else
|
|
pLCSBLK = pDEVBLK->group->grp_data;
|
|
|
|
// When this code is reached the last devblk has been allocated...
|
|
//
|
|
// Now build the LCSDEV's.
|
|
// If an OAT is specified, the addresses that were specified in the
|
|
// hercules.cnf file must match those that are specified in the OAT.
|
|
|
|
for( pLCSDev = pLCSBLK->pDevices; pLCSDev; pLCSDev = pLCSDev->pNext )
|
|
{
|
|
pLCSDev->pDEVBLK[0] = find_group_device(pDEVBLK->group, pLCSDev->sAddr);
|
|
|
|
if( !pLCSDev->pDEVBLK[0] )
|
|
{
|
|
// "%1d:%04X CTC: lcs device %04X not in configuration"
|
|
WRMSG( HHC00920, "E", SSID_TO_LCSS(pDEVBLK->group->memdev[0]->ssid) ,
|
|
pDEVBLK->group->memdev[0]->devnum, pLCSDev->sAddr );
|
|
return -1;
|
|
}
|
|
|
|
// Establish SENSE ID and Command Information Word data.
|
|
SetSIDInfo( pLCSDev->pDEVBLK[0], 0x3088, 0x60, 0x3088, 0x01 );
|
|
// SetCIWInfo( pLCSDev->pDEVBLK[0], 0, 0, 0x72, 0x0080 );
|
|
// SetCIWInfo( pLCSDev->pDEVBLK[0], 1, 1, 0x83, 0x0004 );
|
|
// SetCIWInfo( pLCSDev->pDEVBLK[0], 2, 2, 0x82, 0x0040 );
|
|
|
|
pLCSDev->pDEVBLK[0]->ctctype = CTC_LCS;
|
|
pLCSDev->pDEVBLK[0]->ctcxmode = 1;
|
|
pLCSDev->pDEVBLK[0]->dev_data = pLCSDev;
|
|
pLCSDev->pLCSBLK = pLCSBLK;
|
|
strcpy( pLCSDev->pDEVBLK[0]->filename, pLCSBLK->pszTUNDevice );
|
|
|
|
// If this is an IP Passthru address, we need a write address
|
|
if( pLCSDev->bMode == LCSDEV_MODE_IP )
|
|
{
|
|
pLCSDev->pDEVBLK[1] = find_group_device(pDEVBLK->group, pLCSDev->sAddr^1);
|
|
|
|
if( !pLCSDev->pDEVBLK[1] )
|
|
{
|
|
// "%1d:%04X CTC: lcs device %04X not in configuration"
|
|
WRMSG( HHC00920, "E", SSID_TO_LCSS(pDEVBLK->group->memdev[0]->ssid),
|
|
pDEVBLK->group->memdev[0]->devnum, pLCSDev->sAddr^1 );
|
|
return -1;
|
|
}
|
|
|
|
// Establish SENSE ID and Command Information Word data.
|
|
SetSIDInfo( pLCSDev->pDEVBLK[1], 0x3088, 0x60, 0x3088, 0x01 );
|
|
// SetCIWInfo( pLCSDev->pDEVBLK[1], 0, 0, 0x72, 0x0080 );
|
|
// SetCIWInfo( pLCSDev->pDEVBLK[1], 1, 1, 0x83, 0x0004 );
|
|
// SetCIWInfo( pLCSDev->pDEVBLK[1], 2, 2, 0x82, 0x0040 );
|
|
|
|
pLCSDev->pDEVBLK[1]->ctctype = CTC_LCS;
|
|
pLCSDev->pDEVBLK[1]->ctcxmode = 1;
|
|
pLCSDev->pDEVBLK[1]->dev_data = pLCSDev;
|
|
|
|
strcpy( pLCSDev->pDEVBLK[1]->filename, pLCSBLK->pszTUNDevice );
|
|
}
|
|
|
|
// Indicate that the DEVBLK(s) have been create sucessfully
|
|
pLCSDev->fDevCreated = 1;
|
|
|
|
// Initialize locking and event mechanisms
|
|
initialize_lock( &pLCSDev->DevDataLock );
|
|
initialize_lock( &pLCSDev->DevEventLock );
|
|
initialize_condition( &pLCSDev->DevEvent );
|
|
|
|
// Create the TAP interface (if not already created by a
|
|
// previous pass. More than one interface can exist on a port.
|
|
if( !pLCSBLK->Port[pLCSDev->bPort].fPortCreated )
|
|
{
|
|
int rc;
|
|
|
|
rc = TUNTAP_CreateInterface( pLCSBLK->pszTUNDevice,
|
|
IFF_TAP | IFF_NO_PI,
|
|
&pLCSBLK->Port[pLCSDev->bPort].fd,
|
|
pLCSBLK->Port[pLCSDev->bPort].szNetIfName );
|
|
|
|
if( rc < 0 ) return -1;
|
|
|
|
// "%1d:%04X %s: interface %s, type %s opened"
|
|
WRMSG( HHC00901, "I", SSID_TO_LCSS(pLCSDev->pDEVBLK[0]->ssid), pLCSDev->pDEVBLK[0]->devnum,
|
|
pLCSDev->pDEVBLK[0]->typname,
|
|
pLCSBLK->Port[pLCSDev->bPort].szNetIfName, "TAP");
|
|
|
|
#if defined(OPTION_W32_CTCI)
|
|
|
|
// Set the specified driver/dll i/o buffer sizes..
|
|
{
|
|
struct tt32ctl tt32ctl;
|
|
|
|
memset( &tt32ctl, 0, sizeof(tt32ctl) );
|
|
strlcpy( tt32ctl.tt32ctl_name, pLCSBLK->Port[pLCSDev->bPort].szNetIfName, sizeof(tt32ctl.tt32ctl_name) );
|
|
|
|
tt32ctl.tt32ctl_devbuffsize = pLCSBLK->iKernBuff;
|
|
|
|
if (TUNTAP_IOCtl( pLCSBLK->Port[pLCSDev->bPort].fd, TT32SDEVBUFF, (char*)&tt32ctl ) != 0)
|
|
{
|
|
// "%1d:%04X %s: ioctl %s failed for device %s: %s"
|
|
WRMSG( HHC00902, "W", SSID_TO_LCSS(pLCSDev->pDEVBLK[0]->ssid), pLCSDev->pDEVBLK[0]->devnum,
|
|
pLCSDev->pDEVBLK[0]->typname,
|
|
"TT32SDEVBUFF", pLCSBLK->Port[pLCSDev->bPort].szNetIfName, strerror( errno ) );
|
|
}
|
|
|
|
tt32ctl.tt32ctl_iobuffsize = pLCSBLK->iIOBuff;
|
|
if( TUNTAP_IOCtl( pLCSBLK->Port[pLCSDev->bPort].fd, TT32SIOBUFF, (char*)&tt32ctl ) != 0 )
|
|
{
|
|
// "%1d:%04X %s: ioctl %s failed for device %s: %s"
|
|
WRMSG( HHC00902, "W", SSID_TO_LCSS(pLCSDev->pDEVBLK[0]->ssid), pLCSDev->pDEVBLK[0]->devnum,
|
|
pLCSDev->pDEVBLK[0]->typname,
|
|
"TT32SIOBUFF", pLCSBLK->Port[pLCSDev->bPort].szNetIfName, strerror( errno ) );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Indicate that the port is used.
|
|
pLCSBLK->Port[pLCSDev->bPort].fUsed = 1;
|
|
pLCSBLK->Port[pLCSDev->bPort].fPortCreated = 1;
|
|
|
|
rc = create_thread( &pLCSBLK->Port[pLCSDev->bPort].tid,
|
|
JOINABLE, LCS_PortThread,
|
|
&pLCSBLK->Port[pLCSDev->bPort],
|
|
"LCS_PortThread" );
|
|
if(rc)
|
|
// "Error in function create_thread(): %s"
|
|
WRMSG( HHC00102, "E", strerror(rc));
|
|
/* Identify the thread ID with the devices on which they are active */
|
|
pLCSDev->pDEVBLK[0]->tid = pLCSBLK->Port[pLCSDev->bPort].tid;
|
|
if (pLCSDev->pDEVBLK[1])
|
|
pLCSDev->pDEVBLK[1]->tid = pLCSBLK->Port[pLCSDev->bPort].tid;
|
|
}
|
|
|
|
// Add these devices to the ports device list.
|
|
pLCSBLK->Port[pLCSDev->bPort].icDevices++;
|
|
pLCSDev->pDEVBLK[0]->fd = pLCSBLK->Port[pLCSDev->bPort].fd;
|
|
|
|
if( pLCSDev->pDEVBLK[1] )
|
|
pLCSDev->pDEVBLK[1]->fd = pLCSBLK->Port[pLCSDev->bPort].fd;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ====================================================================
|
|
// LCS_ExecuteCCW
|
|
// ====================================================================
|
|
|
|
void LCS_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 &&
|
|
!IS_CCW_SENSE( bCode ) &&
|
|
!IS_CCW_CONTROL( bCode ) )
|
|
{
|
|
pDEVBLK->sense[0] = SENSE_IR;
|
|
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
|
|
return;
|
|
}
|
|
|
|
// 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;
|
|
#if 0
|
|
// Special case for LCS CIW's
|
|
else if( ( bCode == 72 || bCode == 82 || bCode == 83 ) )
|
|
bOpCode = bCode;
|
|
#endif
|
|
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;
|
|
}
|
|
|
|
LCS_Write( pDEVBLK, sCount, pIOBuf, pUnitStat, pResidual );
|
|
|
|
break;
|
|
|
|
case 0x81: // 1MMMMM01 WEOF
|
|
//------------------------------------------------------------
|
|
// WRITE EOF
|
|
//------------------------------------------------------------
|
|
|
|
// 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
|
|
LCS_Read( pDEVBLK, sCount, pIOBuf, pUnitStat, pResidual, pMore );
|
|
|
|
break;
|
|
|
|
case 0x07: // MMMMM111 CTL
|
|
// -----------------------------------------------------------
|
|
// CONTROL
|
|
// -----------------------------------------------------------
|
|
|
|
*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
|
|
// -----------------------------------------------------------
|
|
|
|
*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;
|
|
|
|
#if 0
|
|
case 0x72: // 0111010 RCD
|
|
// ------------------------------------------------------------
|
|
// READ CONFIGURATION DATA
|
|
// ------------------------------------------------------------
|
|
|
|
case 0x82: // 10000010 SID
|
|
// ------------------------------------------------------------
|
|
// SET INTERFACE IDENTIFER
|
|
// ------------------------------------------------------------
|
|
|
|
case 0x83: // 10000011 RID
|
|
// ------------------------------------------------------------
|
|
// READ NODE IDENTIFER
|
|
// ------------------------------------------------------------
|
|
|
|
LCS_SDC( pDEVBLK, bOpCode, sCount, pIOBuf,
|
|
pUnitStat, pResidual, pMore );
|
|
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
// ------------------------------------------------------------
|
|
// INVALID OPERATION
|
|
// ------------------------------------------------------------
|
|
|
|
// Set command reject sense byte, and unit check status
|
|
pDEVBLK->sense[0] = SENSE_CR;
|
|
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// ====================================================================
|
|
// LCS_Close
|
|
// ====================================================================
|
|
|
|
int LCS_Close( DEVBLK* pDEVBLK )
|
|
{
|
|
PLCSDEV pLCSDEV;
|
|
PLCSBLK pLCSBLK;
|
|
PLCSPORT pLCSPORT;
|
|
|
|
if (!(pLCSDEV = (PLCSDEV)pDEVBLK->dev_data))
|
|
return 0; // (was incomplete group)
|
|
|
|
pLCSBLK = pLCSDEV->pLCSBLK;
|
|
pLCSPORT = &pLCSBLK->Port[pLCSDEV->bPort];
|
|
|
|
pLCSPORT->icDevices--;
|
|
|
|
// Is this the last device on the port?
|
|
if( !pLCSPORT->icDevices )
|
|
{
|
|
// PROGRAMMING NOTE: there's currently no way to interrupt
|
|
// the "LCS_PortThread"s TUNTAP_Read of the adapter. Thus
|
|
// we must simply wait for LCS_PortThread to eventually
|
|
// notice that we're doing a close (via our setting of the
|
|
// fCloseInProgress flag). Its TUNTAP_Read will eventually
|
|
// timeout after a few seconds (currently 5, which is dif-
|
|
// ferent than the CTC_READ_TIMEOUT_SECS timeout value the
|
|
// CTCI_Read function uses) and will then do the close of
|
|
// the adapter for us (TUNTAP_Close) so we don't have to.
|
|
// All we need to do is ask it to exit (via our setting of
|
|
// the fCloseInProgress flag) and then wait for it to exit
|
|
// (which, as stated, could take up to a max of 5 seconds).
|
|
|
|
// All of this is simply because it's poor form to close a
|
|
// device from one thread while another thread is reading
|
|
// from it. Attempting to do so could trip a race condition
|
|
// wherein the internal i/o buffers used to process the
|
|
// read request could have been freed (by the close call)
|
|
// by the time the read request eventually gets serviced.
|
|
|
|
if( pLCSPORT->fd >= 0 )
|
|
{
|
|
TID tid = pLCSPORT->tid;
|
|
obtain_lock( &pLCSPORT->PortEventLock );
|
|
{
|
|
if (pDEVBLK->ccwtrace || pDEVBLK->ccwstep)
|
|
// "%1d:%04X CTC: lcs triggering port %2.2X event"
|
|
WRMSG( HHC00966, "I", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum, pLCSPORT->bPort );
|
|
|
|
pLCSPORT->fPortStarted = 0;
|
|
pLCSPORT->fCloseInProgress = 1;
|
|
signal_condition( &pLCSPORT->PortEvent );
|
|
}
|
|
release_lock( &pLCSPORT->PortEventLock );
|
|
signal_thread( tid, SIGUSR2 );
|
|
join_thread( tid, NULL );
|
|
detach_thread( tid );
|
|
}
|
|
|
|
if( pLCSDEV->pDEVBLK[0] && pLCSDEV->pDEVBLK[0]->fd >= 0 )
|
|
pLCSDEV->pDEVBLK[0]->fd = -1;
|
|
if( pLCSDEV->pDEVBLK[1] && pLCSDEV->pDEVBLK[1]->fd >= 0 )
|
|
pLCSDEV->pDEVBLK[1]->fd = -1;
|
|
}
|
|
|
|
// Housekeeping
|
|
if( pLCSDEV->pDEVBLK[0] == pDEVBLK )
|
|
pLCSDEV->pDEVBLK[0] = NULL;
|
|
if( pLCSDEV->pDEVBLK[1] == pDEVBLK )
|
|
pLCSDEV->pDEVBLK[1] = NULL;
|
|
|
|
if( !pLCSDEV->pDEVBLK[0] &&
|
|
!pLCSDEV->pDEVBLK[1] )
|
|
{
|
|
// Remove this LCS Device from the chain...
|
|
|
|
PLCSDEV pCurrLCSDev = NULL;
|
|
PLCSDEV* ppPrevLCSDev = &pLCSBLK->pDevices;
|
|
|
|
for( pCurrLCSDev = pLCSBLK->pDevices; pCurrLCSDev; pCurrLCSDev = pCurrLCSDev->pNext )
|
|
{
|
|
if( pCurrLCSDev == pLCSDEV )
|
|
{
|
|
*ppPrevLCSDev = pCurrLCSDev->pNext;
|
|
|
|
if( pCurrLCSDev->pszIPAddress )
|
|
{
|
|
free( pCurrLCSDev->pszIPAddress );
|
|
pCurrLCSDev->pszIPAddress = NULL;
|
|
}
|
|
|
|
free( pLCSDEV );
|
|
pLCSDEV = NULL;
|
|
break;
|
|
}
|
|
|
|
ppPrevLCSDev = &pCurrLCSDev->pNext;
|
|
}
|
|
}
|
|
|
|
if( !pLCSBLK->pDevices )
|
|
{
|
|
if( pLCSBLK->pszTUNDevice ) { free( pLCSBLK->pszTUNDevice ); pLCSBLK->pszTUNDevice = NULL; }
|
|
if( pLCSBLK->pszOATFilename ) { free( pLCSBLK->pszOATFilename ); pLCSBLK->pszOATFilename = NULL; }
|
|
if( pLCSBLK->pszIPAddress ) { free( pLCSBLK->pszIPAddress ); pLCSBLK->pszIPAddress = NULL; }
|
|
// if( pLCSBLK->pszMACAddress ) { free( pLCSBLK->pszMACAddress ); pLCSBLK->pszMACAddress = NULL; }
|
|
|
|
free( pLCSBLK );
|
|
pLCSBLK = NULL;
|
|
}
|
|
|
|
pDEVBLK->dev_data = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ====================================================================
|
|
// LCS_Query
|
|
// ====================================================================
|
|
|
|
void LCS_Query( DEVBLK* pDEVBLK, char** ppszClass,
|
|
int iBufLen, char* pBuffer )
|
|
{
|
|
char *sType[] = { "", " Pri", " Sec" };
|
|
|
|
LCSDEV* pLCSDEV;
|
|
|
|
BEGIN_DEVICE_CLASS_QUERY( "CTCA", pDEVBLK, ppszClass, iBufLen, pBuffer );
|
|
|
|
pLCSDEV = (LCSDEV*) pDEVBLK->dev_data;
|
|
|
|
if(!pLCSDEV)
|
|
{
|
|
strlcpy(pBuffer,"*Uninitialized",iBufLen);
|
|
return;
|
|
}
|
|
|
|
snprintf( pBuffer, iBufLen-1, "LCS Port %2.2X %s%s (%s)%s IO[%" I64_FMT "u]",
|
|
pLCSDEV->bPort,
|
|
pLCSDEV->bMode == LCSDEV_MODE_IP ? "IP" : "SNA",
|
|
sType[pLCSDEV->bType],
|
|
pLCSDEV->pLCSBLK->Port[pLCSDEV->bPort].szNetIfName,
|
|
pLCSDEV->pLCSBLK->fDebug ? " -d" : "",
|
|
( pDEVBLK->devnum & 1 ) == 0 ? pLCSDEV->pDEVBLK[0]->excps : pLCSDEV->pDEVBLK[1]->excps );
|
|
}
|
|
|
|
// ====================================================================
|
|
// LCS_Read
|
|
// ====================================================================
|
|
// The guest o/s is issuing a Read CCW for our LCS device. Return to
|
|
// it all available LCS Frames that we have buffered up in our buffer.
|
|
// --------------------------------------------------------------------
|
|
|
|
void LCS_Read( DEVBLK* pDEVBLK, U32 sCount,
|
|
BYTE* pIOBuf, BYTE* pUnitStat,
|
|
U32* pResidual, BYTE* pMore )
|
|
{
|
|
PLCSHDR pLCSHdr;
|
|
PLCSDEV pLCSDEV = (PLCSDEV)pDEVBLK->dev_data;
|
|
size_t iLength = 0;
|
|
int rc = 0;
|
|
|
|
struct timespec waittime;
|
|
struct timeval now;
|
|
|
|
// FIXME: we currently don't support data-chaining but
|
|
// probably should if real LCS devices do (I was unable
|
|
// to determine whether they do or not). -- Fish
|
|
|
|
for (;;)
|
|
{
|
|
// Wait for some LCS Frames to arrive in our buffer...
|
|
|
|
obtain_lock( &pLCSDEV->DevDataLock );
|
|
|
|
if( !( pLCSDEV->fDataPending || pLCSDEV->fReplyPending ) )
|
|
{
|
|
release_lock( &pLCSDEV->DevDataLock );
|
|
|
|
// Wait 5 seconds then check for channel conditions
|
|
|
|
gettimeofday( &now, NULL );
|
|
|
|
waittime.tv_sec = now.tv_sec + CTC_READ_TIMEOUT_SECS;
|
|
waittime.tv_nsec = now.tv_usec * 1000;
|
|
|
|
obtain_lock( &pLCSDEV->DevEventLock );
|
|
|
|
rc = timed_wait_condition( &pLCSDEV->DevEvent,
|
|
&pLCSDEV->DevEventLock,
|
|
&waittime );
|
|
|
|
release_lock( &pLCSDEV->DevEventLock );
|
|
|
|
// If we didn't receive any, keep waiting...
|
|
|
|
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 CTC: halt or clear recognized"
|
|
WRMSG( HHC00904, "I", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum );
|
|
|
|
*pUnitStat = CSW_CE | CSW_DE;
|
|
*pResidual = sCount;
|
|
return;
|
|
}
|
|
continue; // (keep waiting)
|
|
}
|
|
|
|
// We received some LCS Frames...
|
|
|
|
obtain_lock( &pLCSDEV->DevDataLock );
|
|
}
|
|
|
|
// Point to the end of all buffered LCS Frames...
|
|
// (where the next Frame *would* go if there was one)
|
|
|
|
pLCSHdr = (PLCSHDR)( pLCSDEV->bFrameBuffer +
|
|
pLCSDEV->iFrameOffset );
|
|
|
|
// Mark the end of this batch of LCS Frames by setting
|
|
// the "offset to NEXT frame" LCS Header field to zero.
|
|
// (a zero "next Frame offset" is like an "EOF" flag)
|
|
|
|
STORE_HW( pLCSHdr->hwOffset, 0x0000 );
|
|
|
|
// Calculate how much data we're going to be giving them.
|
|
|
|
// Since 'iFrameOffset' points to the next available LCS
|
|
// Frame slot in our buffer, the total amount of LCS Frame
|
|
// data we have is exactly that amount. We give them two
|
|
// extra bytes however so that they can optionally chase
|
|
// the "hwOffset" field in each LCS Frame's LCS Header to
|
|
// eventually reach our zero hwOffset "EOF" flag).
|
|
|
|
iLength = pLCSDEV->iFrameOffset + sizeof(pLCSHdr->hwOffset);
|
|
|
|
// (calculate residual and set memcpy amount)
|
|
|
|
// FIXME: we currently don't support data-chaining but
|
|
// probably should if real LCS devices do (I was unable
|
|
// to determine whether they do or not). -- Fish
|
|
|
|
if( sCount < iLength )
|
|
{
|
|
*pMore = 1;
|
|
*pResidual = 0;
|
|
|
|
iLength = sCount;
|
|
|
|
// PROGRAMMING NOTE: As a result of the caller asking
|
|
// for less data than we actually have available, the
|
|
// remainder of their unread data they didn't ask for
|
|
// will end up being silently discarded. Refer to the
|
|
// other NOTEs and FIXME's sprinkled throughout this
|
|
// function...
|
|
}
|
|
else
|
|
{
|
|
*pMore = 0;
|
|
*pResidual -= iLength;
|
|
}
|
|
|
|
*pUnitStat = CSW_CE | CSW_DE;
|
|
|
|
memcpy( pIOBuf, pLCSDEV->bFrameBuffer, iLength );
|
|
|
|
// Trace the i/o if requested...
|
|
|
|
if( pDEVBLK->ccwtrace || pDEVBLK->ccwstep )
|
|
{
|
|
// "%1d:%04X CTC: lcs read"
|
|
WRMSG( HHC00921, "I", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum );
|
|
packet_trace( pIOBuf, (int)iLength, '<' );
|
|
}
|
|
|
|
// Reset frame buffer to empty...
|
|
|
|
// PROGRAMMING NOTE: even though not all available data
|
|
// may have been read by the guest, we don't currently
|
|
// support data-chaining. Thus any unread data is always
|
|
// discarded by resetting both of the iFrameOffset and
|
|
// fDataPending fields to 0 so that the next read always
|
|
// grabs a new batch of LCS Frames starting at the very
|
|
// beginning of our frame buffer again. (I was unable
|
|
// to determine whether real LCS devices support data-
|
|
// chaining or not, but if they do we should fix this).
|
|
|
|
pLCSDEV->iFrameOffset = 0;
|
|
pLCSDEV->fReplyPending = 0;
|
|
pLCSDEV->fDataPending = 0;
|
|
|
|
release_lock( &pLCSDEV->DevDataLock );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ====================================================================
|
|
// LCS Multi-Write Support
|
|
// ====================================================================
|
|
|
|
#if defined( OPTION_W32_CTCI )
|
|
|
|
static void LCS_BegMWrite( DEVBLK* pDEVBLK )
|
|
{
|
|
if (((LCSDEV*)pDEVBLK->dev_data)->pLCSBLK->fNoMultiWrite) return;
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "b4 begmw", 0, 0, 0 );
|
|
TUNTAP_BegMWrite( pDEVBLK->fd, CTC_FRAME_BUFFER_SIZE );
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "af begmw", 0, 0, 0);
|
|
}
|
|
|
|
static void LCS_EndMWrite( DEVBLK* pDEVBLK, int nEthBytes, int nEthFrames )
|
|
{
|
|
if (((LCSDEV*)pDEVBLK->dev_data)->pLCSBLK->fNoMultiWrite) return;
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "b4 endmw", 0, nEthBytes, nEthFrames );
|
|
TUNTAP_EndMWrite( pDEVBLK->fd );
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "af endmw", 0, nEthBytes, nEthFrames );
|
|
}
|
|
|
|
#else // !defined( OPTION_W32_CTCI )
|
|
|
|
#define LCS_BegMWrite( pDEVBLK )
|
|
#define LCS_EndMWrite( pDEVBLK, nEthBytes, nEthFrames )
|
|
|
|
#endif // defined( OPTION_W32_CTCI )
|
|
|
|
// ====================================================================
|
|
// LCS_Write
|
|
// ====================================================================
|
|
// The guest o/s is issuing a Write CCW for our LCS device. All LCS
|
|
// Frames in its buffer which are NOT internal Command Frames will
|
|
// be immediately written to our host's adapter (via TunTap). Frames
|
|
// that are internal Command Frames however are processed internally
|
|
// and cause a "reply" frame to be enqueued to the LCS Device output
|
|
// buffer to be eventually returned back to the guest the next time
|
|
// it issues a Read CCW.
|
|
// --------------------------------------------------------------------
|
|
|
|
void LCS_Write( DEVBLK* pDEVBLK, U32 sCount,
|
|
BYTE* pIOBuf, BYTE* pUnitStat,
|
|
U32* pResidual )
|
|
{
|
|
PLCSDEV pLCSDEV = (PLCSDEV)pDEVBLK->dev_data;
|
|
PLCSHDR pLCSHDR = NULL;
|
|
PLCSCMDHDR pCmdFrame = NULL;
|
|
PLCSETHFRM pLCSEthFrame = NULL;
|
|
PETHFRM pEthFrame = NULL;
|
|
U16 iOffset = 0;
|
|
U16 iPrevOffset = 0;
|
|
U16 iLength = 0;
|
|
U16 iEthLen = 0;
|
|
int nEthFrames = 0;
|
|
int nEthBytes = 0;
|
|
char buf[32];
|
|
|
|
UNREFERENCED( sCount );
|
|
|
|
// Process each frame in the buffer...
|
|
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "beg write", 0, 0, 0 );
|
|
LCS_BegMWrite( pDEVBLK ); // (performance)
|
|
|
|
while( 1 )
|
|
{
|
|
// Fix-up the LCS header pointer to the current frame
|
|
pLCSHDR = (PLCSHDR)( pIOBuf + iOffset );
|
|
|
|
// Save current offset so we can tell how big next frame is
|
|
iPrevOffset = iOffset;
|
|
|
|
// Get the next frame offset, exit loop if 0
|
|
FETCH_HW( iOffset, pLCSHDR->hwOffset );
|
|
|
|
if( iOffset == 0 ) // ("EOF")
|
|
break;
|
|
|
|
// Calculate size of this LCS Frame
|
|
iLength = iOffset - iPrevOffset;
|
|
|
|
switch( pLCSHDR->bType )
|
|
{
|
|
case LCS_FRMTYP_CMD: // LCS Command Frame
|
|
|
|
pCmdFrame = (PLCSCMDHDR)pLCSHDR;
|
|
|
|
// Trace received command frame...
|
|
if( pLCSDEV->pLCSBLK->fDebug )
|
|
{
|
|
// "%1d:%04X CTC: lcs command packet received"
|
|
WRMSG( HHC00922, "D", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum );
|
|
packet_trace( (BYTE*) pCmdFrame, iLength, '>' );
|
|
}
|
|
|
|
// FIXME: what is this all about? I'm not saying it's wrong,
|
|
// only that we need to document via comments the purpose of
|
|
// this test. What's it doing? Why ignore "initiator 1"? etc.
|
|
// PLEASE EXPLAIN! -- Fish
|
|
if (pCmdFrame->bInitiator == 0x01)
|
|
{
|
|
if (pLCSDEV->pLCSBLK->fDebug)
|
|
// "%1d:%04X CTC: lcs command packet IGNORED (bInitiator == 0x01)"
|
|
WRMSG( HHC00977, "D", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum );
|
|
break;
|
|
}
|
|
|
|
switch( pCmdFrame->bCmdCode )
|
|
{
|
|
// HHC00933 = "%1d:%04X CTC: executing command %s"
|
|
|
|
case LCS_CMD_STARTUP: // Start Host
|
|
if( pLCSDEV->pLCSBLK->fDebug )
|
|
WRMSG(HHC00933, "D", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "startup");
|
|
LCS_Startup( pLCSDEV, pCmdFrame );
|
|
break;
|
|
|
|
case LCS_CMD_SHUTDOWN: // Shutdown Host
|
|
if( pLCSDEV->pLCSBLK->fDebug )
|
|
WRMSG(HHC00933, "D", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "shutdown");
|
|
LCS_Shutdown( pLCSDEV, pCmdFrame );
|
|
break;
|
|
|
|
case LCS_CMD_STRTLAN: // Start LAN
|
|
if( pLCSDEV->pLCSBLK->fDebug )
|
|
WRMSG(HHC00933, "D", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "start lan");
|
|
LCS_StartLan( pLCSDEV, pCmdFrame );
|
|
break;
|
|
|
|
case LCS_CMD_STOPLAN: // Stop LAN
|
|
if( pLCSDEV->pLCSBLK->fDebug )
|
|
WRMSG(HHC00933, "D", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "stop lan");
|
|
LCS_StopLan( pLCSDEV, pCmdFrame );
|
|
break;
|
|
|
|
case LCS_CMD_QIPASSIST: // Query IP Assists
|
|
if( pLCSDEV->pLCSBLK->fDebug )
|
|
WRMSG(HHC00933, "D", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "query IP assist");
|
|
LCS_QueryIPAssists( pLCSDEV, pCmdFrame );
|
|
break;
|
|
|
|
case LCS_CMD_LANSTAT: // LAN Stats
|
|
if( pLCSDEV->pLCSBLK->fDebug )
|
|
WRMSG(HHC00933, "D", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, "lan statistics");
|
|
LCS_LanStats( pLCSDEV, pCmdFrame );
|
|
break;
|
|
|
|
// ZZ FIXME: Once multicasting support is confirmed in tuntap
|
|
// and/or TunTap32, we need to add support in Herc by handling
|
|
// the below LCS_CMD_SETIPM and LCS_CMD_DELIPM frames and then
|
|
// issuing an ioctl( SIOCADDMULTI ) to tuntap/TunTap32...
|
|
|
|
case LCS_CMD_SETIPM: // Set IP Multicast
|
|
case LCS_CMD_DELIPM: // Delete IP Multicast
|
|
case LCS_CMD_GENSTAT: // General Stats
|
|
case LCS_CMD_LISTLAN: // List LAN
|
|
case LCS_CMD_LISTLAN2: // List LAN (another version)
|
|
case LCS_CMD_TIMING: // Timing request
|
|
default:
|
|
if (pLCSDEV->pLCSBLK->fDebug)
|
|
{
|
|
// "%1d:%04X CTC: executing command %s"
|
|
MSGBUF( buf, "other (0x%2.2X)", pCmdFrame->bCmdCode );
|
|
WRMSG( HHC00933, "D", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, buf );
|
|
}
|
|
LCS_DefaultCmdProc( pLCSDEV, pCmdFrame );
|
|
break;
|
|
|
|
} // end switch( LCS Command Frame cmd code )
|
|
break; // end case LCS_FRMTYP_CMD
|
|
|
|
case LCS_FRMTYP_ENET: // Ethernet Passthru
|
|
case LCS_FRMTYP_TR: // Token Ring
|
|
case LCS_FRMTYP_FDDI: // FDDI
|
|
case LCS_FRMTYP_AUTO: // auto-detect
|
|
|
|
pLCSEthFrame = (PLCSETHFRM)pLCSHDR;
|
|
pEthFrame = (PETHFRM)pLCSEthFrame->bData;
|
|
iEthLen = iLength - sizeof(LCSETHFRM);
|
|
|
|
// Trace Ethernet frame before sending to TAP device
|
|
if( pDEVBLK->ccwtrace || pDEVBLK->ccwstep )
|
|
{
|
|
// "%1d:%04X CTC: sending packet to file %s"
|
|
WRMSG( HHC00934, "I", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pDEVBLK->filename );
|
|
packet_trace( (BYTE*)pEthFrame, iEthLen, '>' );
|
|
}
|
|
|
|
// Write the Ethernet frame to the TAP device
|
|
nEthBytes += iEthLen;
|
|
nEthFrames++;
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "b4 write", 0, iEthLen, 1 );
|
|
if( TUNTAP_Write( pDEVBLK->fd,
|
|
(BYTE*)pEthFrame, iEthLen ) != iEthLen )
|
|
{
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "*WRITE ERR", 0, iEthLen, 1 );
|
|
// "%1d:%04X CTC: error writing to file %s: %s"
|
|
WRMSG( HHC00936, "E",
|
|
SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pDEVBLK->filename,
|
|
strerror( errno ) );
|
|
pDEVBLK->sense[0] = SENSE_EC;
|
|
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
|
|
LCS_EndMWrite( pDEVBLK, nEthBytes, nEthFrames );
|
|
return;
|
|
}
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "af write", 0, iEthLen, 1 );
|
|
break;
|
|
|
|
default:
|
|
// "%1d:%04X CTC: lcs write: unsupported frame type 0x%2.2X"
|
|
WRMSG( HHC00937, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pLCSHDR->bType );
|
|
ASSERT( FALSE );
|
|
pDEVBLK->sense[0] = SENSE_EC;
|
|
*pUnitStat = CSW_CE | CSW_DE | CSW_UC;
|
|
LCS_EndMWrite( pDEVBLK, nEthBytes, nEthFrames );
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "end write", 0, 0, 0 );
|
|
return;
|
|
|
|
} // end switch( LCS Frame type )
|
|
|
|
} // end while (1)
|
|
|
|
LCS_EndMWrite( pDEVBLK, nEthBytes, nEthFrames ); // (performance)
|
|
|
|
*pResidual = 0;
|
|
*pUnitStat = CSW_CE | CSW_DE;
|
|
|
|
if( pLCSDEV->fReplyPending )
|
|
{
|
|
if( pDEVBLK->ccwtrace || pDEVBLK->ccwstep )
|
|
// "%1d:%04X CTC: lcs triggering device event"
|
|
WRMSG( HHC00938, "I", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum );
|
|
|
|
obtain_lock( &pLCSDEV->DevEventLock );
|
|
signal_condition( &pLCSDEV->DevEvent );
|
|
release_lock( &pLCSDEV->DevEventLock );
|
|
}
|
|
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "end write", 0, 0, 0 );
|
|
}
|
|
|
|
#if 0
|
|
// ====================================================================
|
|
// LCS_SDC
|
|
// ====================================================================
|
|
|
|
void LCS_SDC( DEVBLK* pDEVBLK, BYTE bOpCode,
|
|
U32 sCount, BYTE* pIOBuf,
|
|
BYTE* UnitStat, U32* pResidual,
|
|
BYTE* pMore )
|
|
{
|
|
PLCSDEV pLCSDEV = (PLCSDEV)pDEVBLK->dev_data;
|
|
PLCSBLK pLCSBLK = pLCSDEV->pLCSBLK;
|
|
|
|
switch( bOpCode )
|
|
{
|
|
case 0x72: // 0111010 RCD
|
|
// ------------------------------------------------------------
|
|
// READ CONFIGURATION DATA
|
|
// ------------------------------------------------------------
|
|
|
|
SDC_CreateNED( pIOBuf, 0,
|
|
NED_EMULATION,
|
|
NED_TYPE_DEV,
|
|
NED_CLASS_CTCA,
|
|
0,
|
|
"003088", "001",
|
|
"", "", "", 0 );
|
|
|
|
SDC_CreateNED( pIOBuf, 1,
|
|
NED_SERIAL_VALID,
|
|
NED_TYPE_DEV,
|
|
NED_CLASS_UNSPECIFIED,
|
|
0,
|
|
"003172", "000",
|
|
"HDG", "00",
|
|
pLCSBLK->szSerialNumber,
|
|
pLCSDEV->bPort );
|
|
|
|
SDC_CreateGeneralNEQ( pIOBuf, 2,
|
|
0, // Interface ID
|
|
60, // Timeout
|
|
NULL ); // Extended Info
|
|
|
|
SDC_CreateNED( pIOBuf, 3,
|
|
NED_TOKEN | NED_SERIAL_UNIQUE,
|
|
NED_TYPE_DEV,
|
|
NED_CLASS_UNSPECIFIED,
|
|
0,
|
|
"003172", "000",
|
|
"HDG", "00",
|
|
pLCSBLK->szSerialNumber,
|
|
0 );
|
|
break;
|
|
|
|
case 0x82: // 10000010 SID
|
|
// ------------------------------------------------------------
|
|
// SET INTERFACE IDENTIFER
|
|
// ------------------------------------------------------------
|
|
break;
|
|
|
|
case 0x83: // 10000011 RID
|
|
// ------------------------------------------------------------
|
|
// READ NODE IDENTIFER
|
|
// ------------------------------------------------------------
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// ====================================================================
|
|
// LCS_Startup
|
|
// ====================================================================
|
|
|
|
static void LCS_Startup( PLCSDEV pLCSDEV, PLCSCMDHDR pCmdFrame )
|
|
{
|
|
LCSSTRTFRM reply;
|
|
PLCSPORT pLCSPORT;
|
|
U16 iOrigMaxFrameBufferSize;
|
|
|
|
INIT_REPLY_FRAME( reply, pCmdFrame );
|
|
|
|
reply.bLCSCmdHdr.bLanType = LCS_FRMTYP_ENET;
|
|
reply.bLCSCmdHdr.bRelAdapterNo = pLCSDEV->bPort;
|
|
|
|
// Save the max buffer size parameter
|
|
iOrigMaxFrameBufferSize = pLCSDEV->iMaxFrameBufferSize;
|
|
FETCH_HW( pLCSDEV->iMaxFrameBufferSize, ((PLCSSTRTFRM)pCmdFrame)->hwBufferSize );
|
|
|
|
// Make sure it doesn't exceed our compiled maximum
|
|
if (pLCSDEV->iMaxFrameBufferSize > sizeof(pLCSDEV->bFrameBuffer))
|
|
{
|
|
// "%1d:%04X CTC: lcs startup: frame buffer size 0x%4.4X '%s' compiled size 0x%4.4X: ignored"
|
|
WRMSG(HHC00939, "W", SSID_TO_LCSS(pLCSDEV->pDEVBLK[1]->ssid),
|
|
pLCSDEV->pDEVBLK[1]->devnum,
|
|
pLCSDEV->iMaxFrameBufferSize, "LCS",
|
|
(int)sizeof( pLCSDEV->bFrameBuffer ) );
|
|
pLCSDEV->iMaxFrameBufferSize = iOrigMaxFrameBufferSize;
|
|
}
|
|
|
|
// Make sure it's not smaller than the compiled minimum size
|
|
if (pLCSDEV->iMaxFrameBufferSize < CTC_MIN_FRAME_BUFFER_SIZE)
|
|
{
|
|
// "%1d:%04X CTC: lcs startup: frame buffer size 0x%4.4X '%s' compiled size 0x%4.4X: ignored"
|
|
WRMSG(HHC00939, "W", SSID_TO_LCSS(pLCSDEV->pDEVBLK[1]->ssid),
|
|
pLCSDEV->pDEVBLK[1]->devnum,
|
|
pLCSDEV->iMaxFrameBufferSize, "LCS",
|
|
CTC_MIN_FRAME_BUFFER_SIZE );
|
|
pLCSDEV->iMaxFrameBufferSize = iOrigMaxFrameBufferSize;
|
|
}
|
|
|
|
pLCSPORT = &pLCSDEV->pLCSBLK->Port[pLCSDEV->bPort];
|
|
|
|
if (!pLCSPORT->fPreconfigured)
|
|
{
|
|
VERIFY( TUNTAP_SetIPAddr( pLCSPORT->szNetIfName, "0.0.0.0" ) == 0 );
|
|
VERIFY( TUNTAP_SetMTU ( pLCSPORT->szNetIfName, "1500" ) == 0 );
|
|
}
|
|
|
|
#ifdef OPTION_TUNTAP_SETMACADDR
|
|
if (!pLCSPORT->fPreconfigured)
|
|
{
|
|
if (pLCSPORT->fLocalMAC)
|
|
{
|
|
VERIFY( TUNTAP_SetMACAddr( pLCSPORT->szNetIfName,
|
|
pLCSPORT->szMACAddress ) == 0 );
|
|
}
|
|
}
|
|
#endif // OPTION_TUNTAP_SETMACADDR
|
|
|
|
ENQUEUE_REPLY_FRAME( pLCSDEV, reply );
|
|
|
|
pLCSDEV->fDevStarted = 1;
|
|
}
|
|
|
|
// ====================================================================
|
|
// LCS_Shutdown
|
|
// ====================================================================
|
|
|
|
static void LCS_Shutdown( PLCSDEV pLCSDEV, PLCSCMDHDR pCmdFrame )
|
|
{
|
|
LCSSTDFRM reply;
|
|
|
|
INIT_REPLY_FRAME( reply, pCmdFrame );
|
|
|
|
reply.bLCSCmdHdr.bLanType = LCS_FRMTYP_ENET;
|
|
reply.bLCSCmdHdr.bRelAdapterNo = pLCSDEV->bPort;
|
|
|
|
ENQUEUE_REPLY_FRAME( pLCSDEV, reply );
|
|
|
|
pLCSDEV->fDevStarted = 0;
|
|
}
|
|
|
|
// ====================================================================
|
|
// LCS_StartLan
|
|
// ====================================================================
|
|
|
|
static void LCS_StartLan( PLCSDEV pLCSDEV, PLCSCMDHDR pCmdFrame )
|
|
{
|
|
LCSSTDFRM reply;
|
|
PLCSPORT pLCSPORT;
|
|
#ifdef OPTION_TUNTAP_DELADD_ROUTES
|
|
PLCSRTE pLCSRTE;
|
|
#endif // OPTION_TUNTAP_DELADD_ROUTES
|
|
int nIFFlags;
|
|
DEVBLK* pDEVBLK = pLCSDEV->pDEVBLK[ LCSDEV_WRITE_SUBCHANN ];
|
|
|
|
INIT_REPLY_FRAME( reply, pCmdFrame );
|
|
|
|
pLCSPORT = &pLCSDEV->pLCSBLK->Port[pLCSDEV->bPort];
|
|
|
|
// Serialize access to eliminate ioctl errors
|
|
obtain_lock( &pLCSPORT->PortDataLock );
|
|
|
|
// Configure the TAP interface if used
|
|
if( pLCSPORT->fUsed && pLCSPORT->fPortCreated && !pLCSPORT->fPortStarted )
|
|
{
|
|
nIFFlags = // Interface flags
|
|
0
|
|
| IFF_UP // (interface is being enabled)
|
|
| IFF_BROADCAST // (interface broadcast addr is valid)
|
|
;
|
|
|
|
#if defined( TUNTAP_IFF_RUNNING_NEEDED )
|
|
|
|
nIFFlags |= // ADDITIONAL Interface flags
|
|
0
|
|
| IFF_RUNNING // (interface is ALSO operational)
|
|
;
|
|
|
|
#endif /* defined( TUNTAP_IFF_RUNNING_NEEDED ) */
|
|
|
|
// Enable the interface by turning on the IFF_UP flag...
|
|
if (!pLCSPORT->fPreconfigured)
|
|
{
|
|
VERIFY( TUNTAP_SetFlags( pLCSPORT->szNetIfName, nIFFlags ) == 0 );
|
|
}
|
|
|
|
#ifdef OPTION_TUNTAP_DELADD_ROUTES
|
|
|
|
// Add any needed extra routing entries the
|
|
// user may have specified in their OAT file
|
|
// to the host's routing table...
|
|
|
|
if (!pLCSPORT->fPreconfigured)
|
|
{
|
|
for( pLCSRTE = pLCSPORT->pRoutes; pLCSRTE; pLCSRTE = pLCSRTE->pNext )
|
|
{
|
|
VERIFY( TUNTAP_AddRoute( pLCSPORT->szNetIfName,
|
|
pLCSRTE->pszNetAddr,
|
|
pLCSRTE->pszNetMask,
|
|
NULL,
|
|
RTF_UP ) == 0 );
|
|
}
|
|
}
|
|
#endif // OPTION_TUNTAP_DELADD_ROUTES
|
|
|
|
if (pDEVBLK->ccwtrace || pDEVBLK->ccwstep)
|
|
// "%1d:%04X CTC: lcs triggering port %2.2X event"
|
|
WRMSG( HHC00966, "I", SSID_TO_LCSS( pDEVBLK->ssid ), pDEVBLK->devnum, pLCSPORT->bPort );
|
|
|
|
obtain_lock( &pLCSPORT->PortEventLock );
|
|
pLCSPORT->fPortStarted = 1;
|
|
signal_condition( &pLCSPORT->PortEvent );
|
|
release_lock( &pLCSPORT->PortEventLock );
|
|
usleep( 250*1000 );
|
|
}
|
|
|
|
release_lock( &pLCSPORT->PortDataLock );
|
|
|
|
#ifdef OPTION_TUNTAP_DELADD_ROUTES
|
|
|
|
// Add a Point-To-Point routing entry to the
|
|
// host's routing table for our interface...
|
|
|
|
if (!pLCSPORT->fPreconfigured)
|
|
{
|
|
if( pLCSDEV->pszIPAddress )
|
|
{
|
|
VERIFY( TUNTAP_AddRoute( pLCSPORT->szNetIfName,
|
|
pLCSDEV->pszIPAddress,
|
|
"255.255.255.255",
|
|
NULL,
|
|
RTF_UP | RTF_HOST ) == 0 );
|
|
}
|
|
}
|
|
#endif // OPTION_TUNTAP_DELADD_ROUTES
|
|
|
|
ENQUEUE_REPLY_FRAME( pLCSDEV, reply );
|
|
}
|
|
|
|
// ====================================================================
|
|
// LCS_StopLan
|
|
// ====================================================================
|
|
|
|
static void LCS_StopLan( PLCSDEV pLCSDEV, PLCSCMDHDR pCmdFrame )
|
|
{
|
|
LCSSTDFRM reply;
|
|
PLCSPORT pLCSPORT;
|
|
#ifdef OPTION_TUNTAP_DELADD_ROUTES
|
|
PLCSRTE pLCSRTE;
|
|
#endif // OPTION_TUNTAP_DELADD_ROUTES
|
|
DEVBLK* pDEVBLK;
|
|
|
|
INIT_REPLY_FRAME( reply, pCmdFrame );
|
|
|
|
pLCSPORT = &pLCSDEV->pLCSBLK->Port[pLCSDEV->bPort];
|
|
pDEVBLK = pLCSDEV->pDEVBLK[ LCSDEV_WRITE_SUBCHANN ];
|
|
|
|
// Serialize access to eliminate ioctl errors
|
|
obtain_lock( &pLCSPORT->PortDataLock );
|
|
|
|
if( pDEVBLK->ccwtrace || pDEVBLK->ccwstep )
|
|
// "%1d:%04X CTC: lcs triggering port %2.2X event"
|
|
WRMSG( HHC00966, "I", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pLCSPORT->bPort );
|
|
|
|
obtain_lock( &pLCSPORT->PortEventLock );
|
|
pLCSPORT->fPortStarted = 0;
|
|
signal_condition( &pLCSPORT->PortEvent );
|
|
release_lock( &pLCSPORT->PortEventLock );
|
|
usleep( 250*1000 );
|
|
|
|
// Disable the interface by turning off the IFF_UP flag...
|
|
if (!pLCSPORT->fPreconfigured)
|
|
{
|
|
VERIFY( TUNTAP_SetFlags( pLCSPORT->szNetIfName, 0 ) == 0 );
|
|
}
|
|
|
|
#ifdef OPTION_TUNTAP_DELADD_ROUTES
|
|
|
|
// Remove routing entries from host's routing table...
|
|
|
|
// First, remove the Point-To-Point routing entry
|
|
// we added when we brought the interface IFF_UP...
|
|
|
|
if (!pLCSPORT->fPreconfigured)
|
|
{
|
|
if( pLCSDEV->pszIPAddress )
|
|
{
|
|
VERIFY( TUNTAP_DelRoute( pLCSPORT->szNetIfName,
|
|
pLCSDEV->pszIPAddress,
|
|
"255.255.255.255",
|
|
NULL,
|
|
RTF_HOST ) == 0 );
|
|
}
|
|
}
|
|
|
|
// Next, remove any extra routing entries
|
|
// (specified by the user in their OAT file)
|
|
// that we may have also added...
|
|
|
|
if (!pLCSPORT->fPreconfigured)
|
|
{
|
|
for( pLCSRTE = pLCSPORT->pRoutes; pLCSRTE; pLCSRTE = pLCSRTE->pNext )
|
|
{
|
|
VERIFY( TUNTAP_DelRoute( pLCSPORT->szNetIfName,
|
|
pLCSRTE->pszNetAddr,
|
|
pLCSRTE->pszNetMask,
|
|
NULL,
|
|
RTF_UP ) == 0 );
|
|
}
|
|
}
|
|
#endif // OPTION_TUNTAP_DELADD_ROUTES
|
|
|
|
release_lock( &pLCSPORT->PortDataLock );
|
|
|
|
// FIXME: Really need to iterate through the devices and close
|
|
// the TAP interface if all devices have been stopped.
|
|
|
|
ENQUEUE_REPLY_FRAME( pLCSDEV, reply );
|
|
}
|
|
|
|
// ====================================================================
|
|
// LCS_QueryIPAssists
|
|
// ====================================================================
|
|
|
|
static void LCS_QueryIPAssists( PLCSDEV pLCSDEV, PLCSCMDHDR pCmdFrame )
|
|
{
|
|
LCSQIPFRM reply;
|
|
PLCSPORT pLCSPORT;
|
|
|
|
INIT_REPLY_FRAME( reply, pCmdFrame );
|
|
|
|
pLCSPORT = &pLCSDEV->pLCSBLK->Port[pLCSDEV->bPort];
|
|
|
|
#if defined( WIN32 )
|
|
|
|
// FIXME: TunTap32 *does* support TCP/IP checksum offloading
|
|
// (for both inbound and outbound packets), but Microsoft's
|
|
// latest NDIS 6.0 release has broken it, so until I can get
|
|
// it straightened out we can't support it. Sorry! -- Fish
|
|
|
|
// The other assists however, TunTap32 does not yet support.
|
|
|
|
pLCSPORT->sIPAssistsSupported =
|
|
0
|
|
// | LCS_INBOUND_CHECKSUM_SUPPORT
|
|
// | LCS_OUTBOUND_CHECKSUM_SUPPORT
|
|
// | LCS_ARP_PROCESSING
|
|
// | LCS_IP_FRAG_REASSEMBLY
|
|
// | LCS_IP_FILTERING
|
|
// | LCS_IP_V6_SUPPORT
|
|
// | LCS_MULTICAST_SUPPORT
|
|
;
|
|
|
|
pLCSPORT->sIPAssistsEnabled =
|
|
0
|
|
// | LCS_INBOUND_CHECKSUM_SUPPORT
|
|
// | LCS_OUTBOUND_CHECKSUM_SUPPORT
|
|
// | LCS_ARP_PROCESSING
|
|
// | LCS_IP_FRAG_REASSEMBLY
|
|
// | LCS_IP_FILTERING
|
|
// | LCS_IP_V6_SUPPORT
|
|
// | LCS_MULTICAST_SUPPORT
|
|
;
|
|
|
|
#else // !WIN32 (Linux, Apple, etc)
|
|
|
|
// Linux/Apple/etc 'tuntap' driver DOES support
|
|
// certain types of assists?? (task offloading)
|
|
|
|
pLCSPORT->sIPAssistsSupported =
|
|
0
|
|
// | LCS_INBOUND_CHECKSUM_SUPPORT
|
|
// | LCS_OUTBOUND_CHECKSUM_SUPPORT
|
|
// | LCS_ARP_PROCESSING
|
|
| LCS_IP_FRAG_REASSEMBLY
|
|
// | LCS_IP_FILTERING
|
|
// | LCS_IP_V6_SUPPORT
|
|
| LCS_MULTICAST_SUPPORT
|
|
;
|
|
|
|
pLCSPORT->sIPAssistsEnabled =
|
|
0
|
|
// | LCS_INBOUND_CHECKSUM_SUPPORT
|
|
// | LCS_OUTBOUND_CHECKSUM_SUPPORT
|
|
// | LCS_ARP_PROCESSING
|
|
| LCS_IP_FRAG_REASSEMBLY
|
|
// | LCS_IP_FILTERING
|
|
// | LCS_IP_V6_SUPPORT
|
|
| LCS_MULTICAST_SUPPORT
|
|
;
|
|
|
|
#endif // WIN32
|
|
|
|
STORE_HW( reply.hwNumIPPairs, 0x0000 );
|
|
STORE_HW( reply.hwIPAssistsSupported, pLCSPORT->sIPAssistsSupported );
|
|
STORE_HW( reply.hwIPAssistsEnabled, pLCSPORT->sIPAssistsEnabled );
|
|
STORE_HW( reply.hwIPVersion, 0x0004 );
|
|
|
|
ENQUEUE_REPLY_FRAME( pLCSDEV, reply );
|
|
}
|
|
|
|
// ====================================================================
|
|
// LCS_LanStats
|
|
// ====================================================================
|
|
|
|
static void LCS_LanStats( PLCSDEV pLCSDEV, PLCSCMDHDR pCmdFrame )
|
|
{
|
|
LCSLSTFRM reply;
|
|
PLCSPORT pLCSPORT;
|
|
int fd;
|
|
struct ifreq ifr;
|
|
BYTE* pPortMAC;
|
|
BYTE* pIFaceMAC;
|
|
|
|
INIT_REPLY_FRAME( reply, pCmdFrame );
|
|
|
|
pLCSPORT = &pLCSDEV->pLCSBLK->Port[pLCSDEV->bPort];
|
|
|
|
fd = socket( AF_INET, SOCK_STREAM, IPPROTO_IP );
|
|
|
|
if( fd == -1 )
|
|
{
|
|
// "CTC: error in function %s: %s"
|
|
WRMSG( HHC00940, "E", "socket()", strerror( HSO_errno ) );
|
|
// FIXME: we should probably be returning a non-zero hwReturnCode
|
|
// STORE_HW( reply.bLCSCmdHdr.hwReturnCode, 0x0001 );
|
|
return;
|
|
}
|
|
|
|
memset( &ifr, 0, sizeof( ifr ) );
|
|
|
|
strcpy( ifr.ifr_name, pLCSPORT->szNetIfName );
|
|
|
|
pPortMAC = (BYTE*) &pLCSPORT->MAC_Address;
|
|
|
|
/* Not all systems can return the hardware address of an interface. */
|
|
#if defined(SIOCGIFHWADDR)
|
|
|
|
if( TUNTAP_IOCtl( fd, SIOCGIFHWADDR, (char*)&ifr ) != 0 )
|
|
{
|
|
// "CTC: ioctl %s failed for device %s: %s"
|
|
WRMSG( HHC00941, "E", "SIOCGIFHWADDR", pLCSPORT->szNetIfName, strerror( errno ) );
|
|
// FIXME: we should probably be returning a non-zero hwReturnCode
|
|
// STORE_HW( reply.bLCSCmdHdr.hwReturnCode, 0x0002 );
|
|
return;
|
|
}
|
|
pIFaceMAC = (BYTE*) ifr.ifr_hwaddr.sa_data;
|
|
|
|
#else // !defined(SIOCGIFHWADDR)
|
|
|
|
pIFaceMAC = pPortMAC;
|
|
|
|
#endif // defined(SIOCGIFHWADDR)
|
|
|
|
/* Report what MAC address we will really be using */
|
|
// "CTC: lcs device '%s' using mac %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X"
|
|
WRMSG( HHC00942, "I", pLCSPORT->szNetIfName, *(pIFaceMAC+0),*(pIFaceMAC+1),
|
|
*(pIFaceMAC+2),*(pIFaceMAC+3),
|
|
*(pIFaceMAC+4),*(pIFaceMAC+5));
|
|
|
|
/* Issue warning if different from specified value */
|
|
if (memcmp( pPortMAC, pIFaceMAC, IFHWADDRLEN ) != 0)
|
|
{
|
|
if (pLCSPORT->fLocalMAC)
|
|
{
|
|
// "CTC: lcs device %s not using mac %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X"
|
|
WRMSG( HHC00943, "W", pLCSPORT->szNetIfName, *(pPortMAC+0),*(pPortMAC+1),
|
|
*(pPortMAC+2),*(pPortMAC+3),
|
|
*(pPortMAC+4),*(pPortMAC+5));
|
|
}
|
|
|
|
memcpy( pPortMAC, pIFaceMAC, IFHWADDRLEN );
|
|
|
|
snprintf(pLCSPORT->szMACAddress, sizeof(pLCSPORT->szMACAddress)-1,
|
|
"%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", *(pPortMAC+0), *(pPortMAC+1),
|
|
*(pPortMAC+2), *(pPortMAC+3), *(pPortMAC+4), *(pPortMAC+5));
|
|
}
|
|
|
|
memcpy( reply.MAC_Address, pIFaceMAC, IFHWADDRLEN );
|
|
|
|
/* Respond with a different MAC address for the LCS side */
|
|
/* unless the TAP mechanism is designed as such */
|
|
/* cf : hostopts.h for an explanation */
|
|
#if !defined(OPTION_TUNTAP_LCS_SAME_ADDR)
|
|
|
|
reply.MAC_Address[5]++;
|
|
|
|
#endif
|
|
|
|
// FIXME: Really should read /proc/net/dev to retrieve actual stats
|
|
|
|
ENQUEUE_REPLY_FRAME( pLCSDEV, reply );
|
|
}
|
|
|
|
// ====================================================================
|
|
// LCS_DefaultCmdProc
|
|
// ====================================================================
|
|
|
|
static void LCS_DefaultCmdProc( PLCSDEV pLCSDEV, PLCSCMDHDR pCmdFrame )
|
|
{
|
|
LCSSTDFRM reply;
|
|
|
|
INIT_REPLY_FRAME( reply, pCmdFrame );
|
|
|
|
reply.bLCSCmdHdr.bLanType = LCS_FRMTYP_ENET;
|
|
reply.bLCSCmdHdr.bRelAdapterNo = pLCSDEV->bPort;
|
|
|
|
ENQUEUE_REPLY_FRAME( pLCSDEV, reply );
|
|
}
|
|
|
|
// ====================================================================
|
|
// LCS_PortThread
|
|
// ====================================================================
|
|
|
|
static void* LCS_PortThread( void* arg)
|
|
{
|
|
DEVBLK* pDEVBLK;
|
|
PLCSPORT pLCSPORT = (PLCSPORT) arg;
|
|
PLCSDEV pLCSDev;
|
|
PLCSDEV pPrimaryLCSDEV;
|
|
PLCSDEV pSecondaryLCSDEV;
|
|
PLCSDEV pMatchingLCSDEV;
|
|
PLCSRTE pLCSRTE;
|
|
PETHFRM pEthFrame;
|
|
PIP4FRM pIPFrame = NULL;
|
|
PARPFRM pARPFrame = NULL;
|
|
int iLength;
|
|
U16 hwEthernetType;
|
|
U32 lIPAddress; // (network byte order)
|
|
BYTE* pMAC;
|
|
BYTE szBuff[2048];
|
|
char bReported = 0;
|
|
char bStartReported = 0;
|
|
time_t t1, t2;
|
|
|
|
pDEVBLK = pLCSPORT->pLCSBLK->pDevices->pDEVBLK[ LCSDEV_READ_SUBCHANN ];
|
|
|
|
pLCSPORT->pid = getpid();
|
|
|
|
for (;;)
|
|
{
|
|
obtain_lock( &pLCSPORT->PortEventLock );
|
|
{
|
|
// Don't read unless/until port is enabled...
|
|
|
|
if (pLCSPORT->pLCSBLK->fDebug && !pLCSPORT->fPortStarted)
|
|
{
|
|
if (bStartReported)
|
|
// "CTC: lcs device port %2.2X: read thread: port stopped"
|
|
WRMSG( HHC00969, "D", pLCSPORT->bPort );
|
|
|
|
// "CTC: lcs device port %2.2X: read thread: waiting for start event"
|
|
WRMSG( HHC00967, "D", pLCSPORT->bPort );
|
|
bStartReported = 0;
|
|
}
|
|
|
|
while (1
|
|
&& !(pLCSPORT->fd < 0)
|
|
&& !pLCSPORT->fCloseInProgress
|
|
&& !pLCSPORT->fPortStarted
|
|
)
|
|
{
|
|
timed_wait_condition_relative_usecs
|
|
(
|
|
&pLCSPORT->PortEvent, // ptr to condition to wait on
|
|
&pLCSPORT->PortEventLock, // ptr to controlling lock (must be held!)
|
|
250*1000, // max #of microseconds to wait
|
|
NULL // [OPTIONAL] ptr to tod value (may be NULL)
|
|
);
|
|
}
|
|
|
|
if (pLCSPORT->pLCSBLK->fDebug && !bStartReported)
|
|
{
|
|
// "CTC: lcs device port %2.2X: read thread: port started"
|
|
WRMSG( HHC00968, "D", pLCSPORT->bPort );
|
|
bStartReported = 1;
|
|
}
|
|
}
|
|
release_lock( &pLCSPORT->PortEventLock );
|
|
|
|
// Exit when told...
|
|
|
|
if ( pLCSPORT->fd < 0 || pLCSPORT->fCloseInProgress )
|
|
break;
|
|
|
|
// Read an IP packet from the TAP device
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "b4 tt read", 0, 0, 0 );
|
|
iLength = TUNTAP_Read( pLCSPORT->fd, szBuff, sizeof( szBuff ) );
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "af tt read", 0, 0, iLength );
|
|
|
|
if( iLength == 0 ) // (probably EINTR; ignore)
|
|
continue;
|
|
|
|
// Check for other error condition
|
|
if( iLength < 0 )
|
|
{
|
|
if( pLCSPORT->fd < 0 || pLCSPORT->fCloseInProgress )
|
|
break;
|
|
// "CTC: lcs device read error from port %2.2X: %s"
|
|
WRMSG( HHC00944, "E", pLCSPORT->bPort, strerror( errno ) );
|
|
break;
|
|
}
|
|
|
|
if( pDEVBLK->ccwtrace || pDEVBLK->ccwstep )
|
|
{
|
|
// Trace the frame
|
|
// "CTC: lcs device port %2.2X: read buffer"
|
|
WRMSG( HHC00945, "I", pLCSPORT->bPort );
|
|
packet_trace( szBuff, iLength, '>' );
|
|
|
|
bReported = 0;
|
|
}
|
|
|
|
pEthFrame = (PETHFRM)szBuff;
|
|
|
|
FETCH_HW( hwEthernetType, pEthFrame->hwEthernetType );
|
|
|
|
// Housekeeping
|
|
pPrimaryLCSDEV = NULL;
|
|
pSecondaryLCSDEV = NULL;
|
|
pMatchingLCSDEV = NULL;
|
|
|
|
// Attempt to find the device that this frame belongs to
|
|
for( pLCSDev = pLCSPORT->pLCSBLK->pDevices; pLCSDev; pLCSDev = pLCSDev->pNext )
|
|
{
|
|
// Only process devices that are on this port
|
|
if( pLCSDev->bPort == pLCSPORT->bPort )
|
|
{
|
|
if( hwEthernetType == ETH_TYPE_IP )
|
|
{
|
|
pIPFrame = (PIP4FRM)pEthFrame->bData;
|
|
lIPAddress = pIPFrame->lDstIP; // (network byte order)
|
|
|
|
if( pLCSPORT->pLCSBLK->fDebug && !bReported )
|
|
{
|
|
union converter { struct { unsigned char a, b, c, d; } b; U32 i; } c;
|
|
char str[40];
|
|
|
|
c.i = ntohl(lIPAddress);
|
|
MSGBUF( str, "%8.08X %d.%d.%d.%d", c.i, c.b.d, c.b.c, c.b.b, c.b.a );
|
|
|
|
// "CTC: lcs device port %2.2X: IPv4 frame received for %s"
|
|
WRMSG( HHC00946, "D", pLCSPORT->bPort, str );
|
|
bReported = 1;
|
|
}
|
|
|
|
// If this is an exact match use it
|
|
// otherwise look for primary and secondary
|
|
// default devices
|
|
if( pLCSDev->lIPAddress == lIPAddress )
|
|
{
|
|
pMatchingLCSDEV = pLCSDev;
|
|
break;
|
|
}
|
|
else if( pLCSDev->bType == LCSDEV_TYPE_PRIMARY )
|
|
pPrimaryLCSDEV = pLCSDev;
|
|
else if( pLCSDev->bType == LCSDEV_TYPE_SECONDARY )
|
|
pSecondaryLCSDEV = pLCSDev;
|
|
}
|
|
else if( hwEthernetType == ETH_TYPE_ARP )
|
|
{
|
|
pARPFrame = (PARPFRM)pEthFrame->bData;
|
|
lIPAddress = pARPFrame->lTargIPAddr; // (network byte order)
|
|
|
|
if( pLCSPORT->pLCSBLK->fDebug && !bReported )
|
|
{
|
|
union converter { struct { unsigned char a, b, c, d; } b; U32 i; } c;
|
|
char str[40];
|
|
|
|
c.i = ntohl(lIPAddress);
|
|
MSGBUF( str, "%8.08X %d.%d.%d.%d", c.i, c.b.d, c.b.c, c.b.b, c.b.a );
|
|
|
|
// "CTC: lcs device port %2.2X: ARP frame received for %s"
|
|
WRMSG( HHC00947, "D", pLCSPORT->bPort, str );
|
|
bReported = 1;
|
|
}
|
|
|
|
// If this is an exact match use it
|
|
// otherwise look for primary and secondary
|
|
// default devices
|
|
if( pLCSDev->lIPAddress == lIPAddress )
|
|
{
|
|
pMatchingLCSDEV = pLCSDev;
|
|
break;
|
|
}
|
|
else if( pLCSDev->bType == LCSDEV_TYPE_PRIMARY )
|
|
pPrimaryLCSDEV = pLCSDev;
|
|
else if( pLCSDev->bType == LCSDEV_TYPE_SECONDARY )
|
|
pSecondaryLCSDEV = pLCSDev;
|
|
}
|
|
else if( hwEthernetType == ETH_TYPE_RARP )
|
|
{
|
|
pARPFrame = (PARPFRM)pEthFrame->bData;
|
|
pMAC = pARPFrame->bTargEthAddr;
|
|
|
|
if( pLCSPORT->pLCSBLK->fDebug && !bReported )
|
|
{
|
|
WRMSG
|
|
(
|
|
// "CTC: lcs device port %2.2X: RARP frame received for %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X"
|
|
|
|
HHC00948, "D"
|
|
|
|
,pLCSPORT->bPort
|
|
,*(pMAC+0)
|
|
,*(pMAC+1)
|
|
,*(pMAC+2)
|
|
,*(pMAC+3)
|
|
,*(pMAC+4)
|
|
,*(pMAC+5)
|
|
);
|
|
bReported = 1;
|
|
}
|
|
|
|
// If this is an exact match use it
|
|
// otherwise look for primary and secondary
|
|
// default devices
|
|
if( memcmp( pMAC, pLCSPORT->MAC_Address, IFHWADDRLEN ) == 0 )
|
|
{
|
|
pMatchingLCSDEV = pLCSDev;
|
|
break;
|
|
}
|
|
else if( pLCSDev->bType == LCSDEV_TYPE_PRIMARY )
|
|
pPrimaryLCSDEV = pLCSDev;
|
|
else if( pLCSDev->bType == LCSDEV_TYPE_SECONDARY )
|
|
pSecondaryLCSDEV = pLCSDev;
|
|
}
|
|
else if( hwEthernetType == ETH_TYPE_SNA )
|
|
{
|
|
if( pLCSPORT->pLCSBLK->fDebug && !bReported )
|
|
{
|
|
// "CTC: lcs device port %2.2X: SNA frame received"
|
|
WRMSG( HHC00949, "D", pLCSPORT->bPort );
|
|
bReported = 1;
|
|
}
|
|
|
|
if( pLCSDev->bMode == LCSDEV_MODE_SNA )
|
|
{
|
|
pMatchingLCSDEV = pLCSDev;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the matching device is not started
|
|
// nullify the pointer and pass frame to one
|
|
// of the defaults if present
|
|
if( pMatchingLCSDEV && !pMatchingLCSDEV->fDevStarted )
|
|
pMatchingLCSDEV = NULL;
|
|
|
|
// Match not found, check for default devices
|
|
// If one is defined and started, use it
|
|
if( !pMatchingLCSDEV )
|
|
{
|
|
if( pPrimaryLCSDEV && pPrimaryLCSDEV->fDevStarted )
|
|
{
|
|
pMatchingLCSDEV = pPrimaryLCSDEV;
|
|
|
|
#ifndef LCS_NO_950_952 // (HHC00950 and HHC00952 are rarely interesting)
|
|
if( pLCSPORT->pLCSBLK->fDebug )
|
|
// "CTC: lcs device port %2.2X: no match found, selecting %s %4.4X"
|
|
WRMSG( HHC00950, "D", pLCSPORT->bPort, "primary", pMatchingLCSDEV->sAddr );
|
|
#endif // LCS_NO_950_952
|
|
}
|
|
else if( pSecondaryLCSDEV && pSecondaryLCSDEV->fDevStarted )
|
|
{
|
|
pMatchingLCSDEV = pSecondaryLCSDEV;
|
|
|
|
#ifndef LCS_NO_950_952 // (HHC00950 and HHC00952 are rarely interesting)
|
|
if( pLCSPORT->pLCSBLK->fDebug )
|
|
// "CTC: lcs device port %2.2X: no match found, selecting %s %4.4X"
|
|
WRMSG( HHC00950, "D", pLCSPORT->bPort, "secondary", pMatchingLCSDEV->sAddr );
|
|
#endif // LCS_NO_950_952
|
|
}
|
|
}
|
|
|
|
// No match found, discard frame
|
|
if( !pMatchingLCSDEV )
|
|
{
|
|
if( pLCSPORT->pLCSBLK->fDebug )
|
|
// "CTC: lcs device port %2.2X: no match found, discarding frame"
|
|
WRMSG( HHC00951, "D", pLCSPORT->bPort );
|
|
|
|
continue;
|
|
}
|
|
|
|
#ifndef LCS_NO_950_952 // (HHC00950 and HHC00952 are rarely interesting)
|
|
if( pLCSPORT->pLCSBLK->fDebug )
|
|
{
|
|
union converter { struct { unsigned char a, b, c, d; } b; U32 i; } c;
|
|
char str[40];
|
|
|
|
c.i = ntohl(pMatchingLCSDEV->lIPAddress);
|
|
MSGBUF( str, "%8.08X %d.%d.%d.%d", c.i, c.b.d, c.b.c, c.b.b, c.b.a );
|
|
|
|
// "CTC: lcs device port %2.2X: enqueing frame to device %4.4X %s"
|
|
WRMSG( HHC00952, "D", pLCSPORT->bPort, pMatchingLCSDEV->sAddr, str );
|
|
}
|
|
#endif // LCS_NO_950_952
|
|
|
|
// Match was found.
|
|
// Enqueue frame on buffer, if buffer is full, keep trying
|
|
|
|
time( &t1 );
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "b4 enqueue", 0, iLength, 0 );
|
|
while( LCS_EnqueueEthFrame( pMatchingLCSDEV, pLCSPORT->bPort, szBuff, iLength ) < 0
|
|
&& pLCSPORT->fd != -1 && !pLCSPORT->fCloseInProgress )
|
|
{
|
|
if (EMSGSIZE == errno)
|
|
{
|
|
if( pLCSPORT->pLCSBLK->fDebug )
|
|
// "CTC: lcs device port %2.2X: packet frame too big, dropped"
|
|
WRMSG( HHC00953, "W", pLCSPORT->bPort );
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "*enq drop", 0, iLength, 0 );
|
|
break;
|
|
}
|
|
if( pLCSPORT->pLCSBLK->fDebug )
|
|
{
|
|
// Limit HHC00965 message rate to only once every few seconds...
|
|
time( &t2 );
|
|
if ((t2 - t1) >= 3)
|
|
{
|
|
union converter { struct { unsigned char a, b, c, d; } b; U32 i; } c;
|
|
char str[40];
|
|
t1 = t2;
|
|
c.i = ntohl(pMatchingLCSDEV->lIPAddress);
|
|
MSGBUF( str, "%8.08X %d.%d.%d.%d", c.i, c.b.d, c.b.c, c.b.b, c.b.a );
|
|
// "CTC: lcs device port %2.2X: STILL trying to enqueue frame to device %4.4X %s"
|
|
WRMSG( HHC00965, "D", pLCSPORT->bPort, pMatchingLCSDEV->sAddr, str );
|
|
}
|
|
}
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "*enq wait", 0, iLength, 0 );
|
|
ASSERT( ENOBUFS == errno );
|
|
usleep( CTC_DELAY_USECS );
|
|
}
|
|
PTT_LCS_TIMING_DEBUG( PTT_CL_INF, "af enqueue", 0, iLength, 0 );
|
|
} // end for(;;)
|
|
|
|
// We must do the close since we were the one doing the i/o...
|
|
|
|
VERIFY( pLCSPORT->fd == -1 || TUNTAP_Close( pLCSPORT->fd ) == 0 );
|
|
|
|
// Housekeeping - Cleanup Port Block
|
|
|
|
memset( pLCSPORT->MAC_Address, 0, sizeof( MAC ) );
|
|
memset( pLCSPORT->szNetIfName, 0, IFNAMSIZ );
|
|
memset( pLCSPORT->szMACAddress, 0, 32 );
|
|
|
|
for( pLCSRTE = pLCSPORT->pRoutes; pLCSRTE; pLCSRTE = pLCSPORT->pRoutes )
|
|
{
|
|
pLCSPORT->pRoutes = pLCSRTE->pNext;
|
|
free( pLCSRTE );
|
|
pLCSRTE = NULL;
|
|
}
|
|
|
|
pLCSPORT->sIPAssistsSupported = 0;
|
|
pLCSPORT->sIPAssistsEnabled = 0;
|
|
|
|
pLCSPORT->fUsed = 0;
|
|
pLCSPORT->fLocalMAC = 0;
|
|
pLCSPORT->fPortCreated = 0;
|
|
pLCSPORT->fPortStarted = 0;
|
|
pLCSPORT->fRouteAdded = 0;
|
|
pLCSPORT->fd = -1;
|
|
|
|
return NULL;
|
|
|
|
} // end of LCS_PortThread
|
|
|
|
// ====================================================================
|
|
// LCS_EnqueueEthFrame
|
|
// ====================================================================
|
|
//
|
|
// Places the provided ethernet frame in the next available frame
|
|
// slot in the adapter buffer.
|
|
//
|
|
// pData points the the Ethernet packet just received
|
|
// iSize is the size of the Ethernet packet
|
|
//
|
|
// Returns:
|
|
//
|
|
// 0 == Success
|
|
// -1 == Failure; errno = ENOBUFS: No buffer space available
|
|
// EMSGSIZE: Message too long
|
|
//
|
|
// --------------------------------------------------------------------
|
|
|
|
static int LCS_EnqueueEthFrame( PLCSDEV pLCSDEV, BYTE bPort,
|
|
BYTE* pData, size_t iSize )
|
|
{
|
|
PLCSETHFRM pLCSEthFrame;
|
|
|
|
// Will frame NEVER fit into buffer??
|
|
if( iSize > MAX_LCS_ETH_FRAME_SIZE( pLCSDEV ) || iSize > 9000 )
|
|
{
|
|
errno = EMSGSIZE; // Message too long
|
|
return -1; // (-1==failure)
|
|
}
|
|
|
|
obtain_lock( &pLCSDEV->DevDataLock );
|
|
|
|
// Ensure we dont overflow the buffer
|
|
if( ( pLCSDEV->iFrameOffset + // Current buffer Offset
|
|
sizeof( LCSETHFRM ) + // Size of Frame Header
|
|
iSize + // Size of Ethernet packet
|
|
sizeof(pLCSEthFrame->bLCSHdr.hwOffset) ) // Size of Frame terminator
|
|
> pLCSDEV->iMaxFrameBufferSize ) // Size of Frame buffer
|
|
{
|
|
release_lock( &pLCSDEV->DevDataLock );
|
|
errno = ENOBUFS; // No buffer space available
|
|
return -1; // (-1==failure)
|
|
}
|
|
|
|
// Point to next available LCS Frame slot in our buffer
|
|
pLCSEthFrame = (PLCSETHFRM)( pLCSDEV->bFrameBuffer +
|
|
pLCSDEV->iFrameOffset );
|
|
|
|
// Increment offset to NEXT available slot (after ours)
|
|
pLCSDEV->iFrameOffset += (U16)(sizeof(LCSETHFRM) + iSize);
|
|
|
|
// Plug updated offset to next frame into our frame header
|
|
STORE_HW( pLCSEthFrame->bLCSHdr.hwOffset, pLCSDEV->iFrameOffset );
|
|
|
|
// Finish building the LCS Ethernet Passthru frame header
|
|
pLCSEthFrame->bLCSHdr.bType = LCS_FRMTYP_ENET;
|
|
pLCSEthFrame->bLCSHdr.bSlot = bPort;
|
|
|
|
// Copy Ethernet packet to LCS Ethernet Passthru frame
|
|
memcpy( pLCSEthFrame->bData, pData, iSize );
|
|
|
|
// Tell "LCS_Read" function that data is available for reading
|
|
pLCSDEV->fDataPending = 1;
|
|
|
|
release_lock( &pLCSDEV->DevDataLock );
|
|
|
|
// (wake up "LCS_Read" function)
|
|
obtain_lock( &pLCSDEV->DevEventLock );
|
|
signal_condition( &pLCSDEV->DevEvent );
|
|
release_lock( &pLCSDEV->DevEventLock );
|
|
|
|
return 0; // (success)
|
|
}
|
|
|
|
// ====================================================================
|
|
// LCS_EnqueueReplyFrame
|
|
// ====================================================================
|
|
//
|
|
// Copy a pre-built LCS Command Frame reply frame of iSize bytes
|
|
// to the next available frame slot. Returns 0 on success, -1 and
|
|
// errno set to ENOBUFS on failure (no room (yet) in o/p buffer).
|
|
// The LCS device lock must NOT be held when called.
|
|
//
|
|
// --------------------------------------------------------------------
|
|
|
|
static int LCS_EnqueueReplyFrame( PLCSDEV pLCSDEV, PLCSCMDHDR pReply,
|
|
size_t iSize )
|
|
{
|
|
PLCSCMDHDR pReplyCmdFrame;
|
|
|
|
obtain_lock( &pLCSDEV->DevDataLock );
|
|
|
|
// Ensure we dont overflow the buffer
|
|
if( ( pLCSDEV->iFrameOffset + // Current buffer Offset
|
|
iSize + // Size of reply frame
|
|
sizeof(pReply->bLCSHdr.hwOffset)) // Size of Frame terminator
|
|
> pLCSDEV->iMaxFrameBufferSize ) // Size of Frame buffer
|
|
{
|
|
release_lock( &pLCSDEV->DevDataLock );
|
|
errno = ENOBUFS; // No buffer space available
|
|
return -1; // (-1==failure)
|
|
}
|
|
|
|
// Point to next available LCS Frame slot in our buffer...
|
|
pReplyCmdFrame = (PLCSCMDHDR)( pLCSDEV->bFrameBuffer +
|
|
pLCSDEV->iFrameOffset );
|
|
|
|
// Copy the reply frame into the frame buffer slot...
|
|
memcpy( pReplyCmdFrame, pReply, iSize );
|
|
|
|
// Increment buffer offset to NEXT next-available-slot...
|
|
pLCSDEV->iFrameOffset += (U16) iSize;
|
|
|
|
// Store offset of next frame
|
|
STORE_HW( pReplyCmdFrame->bLCSHdr.hwOffset, pLCSDEV->iFrameOffset );
|
|
|
|
// Mark reply pending
|
|
pLCSDEV->fReplyPending = 1;
|
|
|
|
release_lock( &pLCSDEV->DevDataLock );
|
|
|
|
return 0; // success
|
|
}
|
|
|
|
// ====================================================================
|
|
// ParseArgs
|
|
// ====================================================================
|
|
|
|
int ParseArgs( DEVBLK* pDEVBLK, PLCSBLK pLCSBLK,
|
|
int argc, char** argx )
|
|
{
|
|
struct in_addr addr; // Work area for addresses
|
|
MAC mac;
|
|
int i;
|
|
#if defined(OPTION_W32_CTCI)
|
|
int iKernBuff;
|
|
int iIOBuff;
|
|
#endif
|
|
char *argn[MAX_ARGS];
|
|
char **argv = argn;
|
|
int saw_if = 0; /* -x (or --if) specified */
|
|
int saw_conf = 0; /* Other configuration flags present */
|
|
|
|
|
|
// Build a copy of the argv list.
|
|
// getopt() and getopt_long() expect argv[0] to be a program name.
|
|
// We need to shift the arguments and insert a dummy argv[0].
|
|
if( argc > (MAX_ARGS-1) )
|
|
argc = (MAX_ARGS-1);
|
|
for( i = 0; i < argc; i++ )
|
|
argn[i+1] = argx[i];
|
|
argc++;
|
|
argn[0] = pDEVBLK->typname;
|
|
|
|
// Housekeeping
|
|
memset( &addr, 0, sizeof( struct in_addr ) );
|
|
|
|
// Set some initial defaults
|
|
#if defined( OPTION_W32_CTCI )
|
|
pLCSBLK->pszTUNDevice = strdup( tt32_get_default_iface() );
|
|
#else
|
|
pLCSBLK->pszTUNDevice = strdup( HERCTUN_DEV );
|
|
#endif
|
|
pLCSBLK->pszOATFilename = NULL;
|
|
pLCSBLK->pszIPAddress = NULL;
|
|
// pLCSBLK->pszMACAddress = NULL;
|
|
#if defined( OPTION_W32_CTCI )
|
|
pLCSBLK->iKernBuff = DEF_CAPTURE_BUFFSIZE;
|
|
pLCSBLK->iIOBuff = DEF_PACKET_BUFFSIZE;
|
|
#endif
|
|
|
|
// Initialize getopt's counter. This is necessary in the case
|
|
// that getopt was used previously for another device.
|
|
OPTRESET();
|
|
optind = 0;
|
|
|
|
// Parse any optional arguments
|
|
while( 1 )
|
|
{
|
|
int c;
|
|
|
|
#if defined( OPTION_W32_CTCI )
|
|
#define LCS_OPTSTRING "n:m:o:dk:i:w"
|
|
#else
|
|
#define LCS_OPTSTRING "n:x:m:o:d"
|
|
#endif
|
|
#if defined( HAVE_GETOPT_LONG )
|
|
int iOpt;
|
|
|
|
static struct option options[] =
|
|
{
|
|
{ "dev", required_argument, NULL, 'n' },
|
|
#if !defined(OPTION_W32_CTCI)
|
|
{ "if", required_argument, NULL, 'x' },
|
|
#endif /*!defined(OPTION_W32_CTCI)*/
|
|
{ "mac", required_argument, NULL, 'm' },
|
|
{ "oat", required_argument, NULL, 'o' },
|
|
{ "debug", no_argument, NULL, 'd' },
|
|
#if defined( OPTION_W32_CTCI )
|
|
{ "kbuff", required_argument, NULL, 'k' },
|
|
{ "ibuff", required_argument, NULL, 'i' },
|
|
{ "swrite", no_argument, NULL, 'w' },
|
|
#endif
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
|
|
c = getopt_long( argc, argv, LCS_OPTSTRING, options, &iOpt );
|
|
|
|
#else /* defined( HAVE_GETOPT_LONG ) */
|
|
|
|
c = getopt( argc, argv, LCS_OPTSTRING );
|
|
|
|
#endif /* defined( HAVE_GETOPT_LONG ) */
|
|
|
|
if( c == -1 )
|
|
break;
|
|
|
|
switch( c )
|
|
{
|
|
case 'n':
|
|
|
|
if( strlen( optarg ) > sizeof( pDEVBLK->filename ) - 1 )
|
|
{
|
|
// "%1d:%04X CTC: option %s value %s invalid"
|
|
WRMSG( HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum,
|
|
"device name", optarg );
|
|
return -1;
|
|
}
|
|
|
|
pLCSBLK->pszTUNDevice = strdup( optarg );
|
|
break;
|
|
|
|
#if !defined(OPTION_W32_CTCI)
|
|
case 'x': /* TAP network interface name */
|
|
if( strlen( optarg ) > sizeof(pLCSBLK->Port[0].szNetIfName)-1 )
|
|
{
|
|
// HHC03976 "%1d:%04X %s: option '%s' value '%s' invalid"
|
|
WRMSG(HHC03976, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pDEVBLK->typname,
|
|
"TAP device name", optarg );
|
|
return -1;
|
|
}
|
|
strlcpy( pLCSBLK->Port[0].szNetIfName, optarg, sizeof(pLCSBLK->Port[0].szNetIfName) );
|
|
saw_if = 1;
|
|
break;
|
|
#endif /*!defined(OPTION_W32_CTCI)*/
|
|
|
|
case 'm':
|
|
|
|
if( ParseMAC( optarg, mac ) != 0 )
|
|
{
|
|
// "%1d:%04X CTC: option %s value %s invalid"
|
|
WRMSG( HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum,
|
|
"MAC address", optarg );
|
|
return -1;
|
|
}
|
|
|
|
strcpy( pLCSBLK->Port[0].szMACAddress, optarg );
|
|
memcpy( pLCSBLK->Port[0].MAC_Address, &mac, sizeof(MAC) );
|
|
pLCSBLK->Port[0].fLocalMAC = TRUE;
|
|
saw_conf = 1;
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
pLCSBLK->pszOATFilename = strdup( optarg );
|
|
saw_conf = 1;
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
pLCSBLK->fDebug = TRUE;
|
|
break;
|
|
|
|
#if defined( OPTION_W32_CTCI )
|
|
|
|
case 'k': // Kernel Buffer Size (Windows only)
|
|
|
|
iKernBuff = atoi( optarg );
|
|
|
|
if( iKernBuff * 1024 < MIN_CAPTURE_BUFFSIZE ||
|
|
iKernBuff * 1024 > MAX_CAPTURE_BUFFSIZE )
|
|
{
|
|
// "%1d:%04X CTC: option %s value %s invalid"
|
|
WRMSG( HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum,
|
|
"kernel buffer size", optarg );
|
|
return -1;
|
|
}
|
|
|
|
pLCSBLK->iKernBuff = iKernBuff * 1024;
|
|
break;
|
|
|
|
case 'i': // I/O Buffer Size (Windows only)
|
|
|
|
iIOBuff = atoi( optarg );
|
|
|
|
if( iIOBuff * 1024 < MIN_PACKET_BUFFSIZE ||
|
|
iIOBuff * 1024 > MAX_PACKET_BUFFSIZE )
|
|
{
|
|
// "%1d:%04X CTC: option %s value %s invalid"
|
|
WRMSG( HHC00916, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum,
|
|
"dll i/o buffer size", optarg );
|
|
return -1;
|
|
}
|
|
|
|
pLCSBLK->iIOBuff = iIOBuff * 1024;
|
|
break;
|
|
|
|
case 'w': // Single packet writes mode (Windows only)
|
|
|
|
pLCSBLK->fNoMultiWrite = TRUE;
|
|
break;
|
|
|
|
#endif // defined( OPTION_W32_CTCI )
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
#if !defined(OPTION_W32_CTCI)
|
|
/* If the -x option and one of the configuration options (e.g. the */
|
|
/* -m or the -o options has been specified, reject the -x option. */
|
|
if (saw_if && saw_conf)
|
|
{
|
|
/* HHC03976 "%1d:%04X %s: option '%s' value '%s' invalid" */
|
|
WRMSG(HHC03976, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pDEVBLK->typname,
|
|
"TUN device name", pLCSBLK->Port[0].szNetIfName );
|
|
return -1;
|
|
}
|
|
#endif /*!defined(OPTION_W32_CTCI)*/
|
|
|
|
if (argc > 1)
|
|
{
|
|
/* There are two or more arguments. */
|
|
/* HHC03975 "%1d:%04X %s: incorrect number of parameters" */
|
|
WRMSG(HHC03975, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pDEVBLK->typname );
|
|
return -1;
|
|
}
|
|
else if (argc == 1)
|
|
{
|
|
/* There is one argument, check for an IPv4 address. */
|
|
if( inet_aton( *argv, &addr ) != 0 )
|
|
{
|
|
/* The argument is an IPv4 address. If the -x option was specified, */
|
|
/* it has pre-named the TAP interface that LCS will use (*nix). */
|
|
if ( pLCSBLK->pszIPAddress ) { free( pLCSBLK->pszIPAddress ); pLCSBLK->pszIPAddress = NULL; }
|
|
pLCSBLK->pszIPAddress = strdup( *argv );
|
|
pLCSBLK->Port[0].fPreconfigured = FALSE;
|
|
} else {
|
|
#if defined(OPTION_W32_CTCI)
|
|
WRMSG(HHC03976, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pDEVBLK->typname,
|
|
"IP address", *argv );
|
|
return -1;
|
|
#else /*defined(OPTION_W32_CTCI)*/
|
|
/* The argument is not an IPv4 address. If the -x option was */
|
|
/* specified, the argument shouldn't have been specified. */
|
|
if (saw_if ) {
|
|
WRMSG(HHC03976, "E", SSID_TO_LCSS(pDEVBLK->ssid), pDEVBLK->devnum, pDEVBLK->typname,
|
|
"IP address", *argv );
|
|
return -1;
|
|
}
|
|
/* The -x option was not specified, so the argument should be the */
|
|
/* the name of the pre-configured TAP interface that LCS will use. */
|
|
strlcpy( pLCSBLK->Port[0].szNetIfName, argv[0], sizeof(pLCSBLK->Port[0].szNetIfName) );
|
|
pLCSBLK->Port[0].fPreconfigured = TRUE;
|
|
#endif /*defined(OPTION_W32_CTCI)*/
|
|
}
|
|
}
|
|
#if !defined(OPTION_W32_CTCI)
|
|
else
|
|
{
|
|
/* There are no arguments. If the -x option was specified, it */
|
|
/* named a pre-configured TAP interface that LCS will use. */
|
|
if (saw_if)
|
|
pLCSBLK->Port[0].fPreconfigured = TRUE;
|
|
else
|
|
pLCSBLK->Port[0].fPreconfigured = FALSE;
|
|
}
|
|
#endif /*!defined(OPTION_W32_CTCI)*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ====================================================================
|
|
// BuildOAT
|
|
// ====================================================================
|
|
|
|
static int BuildOAT( char* pszOATName, PLCSBLK pLCSBLK )
|
|
{
|
|
FILE* fp;
|
|
char szBuff[255];
|
|
|
|
int i;
|
|
char c; // Character work area
|
|
char* pszStatement = NULL; // -> Resolved statement
|
|
char* pszKeyword; // -> Statement keyword
|
|
char* pszOperand; // -> Statement operand
|
|
int argc; // Number of args
|
|
char* argv[MAX_ARGS]; // Argument array
|
|
|
|
PLCSPORT pLCSPORT;
|
|
PLCSDEV pLCSDev;
|
|
PLCSRTE pLCSRTE;
|
|
|
|
U16 sPort;
|
|
BYTE bMode;
|
|
U16 sDevNum;
|
|
BYTE bType;
|
|
U32 lIPAddr = 0; // (network byte order)
|
|
char* pszIPAddress = NULL;
|
|
char* pszNetAddr = NULL;
|
|
char* pszNetMask = NULL;
|
|
char* strtok_str = NULL;
|
|
|
|
struct in_addr addr; // Work area for addresses
|
|
char pathname[MAX_PATH]; // pszOATName in host path format
|
|
|
|
// Open the configuration file
|
|
hostpath(pathname, pszOATName, sizeof(pathname));
|
|
fp = fopen( pathname, "r" );
|
|
if( !fp )
|
|
{
|
|
char msgbuf[MAX_PATH+80];
|
|
MSGBUF( msgbuf, "fopen(%s, \"r\")", pathname);
|
|
// "CTC: error in function %s: %s"
|
|
WRMSG( HHC00940, "E", msgbuf, strerror( errno ) );
|
|
return -1;
|
|
}
|
|
|
|
for(;;)
|
|
{
|
|
// Read next record from the OAT file
|
|
if( !ReadOAT( pszOATName, fp, szBuff ) )
|
|
{
|
|
fclose( fp );
|
|
return 0;
|
|
}
|
|
|
|
if( pszStatement )
|
|
{
|
|
free( pszStatement );
|
|
pszStatement = NULL;
|
|
}
|
|
|
|
#if defined(OPTION_CONFIG_SYMBOLS)
|
|
// Make a copy of the OAT statement with symbols resolved
|
|
pszStatement = resolve_symbol_string( szBuff );
|
|
#else
|
|
// Make a copy of the OAT statement
|
|
pszStatement = strdup( szBuff );
|
|
#endif
|
|
|
|
sPort = 0;
|
|
bMode = 0;
|
|
sDevNum = 0;
|
|
bType = 0;
|
|
pszIPAddress = NULL;
|
|
pszNetAddr = NULL;
|
|
pszNetMask = NULL;
|
|
|
|
memset( &addr, 0, sizeof( addr ) );
|
|
|
|
// Split the statement into keyword and first operand
|
|
pszKeyword = strtok_r( pszStatement, " \t", &strtok_str );
|
|
pszOperand = strtok_r( NULL, " \t", &strtok_str );
|
|
|
|
// Extract any arguments
|
|
for( argc = 0;
|
|
argc < MAX_ARGS &&
|
|
( argv[argc] = strtok_r( NULL, " \t", &strtok_str ) ) != NULL &&
|
|
argv[argc][0] != '#';
|
|
argc++ );
|
|
|
|
// Clear any unused argument pointers
|
|
for( i = argc; i < MAX_ARGS; i++ )
|
|
argv[i] = NULL;
|
|
|
|
if( strcasecmp( pszKeyword, "HWADD" ) == 0 )
|
|
{
|
|
if( !pszOperand ||
|
|
argc != 1 ||
|
|
sscanf( pszOperand, "%hi%c", &sPort, &c ) != 1 )
|
|
{
|
|
// "CTC: invalid statement %s in file %s: %s"
|
|
WRMSG( HHC00954, "E", "HWADD", pszOATName, szBuff );
|
|
return -1;
|
|
}
|
|
|
|
pLCSPORT = &pLCSBLK->Port[sPort];
|
|
|
|
if( ParseMAC( argv[0], pLCSPORT->MAC_Address ) != 0 )
|
|
{
|
|
// "CTC: invalid %s %s in statement %s in file %s: %s"
|
|
WRMSG( HHC00955, "E", "MAC", argv[0], "HWADD", pszOATName, szBuff );
|
|
|
|
memset( pLCSPORT->MAC_Address, 0, sizeof(MAC) );
|
|
return -1;
|
|
}
|
|
|
|
strcpy( pLCSPORT->szMACAddress, argv[0] );
|
|
pLCSPORT->fLocalMAC = TRUE;
|
|
}
|
|
else if( strcasecmp( pszKeyword, "ROUTE" ) == 0 )
|
|
{
|
|
if( !pszOperand ||
|
|
argc != 2 ||
|
|
sscanf( pszOperand, "%hi%c", &sPort, &c ) != 1 )
|
|
{
|
|
// "CTC: invalid statement %s in file %s: %s"
|
|
WRMSG( HHC00954, "E", "ROUTE", pszOATName, szBuff );
|
|
return -1;
|
|
}
|
|
|
|
if( inet_aton( argv[0], &addr ) == 0 )
|
|
{
|
|
// "CTC: invalid %s %s in statement %s in file %s: %s"
|
|
WRMSG( HHC00955, "E", "net address", argv[0], "ROUTE", pszOATName, szBuff );
|
|
return -1;
|
|
}
|
|
|
|
pszNetAddr = strdup( argv[0] );
|
|
|
|
if( inet_aton( argv[1], &addr ) == 0 )
|
|
{
|
|
free(pszNetAddr);
|
|
// "CTC: invalid %s %s in statement %s in file %s: %s"
|
|
WRMSG( HHC00955, "E", "netmask", argv[1], "ROUTE", pszOATName, szBuff );
|
|
return -1;
|
|
}
|
|
|
|
pszNetMask = strdup( argv[1] );
|
|
|
|
pLCSPORT = &pLCSBLK->Port[sPort];
|
|
|
|
if( !pLCSPORT->pRoutes )
|
|
{
|
|
pLCSPORT->pRoutes = malloc( sizeof( LCSRTE ) );
|
|
pLCSRTE = pLCSPORT->pRoutes;
|
|
}
|
|
else
|
|
{
|
|
for( pLCSRTE = pLCSPORT->pRoutes;
|
|
pLCSRTE->pNext;
|
|
pLCSRTE = pLCSRTE->pNext );
|
|
|
|
pLCSRTE->pNext = malloc( sizeof( LCSRTE ) );
|
|
pLCSRTE = pLCSRTE->pNext;
|
|
}
|
|
|
|
pLCSRTE->pszNetAddr = pszNetAddr;
|
|
pLCSRTE->pszNetMask = pszNetMask;
|
|
pLCSRTE->pNext = NULL;
|
|
}
|
|
else // (presumed OAT file device statement)
|
|
{
|
|
if( !pszKeyword || !pszOperand )
|
|
{
|
|
// "CTC: error in file %s: missing device number or mode"
|
|
WRMSG( HHC00956, "E", pszOATName );
|
|
return -1;
|
|
}
|
|
|
|
if( strlen( pszKeyword ) > 4 ||
|
|
sscanf( pszKeyword, "%hx%c", &sDevNum, &c ) != 1 )
|
|
{
|
|
// "CTC: error in file %s: invalid %s value %s"
|
|
WRMSG( HHC00957, "E", pszOATName, "device number", pszKeyword );
|
|
return -1;
|
|
}
|
|
|
|
if( strcasecmp( pszOperand, "IP" ) == 0 )
|
|
{
|
|
bMode = LCSDEV_MODE_IP;
|
|
|
|
if( argc < 1 )
|
|
{
|
|
// "CTC: error in file %s: %s: missing port number"
|
|
WRMSG( HHC00958, "E", pszOATName, szBuff );
|
|
return -1;
|
|
}
|
|
|
|
if( sscanf( argv[0], "%hi%c", &sPort, &c ) != 1 )
|
|
{
|
|
// "CTC: error in file %s: invalid %s value %s"
|
|
WRMSG( HHC00957, "E", pszOATName, "port number", argv[0] );
|
|
return -1;
|
|
}
|
|
|
|
if( argc > 1 )
|
|
{
|
|
if( strcasecmp( argv[1], "PRI" ) == 0 )
|
|
bType = LCSDEV_TYPE_PRIMARY;
|
|
else if( strcasecmp( argv[1], "SEC" ) == 0 )
|
|
bType = LCSDEV_TYPE_SECONDARY;
|
|
else if( strcasecmp( argv[1], "NO" ) == 0 )
|
|
bType = LCSDEV_TYPE_NONE;
|
|
else
|
|
{
|
|
// "CTC: error in file %s: %s: invalid entry starting at %s"
|
|
WRMSG( HHC00959, "E", pszOATName, szBuff, argv[1] );
|
|
return -1;
|
|
}
|
|
|
|
if( argc > 2 )
|
|
{
|
|
pszIPAddress = strdup( argv[2] );
|
|
|
|
if( inet_aton( pszIPAddress, &addr ) == 0 )
|
|
{
|
|
// "CTC: error in file %s: invalid %s value %s"
|
|
WRMSG( HHC00957, "E", pszOATName, "IP address", pszIPAddress );
|
|
return -1;
|
|
}
|
|
|
|
lIPAddr = addr.s_addr; // (network byte order)
|
|
}
|
|
}
|
|
}
|
|
else if( strcasecmp( pszOperand, "SNA" ) == 0 )
|
|
{
|
|
bMode = LCSDEV_MODE_SNA;
|
|
|
|
if( argc < 1 )
|
|
{
|
|
// "CTC: error in file %s: %s: missing port number"
|
|
WRMSG( HHC00958, "E", pszOATName, szBuff );
|
|
return -1;
|
|
}
|
|
|
|
if( sscanf( argv[0], "%hi%c", &sPort, &c ) != 1 )
|
|
{
|
|
// "CTC: error in file %s: invalid %s value %s"
|
|
WRMSG( HHC00957, "E", pszOATName, "port number", argv[0] );
|
|
return -1;
|
|
}
|
|
|
|
if( argc > 1 )
|
|
{
|
|
// "CTC: error in file %s: %s: SNA does not accept any arguments"
|
|
WRMSG( HHC00960, "E", pszOATName, szBuff );
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// "CTC: error in file %s: %s: invalid mode"
|
|
WRMSG( HHC00961, "E", pszOATName, pszOperand );
|
|
return -1;
|
|
}
|
|
|
|
// Create new LCS Device...
|
|
|
|
pLCSDev = malloc( sizeof( LCSDEV ) );
|
|
memset( pLCSDev, 0, sizeof( LCSDEV ) );
|
|
|
|
pLCSDev->sAddr = sDevNum;
|
|
pLCSDev->bMode = bMode;
|
|
pLCSDev->bPort = sPort;
|
|
pLCSDev->bType = bType;
|
|
pLCSDev->lIPAddress = lIPAddr; // (network byte order)
|
|
pLCSDev->pszIPAddress = pszIPAddress;
|
|
pLCSDev->pNext = NULL;
|
|
|
|
// Add it to end of chain...
|
|
|
|
if( !pLCSBLK->pDevices )
|
|
pLCSBLK->pDevices = pLCSDev; // (first link in chain)
|
|
else
|
|
{
|
|
PLCSDEV pOldLastLCSDEV;
|
|
// (find last link in chain)
|
|
for( pOldLastLCSDEV = pLCSBLK->pDevices;
|
|
pOldLastLCSDEV->pNext;
|
|
pOldLastLCSDEV = pOldLastLCSDEV->pNext );
|
|
// (add new link to end of chain)
|
|
pOldLastLCSDEV->pNext = pLCSDev;
|
|
}
|
|
|
|
// Count it...
|
|
|
|
if(pLCSDev->bMode == LCSDEV_MODE_IP)
|
|
pLCSBLK->icDevices += 2;
|
|
else
|
|
pLCSBLK->icDevices += 1;
|
|
|
|
} // end OAT file statement
|
|
|
|
} // end for(;;)
|
|
|
|
UNREACHABLE_CODE();
|
|
}
|
|
|
|
// ====================================================================
|
|
// ReadOAT
|
|
// ====================================================================
|
|
|
|
static char* ReadOAT( char* pszOATName, FILE* fp, char* pszBuff )
|
|
{
|
|
int c; // Character work area
|
|
int iLine = 0; // Statement number
|
|
int iLen; // Statement length
|
|
|
|
while( 1 )
|
|
{
|
|
// Increment statement number
|
|
iLine++;
|
|
|
|
// Read next statement from OAT
|
|
for( iLen = 0; ; )
|
|
{
|
|
// Read character from OAT
|
|
c = fgetc( fp );
|
|
|
|
// Check for I/O error
|
|
if( ferror( fp ) )
|
|
{
|
|
// "CTC: error in file %s: reading line %d: %s"
|
|
WRMSG( HHC00962, "E", pszOATName, iLine, strerror( errno ) );
|
|
return NULL;
|
|
}
|
|
|
|
// Check for end of file
|
|
if( iLen == 0 && ( c == EOF || c == '\x1A' ) )
|
|
return NULL;
|
|
|
|
// Check for end of line
|
|
if( c == '\n' || c == EOF || c == '\x1A' )
|
|
break;
|
|
|
|
// Ignore leading blanks and tabs
|
|
if( iLen == 0 && ( c == ' ' || c == '\t' ) )
|
|
continue;
|
|
|
|
// Ignore nulls and carriage returns
|
|
if( c == '\0' || c == '\r' )
|
|
continue;
|
|
|
|
// Check that statement does not overflow bufffer
|
|
if( iLen >= 255 )
|
|
{
|
|
// "CTC: error in file %s: line %d is too long"
|
|
WRMSG( HHC00963, "E", pszOATName, iLine );
|
|
exit(1);
|
|
}
|
|
|
|
// Append character to buffer
|
|
pszBuff[iLen++] = c;
|
|
}
|
|
|
|
// Remove trailing blanks and tabs
|
|
while( iLen > 0 &&
|
|
( pszBuff[iLen-1] == ' ' ||
|
|
pszBuff[iLen-1] == '\t' ) )
|
|
iLen--;
|
|
|
|
pszBuff[iLen] = '\0';
|
|
|
|
// Ignore comments and null statements
|
|
if( iLen == 0 || pszBuff[0] == '*' || pszBuff[0] == '#' )
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
return pszBuff;
|
|
}
|
|
|
|
// ====================================================================
|
|
// Device Handler Information
|
|
// ====================================================================
|
|
|
|
/* NOTE : lcs_device_hndinfo is NEVER static as it is referenced by the CTC meta driver */
|
|
DEVHND lcs_device_hndinfo =
|
|
{
|
|
&LCS_Init, /* Device Initialisation */
|
|
&LCS_ExecuteCCW, /* Device CCW execute */
|
|
&LCS_Close, /* Device Close */
|
|
&LCS_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 */
|
|
CTC_Immed_Commands, /* Immediate CCW Codes */
|
|
NULL, /* Signal Adapter Input */
|
|
NULL, /* Signal Adapter Output */
|
|
NULL, /* Signal Adapter Sync */
|
|
NULL, /* Signal Adapter Output Mult */
|
|
NULL, /* QDIO subsys desc */
|
|
NULL, /* QDIO set subchan ind */
|
|
NULL, /* Hercules suspend */
|
|
NULL /* Hercules resume */
|
|
};
|
|
|
|
|
|
/* Libtool static name colision resolution */
|
|
/* note : lt_dlopen will look for symbol & modulename_LTX_symbol */
|
|
#if !defined(HDL_BUILD_SHARED) && defined(HDL_USE_LIBTOOL)
|
|
#define hdl_ddev hdt3088_LTX_hdl_ddev
|
|
#define hdl_depc hdt3088_LTX_hdl_depc
|
|
#define hdl_reso hdt3088_LTX_hdl_reso
|
|
#define hdl_init hdt3088_LTX_hdl_init
|
|
#define hdl_fini hdt3088_LTX_hdl_fini
|
|
#endif
|
|
|
|
#if defined(OPTION_DYNAMIC_LOAD)
|
|
HDL_DEPENDENCY_SECTION;
|
|
{
|
|
HDL_DEPENDENCY(HERCULES);
|
|
HDL_DEPENDENCY(DEVBLK);
|
|
}
|
|
END_DEPENDENCY_SECTION
|
|
|
|
HDL_REGISTER_SECTION; // ("Register" our entry-points)
|
|
|
|
// Hercules's Our
|
|
// registered overriding
|
|
// entry-point entry-point
|
|
// name value
|
|
|
|
#if defined( WIN32 )
|
|
HDL_REGISTER ( debug_tt32_stats, display_tt32_stats );
|
|
HDL_REGISTER ( debug_tt32_tracing, enable_tt32_debug_tracing );
|
|
#endif
|
|
|
|
END_REGISTER_SECTION
|
|
|
|
|
|
HDL_DEVICE_SECTION;
|
|
{
|
|
HDL_DEVICE(LCS, lcs_device_hndinfo );
|
|
|
|
// ZZ the following device types should be moved to
|
|
// ZZ their own loadable modules
|
|
HDL_DEVICE(3088, ctcadpt_device_hndinfo );
|
|
HDL_DEVICE(CTCI, ctci_device_hndinfo );
|
|
HDL_DEVICE(CTCT, ctct_device_hndinfo );
|
|
HDL_DEVICE(CTCE, ctce_device_hndinfo );
|
|
HDL_DEVICE(VMNET,vmnet_device_hndinfo );
|
|
#if defined(WIN32)
|
|
HDL_DEVICE(CTCI-W32,ctci_device_hndinfo );
|
|
#endif
|
|
}
|
|
END_DEVICE_SECTION
|
|
#endif
|
|
|
|
#if defined( _MSVC_ ) && defined( NO_LCS_OPTIMIZE )
|
|
#pragma optimize( "", on ) // restore previous settings
|
|
#endif
|
|
|
|
#endif /* !defined(__SOLARIS__) jbs 10/2007 10/2007 */
|