/*++
*******************************************************************************
* Copyright (c) 1990-2000 Digi International Inc., All Rights Reserved		  *
* 																			  *
* This software contains proprietary and confidential information of Digi	  *
* International Inc.  By accepting transfer of this copy, Recipient agrees	  *
* to retain this software in confidence, to prevent disclosure to others,	  *
* and to make no use of this software other than that for which it was		  *
* delivered.  This is an unpublished copyrighted work of Digi International	  *
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly	  *
* prohibited.																  *
* 																			  *
* Restricted Rights Legend													  *
*																			  *
* Use, duplication, or disclosure by the Government is subject to			  *
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in		  *
* Technical Data and Computer Software clause at DFARS 252.227-7031 or		  *
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -		  *
* Restricted Rights at 48 CFR 52.227-19, as applicable.						  *
*																			  *
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343		  *
*																			  *
*******************************************************************************

Module Name:

   lapbmib.c

Abstract:

    Physical Layer MIB SNMP Extension Agent for Windows NT.

    These files (lapbdll.c, lapbmib.c, and lapbmib.h) provide an 
    Extension Agent DLL which works in conjunction with the SNMP 
    Extendible Agent for Windows NT.

    This SNMP Extension Agent was derived from the sample on the Microsoft
    Developer's Toolkit. See the "Microsoft Windows/NT SNMP Programmer's Reference".

	This Extension Agent implements the Internet LAPB MIB. It's definition is 
	found in the IETF RFC 1381.

Author:

   Carl Caldwell

--*/
// #include <windows.h>
#include <snmp.h>
#include "regnames.h"
#include "dxbreg.h"

// Contains definitions for the table structure describing the MIB.  This
// is used in conjunction with testmib.c where the MIB requests are resolved.
#include "lapbmib.h"
#include "lapbsxb.h"

// Buffer for formatted Debug messages
char	DbgBuf[80];

// Handle used to control the data acquisition thread
extern HANDLE hSxbCollectionThread;

// Termination event handle
extern HANDLE	terminateEvent;

// Global Var that tells how many ports we are dealing with
AsnInteger portCount;

// Global Var that terminates data collection
BOOL	done = FALSE;

// If an addition or deletion to the MIB is necessary, there are several
// places in the code that must be checked and possibly changed.
//
// The last field in each MIB entry is used to point to the NEXT
// leaf variable.  If an addition or deletetion is made, these pointers
// may need to be updated to reflect the modification.

// The prefix to all of these MIB variables is 1.3.6.1.2.1.10
// {.iso.org.dod.internet.mgmt.mib.transmission.lapb
UINT OID_Prefix[] = { 1, 3, 6, 1, 2, 1, 10, 16 };
AsnObjectIdentifier MIB_OidPrefix = { OID_SIZEOF(OID_Prefix), OID_Prefix };

//                         //
// OID definitions for MIB //
//                         //
// Definition of the LAPB group

UINT MIB_lapb[]  = { 16 };

// Definition of leaf variables under the LAPB group
// All leaf variables have a zero appended to their OID to indicate
// that it is the only instance of this variable and it exists.

UINT MIB_lapbNumber[]              = { 1, 0 };

// The following definitions are those of the LAPB Admin table
// These entries are a little different in that they indicate
// The presence of the first configured port. If no ports are 
// configured, the DLL will fail to initialize. If more than one 
// port exists, they will be added during initialization by chaining
// columnwise to the previous port (i.e. port index 2 is the NEXT 
// MIB entry after port index 1, and port type 1 is the next MIB 
// entry after port index 2

UINT MIB_lapbAdmnIndex[]				= { 1, 1, 1,  1 };
UINT MIB_lapbAdmnStationType[]			= { 1, 1, 2,  1 };
UINT MIB_lapbAdmnControlField[]			= { 1, 1, 3,  1 };
UINT MIB_lapbAdmnTransmitN1FrameSize[]	= { 1, 1, 4,  1 };
UINT MIB_lapbAdmnReceiveN1FrameSize[]	= { 1, 1, 5,  1 };
UINT MIB_lapbAdmnTransmitKWindowSize[]	= { 1, 1, 6,  1 };
UINT MIB_lapbAdmnReceiveKWindowSize[]	= { 1, 1, 7,  1 };
UINT MIB_lapbAdmnN2RxmitCount[]			= { 1, 1, 8,  1 };
UINT MIB_lapbAdmnT1AckTimer[]			= { 1, 1, 9,  1 };
UINT MIB_lapbAdmnT2AckDelayTimer[]		= { 1, 1, 10, 1 };
UINT MIB_lapbAdmnT3DisconnectTimer[]	= { 1, 1, 11, 1 };
UINT MIB_lapbAdmnT4IdleTimer[]			= { 1, 1, 12, 1 };
UINT MIB_lapbAdmnActionInitiate[]		= { 1, 1, 13, 1 };
UINT MIB_lapbAdmnActionRecvDM[]			= { 1, 1, 14, 1 };

// The following definitions are those of the LAPB Operations table
// These entries are different, as well,  in that they indicate
// The presence of the first configured port. If no ports are 
// configured, the DLL will fail to initialize. If more than one 
// port exists, they will be added during initialization by chaining
// columnwise as described above.

UINT MIB_lapbOperIndex[]				= { 2, 1, 1,  1 };
UINT MIB_lapbOperStationType[]			= { 2, 1, 2,  1 };
UINT MIB_lapbOperControlField[]			= { 2, 1, 3,  1 };
UINT MIB_lapbOperTransmitN1FrameSize[]	= { 2, 1, 4,  1 };
UINT MIB_lapbOperReceiveN1FrameSize[]	= { 2, 1, 5,  1 };
UINT MIB_lapbOperTransmitKWindowSize[]	= { 2, 1, 6,  1 };
UINT MIB_lapbOperReceiveKWindowSize[]	= { 2, 1, 7,  1 };
UINT MIB_lapbOperN2RxmitCount[]			= { 2, 1, 8,  1 };
UINT MIB_lapbOperT1AckTimer[]			= { 2, 1, 9,  1 };
UINT MIB_lapbOperT2AckDelayTimer[]		= { 2, 1, 10, 1 };
UINT MIB_lapbOperT3DisconnectTimer[]	= { 2, 1, 11, 1 };
UINT MIB_lapbOperT4IdleTimer[]			= { 2, 1, 12, 1 };
UINT MIB_lapbOperPortId[]				= { 2, 1, 13, 1 };
UINT MIB_lapbOperProtocolVersion[]		= { 2, 1, 14, 1 };

// The following definitions are those of the LAPB flow table
// These entries are different, as well,  in that they indicate
// The presence of the first configured port. If no ports are 
// configured, the DLL will fail to initialize. If more than one 
// port exists, they will be added during initialization by chaining
// columnwise as described above.

UINT MIB_lapbFlowIndex[]				= { 3, 1, 1,  1 };
UINT MIB_lapbFlowStateChanges[]			= { 3, 1, 2,  1 };
UINT MIB_lapbFlowChangeReason[]			= { 3, 1, 3,  1 };
UINT MIB_lapbFlowCurrentMode[]			= { 3, 1, 4,  1 };
UINT MIB_lapbFlowBusyDefers[]			= { 3, 1, 5,  1 };
UINT MIB_lapbFlowRejOutPkts[]			= { 3, 1, 6,  1 };
UINT MIB_lapbFlowRejInPkts[]			= { 3, 1, 7,  1 };
UINT MIB_lapbFlowT1Timeouts[]			= { 3, 1, 8,  1 };
UINT MIB_lapbFlowFrmrSent[]				= { 3, 1, 9,  1 };
UINT MIB_lapbFlowFrmrReceived[]			= { 3, 1, 10, 1 };
UINT MIB_lapbFlowXidReceived[]			= { 3, 1, 11, 1 };

// The following definitions are those of the LAPB XID table
// These entries are different, as well,  in that they indicate
// The presence of the first configured port. If no ports are 
// configured, the DLL will fail to initialize. If more than one 
// port exists, they will be added during initialization by chaining
// columnwise as described above.

UINT MIB_lapbXidIndex[]						= { 4, 1, 1,  1 };
UINT MIB_lapbXidAdRIdentifier[]				= { 4, 1, 2,  1 };
UINT MIB_lapbXidParameterUniqueIdentifier[]	= { 4, 1, 3,  1 };
UINT MIB_lapbXidGroupAddress[]				= { 4, 1, 4,  1 };
UINT MIB_lapbXidPortNumber[]				= { 4, 1, 5,  1 };
UINT MIB_lapbXidUserDataSubfield[]			= { 4, 1, 6,  1 };

// The following MIB Entries identify the version of the protocol
UINT MIB_lapbProtocolVersion[]				= { 1, 3, 6, 1, 2, 1, 10, 16, 5 };
UINT MIB_lapbProtocolVersionIso7776V1986[]	= { 1, 3, 6, 1, 2, 1, 10, 16, 5, 1 };
UINT MIB_lapbProtocolVersionCcittV1980[]	= { 1, 3, 6, 1, 2, 1, 10, 16, 5, 2 };
UINT MIB_lapbProtocolVersionCcittV1984[]	= { 1, 3, 6, 1, 2, 1, 10, 16, 5, 3 };

// The following MIB Entries identify the version of the protocol
UINT MIB_wanPortIdentifier[] = {1, 3, 6, 1, 2, 1, 10, 33, 2, 1, 1, 1};
//                             //
// Storage definitions for MIB //
//                             //
AsnInteger MIB_lapbAdmnIndexStor				= 1;
AsnInteger MIB_lapbAdmnStationTypeStor			= LAPBMIB_STATION_TYPE_DTE;
AsnInteger MIB_lapbAdmnControlFieldStor			= LAPBMIB_CONTROL_FIELD_MOD_8;
AsnInteger MIB_lapbAdmnTransmitN1FrameSizeStor	= LAPBMIB_DEFAULT_FRAME_SIZE;
AsnInteger MIB_lapbAdmnReceiveN1FrameSizeStor	= LAPBMIB_DEFAULT_FRAME_SIZE;
AsnInteger MIB_lapbAdmnTransmitKWindowSizeStor	= LAPBMIB_DEFAULT_WINDOW_SIZE;
AsnInteger MIB_lapbAdmnReceiveKWindowSizeStor	= LAPBMIB_DEFAULT_WINDOW_SIZE;
AsnInteger MIB_lapbAdmnN2RxmitCountStor			= LAPBMIB_DEFAULT_RXMIT_COUNT;
AsnInteger MIB_lapbAdmnT1AckTimerStor			= LAPBMIB_DEFAULT_ACK_TIMER;
AsnInteger MIB_lapbAdmnT2AckDelayTimerStor		= LAPBMIB_DEFAULT_ACK_DELAY_TIMER;
AsnInteger MIB_lapbAdmnT3DisconnectTimerStor	= LAPBMIB_DEFAULT_ACK_DELAY_TIMER;
AsnInteger MIB_lapbAdmnT4IdleTimerStor			= LAPBMIB_DEFAULT_NO_TIMER;
AsnInteger MIB_lapbAdmnActionInitiateStor		= LAPBMIB_ACTION_INITIATE_SEND_SABM;
AsnInteger MIB_lapbAdmnActionRecvDMStor			= LAPBMIB_ACTION_RECEIVE_DM_SEND_SABM;

AsnInteger MIB_lapbOperIndexStor					= 1;
AsnInteger MIB_lapbOperStationTypeStor				= LAPBMIB_STATION_TYPE_DTE;
AsnInteger MIB_lapbOperControlFieldStor				= LAPBMIB_CONTROL_FIELD_MOD_8;
AsnInteger MIB_lapbOperTransmitN1FrameSizeStor		= LAPBMIB_DEFAULT_FRAME_SIZE;
AsnInteger MIB_lapbOperReceiveN1FrameSizeStor		= LAPBMIB_DEFAULT_FRAME_SIZE;
AsnInteger MIB_lapbOperTransmitKWindowSizeStor		= LAPBMIB_DEFAULT_WINDOW_SIZE;
AsnInteger MIB_lapbOperReceiveKWindowSizeStor		= LAPBMIB_DEFAULT_WINDOW_SIZE;
AsnInteger MIB_lapbOperN2RxmitCountStor				= LAPBMIB_DEFAULT_RXMIT_COUNT;
AsnInteger MIB_lapbOperT1AckTimerStor				= LAPBMIB_DEFAULT_ACK_TIMER;
AsnInteger MIB_lapbOperT2AckDelayTimerStor			= LAPBMIB_DEFAULT_ACK_DELAY_TIMER;
AsnInteger MIB_lapbOperT3DisconnectTimerStor		= LAPBMIB_DEFAULT_ACK_DELAY_TIMER;
AsnInteger MIB_lapbOperT4IdleTimerStor				= LAPBMIB_DEFAULT_NO_TIMER;
AsnObjectIdentifier MIB_lapbOperPortIdStor			= { OID_SIZEOF(MIB_wanPortIdentifier),
														MIB_wanPortIdentifier};
AsnObjectIdentifier MIB_lapbOperProtocolVersionStor	= { OID_SIZEOF(MIB_lapbProtocolVersionCcittV1984),
														MIB_lapbProtocolVersionCcittV1984};
AsnInteger MIB_lapbFlowIndexStor			= 1;
AsnCounter MIB_lapbFlowStateChangesStor		= 0;
AsnInteger MIB_lapbFlowChangeReasonStor		= LAPBMIB_FIRST_FLOW_XID_RECEIVED;
AsnInteger MIB_lapbFlowCurrentModeStor		= LAPBMIB_FLOW_MODE_DISCONNECTED;
AsnCounter MIB_lapbFlowBusyDefersStor		= 0;
AsnCounter MIB_lapbFlowRejOutPktsStor		= 0;
AsnCounter MIB_lapbFlowRejInPktsStor		= 0;
AsnCounter MIB_lapbFlowT1TimeoutsStor		= 0;
AsnOctetString MIB_lapbFlowFrmrSentStor		= {{0},0,FALSE};
AsnOctetString MIB_lapbFlowFrmrReceivedStor = {{0},0,FALSE};
AsnOctetString MIB_lapbFlowXidReceivedStor	= {{0},0,FALSE};

AsnInteger MIB_lapbXidIndexStor							= 1;
AsnOctetString MIB_lapbXidAdRIdentifierStor				= {{0},0,FALSE};
AsnOctetString MIB_lapbXidParameterUniqueIdentifierStor = {{0},0,FALSE};
AsnOctetString MIB_lapbXidGroupAddressStor				= {{0},0,FALSE};
AsnOctetString MIB_lapbXidPortNumberStor				= {{0},0,FALSE};
AsnOctetString MIB_lapbXidUserDataSubfieldStor			= {{0},0,FALSE};

// MIB definiton
MIB_ENTRY Mib[] = {
	{ { OID_SIZEOF(MIB_lapbAdmnIndex), MIB_lapbAdmnIndex},
	&MIB_lapbAdmnIndexStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[1] },   //Index 0

	{ { OID_SIZEOF(MIB_lapbAdmnStationType), MIB_lapbAdmnStationType},
	&MIB_lapbAdmnStationTypeStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[2] },   //Index 1

	{ { OID_SIZEOF(MIB_lapbAdmnControlField), MIB_lapbAdmnControlField},
	&MIB_lapbAdmnControlFieldStor, NULL,
	ASN_INTEGER, 
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[3] },   //Index 2

	{ { OID_SIZEOF(MIB_lapbAdmnTransmitN1FrameSize), MIB_lapbAdmnTransmitN1FrameSize},
	&MIB_lapbAdmnTransmitN1FrameSizeStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[4] },   //Index 3

	{ { OID_SIZEOF(MIB_lapbAdmnReceiveN1FrameSize), MIB_lapbAdmnReceiveN1FrameSize},
	&MIB_lapbAdmnReceiveN1FrameSizeStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[5] },   //Index 4

	{ { OID_SIZEOF(MIB_lapbAdmnTransmitKWindowSize), MIB_lapbAdmnTransmitKWindowSize},
	&MIB_lapbAdmnTransmitKWindowSizeStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[6] },   //Index 5

	{ { OID_SIZEOF(MIB_lapbAdmnReceiveKWindowSize), MIB_lapbAdmnReceiveKWindowSize},
	&MIB_lapbAdmnReceiveKWindowSizeStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[7] },   //Index 6

	{ { OID_SIZEOF(MIB_lapbAdmnN2RxmitCount), MIB_lapbAdmnN2RxmitCount},
	&MIB_lapbAdmnN2RxmitCount, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[8] },   //Index 7

	{ { OID_SIZEOF(MIB_lapbAdmnT1AckTimer), MIB_lapbAdmnT1AckTimer},
	&MIB_lapbAdmnT1AckTimerStor, NULL, 
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[9] },   //Index 8

	{ { OID_SIZEOF(MIB_lapbAdmnT2AckDelayTimer), MIB_lapbAdmnT2AckDelayTimer},
	&MIB_lapbAdmnT2AckDelayTimerStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[10] },   //Index 9

	{ { OID_SIZEOF(MIB_lapbAdmnT3DisconnectTimer), MIB_lapbAdmnT3DisconnectTimer},
	&MIB_lapbAdmnT3DisconnectTimerStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[11] },   //Index 10

	{ { OID_SIZEOF(MIB_lapbAdmnT4IdleTimer), MIB_lapbAdmnT4IdleTimer},
	&MIB_lapbAdmnT4IdleTimerStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[12] },   //Index 11

	{ { OID_SIZEOF(MIB_lapbAdmnActionInitiate), MIB_lapbAdmnActionInitiate},
	&MIB_lapbAdmnActionInitiateStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[13] },   //Index 12

	{ { OID_SIZEOF(MIB_lapbAdmnActionRecvDM), MIB_lapbAdmnActionRecvDM},
	&MIB_lapbAdmnActionRecvDMStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[14] },  //Index 13

	{ { OID_SIZEOF(MIB_lapbOperIndex), MIB_lapbOperIndex},
	&MIB_lapbOperIndexStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[15] },   //Index 14

	{ { OID_SIZEOF(MIB_lapbOperStationType), MIB_lapbOperStationType},
	&MIB_lapbOperStationTypeStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[16] },   //Index 15

	{ { OID_SIZEOF(MIB_lapbOperControlField), MIB_lapbOperControlField},
	&MIB_lapbOperControlFieldStor, NULL,
	ASN_INTEGER, 
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[17] },   //Index 16

	{ { OID_SIZEOF(MIB_lapbOperTransmitN1FrameSize), MIB_lapbOperTransmitN1FrameSize},
	&MIB_lapbOperTransmitN1FrameSizeStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[18] },   //Index 17

	{ { OID_SIZEOF(MIB_lapbOperReceiveN1FrameSize), MIB_lapbOperReceiveN1FrameSize},
	&MIB_lapbOperReceiveN1FrameSizeStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[19] },   //Index 18

	{ { OID_SIZEOF(MIB_lapbOperTransmitKWindowSize), MIB_lapbOperTransmitKWindowSize},
	&MIB_lapbOperTransmitKWindowSizeStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[20] },   //Index 19

	{ { OID_SIZEOF(MIB_lapbOperReceiveKWindowSize), MIB_lapbOperReceiveKWindowSize},
	&MIB_lapbOperReceiveKWindowSizeStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[21] },   //Index 20

	{ { OID_SIZEOF(MIB_lapbOperN2RxmitCount), MIB_lapbOperN2RxmitCount},
	&MIB_lapbOperN2RxmitCount, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[22] },   //Index 21

	{ { OID_SIZEOF(MIB_lapbOperT1AckTimer), MIB_lapbOperT1AckTimer},
	&MIB_lapbOperT1AckTimerStor, NULL, 
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[23] },   //Index 22

	{ { OID_SIZEOF(MIB_lapbOperT2AckDelayTimer), MIB_lapbOperT2AckDelayTimer},
	&MIB_lapbOperT2AckDelayTimerStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[24] },   //Index 23

	{ { OID_SIZEOF(MIB_lapbOperT3DisconnectTimer), MIB_lapbOperT3DisconnectTimer},
	&MIB_lapbOperT3DisconnectTimerStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[25] },   //Index 24

	{ { OID_SIZEOF(MIB_lapbOperT4IdleTimer), MIB_lapbOperT4IdleTimer},
	&MIB_lapbOperT4IdleTimerStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[26] },   //Index 25

	{ { OID_SIZEOF(MIB_lapbOperPortId), MIB_lapbOperPortId},
	&MIB_lapbOperPortIdStor, NULL,
	ASN_OBJECTIDENTIFIER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[27] },   //Index 26

	{ { OID_SIZEOF(MIB_lapbOperProtocolVersion), MIB_lapbOperProtocolVersion},
	&MIB_lapbOperProtocolVersionStor, NULL,
	ASN_OBJECTIDENTIFIER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[28] },  //Index 27

	{ { OID_SIZEOF(MIB_lapbFlowIndex), MIB_lapbFlowIndex},
	&MIB_lapbFlowIndexStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[29] },  //Index 28

	{ { OID_SIZEOF(MIB_lapbFlowStateChanges), MIB_lapbFlowStateChanges},
	&MIB_lapbFlowStateChangesStor, NULL,
	ASN_RFC1155_COUNTER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[30] },  //Index 29

	{ { OID_SIZEOF(MIB_lapbFlowChangeReason), MIB_lapbFlowChangeReason},
	&MIB_lapbFlowChangeReasonStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[31] },  //Index 30

	{ { OID_SIZEOF(MIB_lapbFlowCurrentMode), MIB_lapbFlowCurrentMode},
	&MIB_lapbFlowCurrentModeStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[32] },  //Index 31

	{ { OID_SIZEOF(MIB_lapbFlowBusyDefers), MIB_lapbFlowBusyDefers},
	&MIB_lapbFlowBusyDefersStor, NULL,
	ASN_RFC1155_COUNTER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[33] },  //Index 32

	{ { OID_SIZEOF(MIB_lapbFlowRejOutPkts), MIB_lapbFlowRejOutPkts},
	&MIB_lapbFlowRejOutPktsStor, NULL,
	ASN_RFC1155_COUNTER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[34] },  //Index 33

	{ { OID_SIZEOF(MIB_lapbFlowRejInPkts), MIB_lapbFlowRejInPkts},
	&MIB_lapbFlowRejInPkts, NULL,
	ASN_RFC1155_COUNTER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[35] },  //Index 34

	{ { OID_SIZEOF(MIB_lapbFlowT1Timeouts), MIB_lapbFlowT1Timeouts},
	&MIB_lapbFlowT1TimeoutsStor, NULL,
	ASN_RFC1155_COUNTER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[36] },  //Index 35

	{ { OID_SIZEOF(MIB_lapbFlowFrmrSent), MIB_lapbFlowFrmrSent},
	&MIB_lapbFlowFrmrSentStor, NULL,
	ASN_OCTETSTRING,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[37] },  //Index 36

	{ { OID_SIZEOF(MIB_lapbFlowFrmrReceived), MIB_lapbFlowFrmrReceived},
	&MIB_lapbFlowFrmrReceivedStor, NULL,
	ASN_OCTETSTRING,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[38] },  //Index 37

	{ { OID_SIZEOF(MIB_lapbFlowXidReceived), MIB_lapbFlowXidReceived},
	&MIB_lapbFlowXidReceivedStor, NULL,
	ASN_OCTETSTRING,
	MIB_ACCESS_READ, 
	MIB_leaf_func, NULL},  //Index 38 STOP HERE W/O XID

	{ { OID_SIZEOF(MIB_lapbXidIndex), MIB_lapbXidIndex},
	&MIB_lapbXidIndexStor, NULL,
	ASN_INTEGER,
	MIB_ACCESS_READ, 
	MIB_leaf_func, &Mib[40] },  //Index 39

	{ { OID_SIZEOF(MIB_lapbXidAdRIdentifier), MIB_lapbXidAdRIdentifier},
	&MIB_lapbXidAdRIdentifierStor, NULL,
	ASN_OCTETSTRING,
	MIB_ACCESS_READWRITE, 
	MIB_leaf_func, &Mib[41] },  //Index 40

	{ { OID_SIZEOF(MIB_lapbXidParameterUniqueIdentifier), MIB_lapbXidParameterUniqueIdentifier},
	&MIB_lapbXidParameterUniqueIdentifierStor, NULL,
	ASN_OCTETSTRING,
	MIB_ACCESS_READWRITE, 
	MIB_leaf_func, &Mib[42] },  //Index 41

	{ { OID_SIZEOF(MIB_lapbXidGroupAddress), MIB_lapbXidGroupAddress},
	&MIB_lapbXidGroupAddressStor, NULL,
	ASN_OCTETSTRING,
	MIB_ACCESS_READWRITE, 
	MIB_leaf_func, &Mib[43] },  //Index 42

	{ { OID_SIZEOF(MIB_lapbXidPortNumber), MIB_lapbXidPortNumber},
	&MIB_lapbXidPortNumberStor, NULL,
	ASN_OCTETSTRING,
	MIB_ACCESS_READWRITE, 
	MIB_leaf_func, &Mib[44] },  //Index 43

	{ { OID_SIZEOF(MIB_lapbXidUserDataSubfield), MIB_lapbXidUserDataSubfield},
	&MIB_lapbXidUserDataSubfieldStor, NULL,
	ASN_OCTETSTRING,
	MIB_ACCESS_READWRITE, 
	MIB_leaf_func, NULL },  //Index 44
};

UINT MIB_num_variables = sizeof Mib / sizeof( MIB_ENTRY );

//
// InitMibTableEntries
//    Initializes the Table Entries in a Mib from the registry. The
//    port table is initialized, followed by the sync port table. If
//    control leads were exposed by the driver, the In and Out signal 
//    tables would come next:
//
// Notes:
//    InitInSigTablesMibEntries, and InitOutSigTablesMibEntries
//    Must be added when the SXB driver exposes control leads via the
//    Ioctl Interface.
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT InitMibTableEntries(IN OUT PLAPB_SXB_INFO pLapbSxbInfo)
{
UINT            initStatus;
    // Initialize the four MIB elements
    initStatus = InitAdmnTableMibEntries(Mib, pLapbSxbInfo);
    if (!initStatus)
    { 
	    initStatus = InitOperTableMibEntries(Mib, pLapbSxbInfo);
	    if (!initStatus)
	    {
			initStatus = InitFlowTableMibEntries(Mib, pLapbSxbInfo);
		    if (!initStatus)
		    {
				initStatus = InitXidTableMibEntries(Mib, pLapbSxbInfo);
		    }
	    }
    }
    return initStatus;
}    

//
// InitAdmnTableMibEntries
//    Initializes the Admin Table Entries in the Mib from the registry.
//    The port table is initialized by appending columns to the vector
//    of port specfic Mib entries. In the initial data structuring stage,
//    The first port table entry (port index) links to the second entry.
//    This will be the case when only one port is active. However, when 
//    a second port is added, the second port index is chained to the first.
//    subsequently, the second row entry (port type) of the FIRST port becomes 
//    the NEXT mib entry of the first port. The table is contructed this way 
//    building and chaining columns (from the registry, initially) until all
//    port specific columns are intialized.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT InitAdmnTableMibEntries(PMIB_ENTRY pMibBase,
							 PLAPB_SXB_INFO pLapbSxbInfo)
{

    UINT	initStatus = SNMP_ERRORSTATUS_NOERROR;

    // Variables to be obtained from the registry
	AsnInteger	portNdx;

	// Pointers to the IOCTL structures used to collect data from the SXB interface
	// First IOCTL gets config data, second in chain gets flow table
	PLAPB_SXB_INFO	pSxbAdmnStats = pLapbSxbInfo;
    
    // Pointers used to manipulate Mib table as it is built
    MIB_ENTRY FAR * pCurLapbAdmnIndexMibEntry 
		= &Mib[LAPBMIB_FIRST_ADMN_NDX];
    
	MIB_ENTRY FAR * pCurLapbAdmnStationTypeMibEntry	
		= &Mib[LAPBMIB_FIRST_ADMN_STATION_TYPE];
    
	MIB_ENTRY FAR * pCurLapbAdmnControlFieldMibEntry 
		= &Mib[LAPBMIB_FIRST_ADMN_CONTROL_FIELD];
    
	MIB_ENTRY FAR * pCurLapbAdmnTransmitN1FrameSizeMibEntry
		= &Mib[LAPBMIB_FIRST_ADMN_TRANSMIT_N1_FRAME_SIZE];
    
	MIB_ENTRY FAR * pCurLapbAdmnReceiveN1FrameSizeMibEntry
		= &Mib[LAPBMIB_FIRST_ADMN_RECEIVE_N1_FRAME_SIZE];
    
	MIB_ENTRY FAR * pCurLapbAdmnTransmitKWindowSizeMibEntry
		= &Mib[LAPBMIB_FIRST_ADMN_TRANSMIT_K_WINDOW_SIZE];
    
	MIB_ENTRY FAR * pCurLapbAdmnReceiveKWindowSizeMibEntry
		= &Mib[LAPBMIB_FIRST_ADMN_RECEIVE_K_WINDOW_SIZE];
    
	MIB_ENTRY FAR * pCurLapbAdmnN2RxmitCountMibEntry
		= &Mib[LAPBMIB_FIRST_ADMN_N2_RXMIT_COUNT];
    
	MIB_ENTRY FAR * pCurLapbAdmnT1AckTimerMibEntry
		= &Mib[LAPBMIB_FIRST_ADMN_T1_ACK_TIMER];
    
	MIB_ENTRY FAR * pCurLapbAdmnT2AckDelayTimerMibEntry
		= &Mib[LAPBMIB_FIRST_ADMN_T2_ACK_DELAY_TIMER];
    
	MIB_ENTRY FAR * pCurLapbAdmnT3DisconnectTimerMibEntry
		= &Mib[LAPBMIB_FIRST_ADMN_T3_DISCONNECT_TIMER];
    
	MIB_ENTRY FAR * pCurLapbAdmnT4IdleTimerMibEntry
		= &Mib[LAPBMIB_FIRST_ADMN_T4_IDLE_TIMER];
    
	MIB_ENTRY FAR * pCurLapbAdmnActionInitiateMibEntry
		= &Mib[LAPBMIB_FIRST_ADMN_ACTION_INITIATE];
    
	MIB_ENTRY FAR * pCurLapbAdmnActionRecvDMMibEntry		
		= &Mib[LAPBMIB_FIRST_ADMN_RECV_DM];

    for (portNdx=1; portNdx <= portCount; portNdx++)
    {

		//// Initialize the Mib variables with the registry values
		// Initialize the Admn index
		*( (long *) pCurLapbAdmnIndexMibEntry->Storage ) = 
			pSxbAdmnStats->PortId;

		// Initialize the Control Field  variable mapping
		pCurLapbAdmnControlFieldMibEntry->pSxbRawData = 
			&pSxbAdmnStats->upData.pLapbConfigData->lapbAdmnControlField;

		// Initialize the transmit frame size  variable mapping
		pCurLapbAdmnTransmitN1FrameSizeMibEntry->pSxbRawData =
			&pSxbAdmnStats->upData.pLapbConfigData->lapbAdmnTransmitN1FrameSize;

		// Initialize the receive frame size variable mapping
		pCurLapbAdmnReceiveN1FrameSizeMibEntry->pSxbRawData = 
			&pSxbAdmnStats->upData.pLapbConfigData->lapbAdmnReceiveN1FrameSize;

		// Initialize the transmit window size variable mapping
		pCurLapbAdmnTransmitKWindowSizeMibEntry->pSxbRawData = 
			&pSxbAdmnStats->upData.pLapbConfigData->lapbAdmnTransmitKWindowsSize;
		
		// Initialize the receive window size variable mapping
		pCurLapbAdmnReceiveKWindowSizeMibEntry->pSxbRawData = 
			&pSxbAdmnStats->upData.pLapbConfigData->lapbAdmnReceiveKWindowsSize;

		// Initialize the retransmit count variable mapping
		pCurLapbAdmnN2RxmitCountMibEntry->pSxbRawData = 
			&pSxbAdmnStats->upData.pLapbConfigData->lapbAdmnN2RxmitCount;
		
		// Initialize the ack timer variable mapping
		pCurLapbAdmnT1AckTimerMibEntry->pSxbRawData = 
			&pSxbAdmnStats->upData.pLapbConfigData->lapbAdmnT1AckTimer;
		
		// Initialize the ack delay timer variable mapping
		pCurLapbAdmnT2AckDelayTimerMibEntry->pSxbRawData = 
			&pSxbAdmnStats->upData.pLapbConfigData->lapbAdmnT2AckDelayTimer;
		
		// Initialize the disconnect timer variable mapping
		pCurLapbAdmnT3DisconnectTimerMibEntry->pSxbRawData = 
			&pSxbAdmnStats->upData.pLapbConfigData->lapbAdmnT3DisconnectTimer;
		
		// Initialize the idle timer variable mapping
		pCurLapbAdmnT4IdleTimerMibEntry->pSxbRawData = 
			&pSxbAdmnStats->upData.pLapbConfigData->lapbAdmnT4IdleTimer;
		
		// Create a new column for the next port if not the last one.
		// The new column element will chain itself in after the existing
		// one, and will increment the last digit in the oid to reflect the
		// next indexed element in a particular row.
		if (portNdx  < portCount)
		{
			pCurLapbAdmnIndexMibEntry = CreateNextMibColFrom(pCurLapbAdmnIndexMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbAdmnIndexMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbAdmnStationTypeMibEntry = CreateNextMibColFrom(pCurLapbAdmnStationTypeMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbAdmnStationTypeMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}
			
			pCurLapbAdmnControlFieldMibEntry = CreateNextMibColFrom(
															pCurLapbAdmnControlFieldMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbAdmnControlFieldMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbAdmnTransmitN1FrameSizeMibEntry = CreateNextMibColFrom(
															pCurLapbAdmnTransmitN1FrameSizeMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbAdmnTransmitN1FrameSizeMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbAdmnReceiveN1FrameSizeMibEntry = CreateNextMibColFrom(
															pCurLapbAdmnReceiveN1FrameSizeMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbAdmnReceiveN1FrameSizeMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbAdmnTransmitKWindowSizeMibEntry = CreateNextMibColFrom(
															pCurLapbAdmnTransmitKWindowSizeMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbAdmnTransmitKWindowSizeMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbAdmnReceiveKWindowSizeMibEntry = CreateNextMibColFrom(
															pCurLapbAdmnReceiveKWindowSizeMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbAdmnReceiveKWindowSizeMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbAdmnN2RxmitCountMibEntry = CreateNextMibColFrom(
															pCurLapbAdmnN2RxmitCountMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbAdmnN2RxmitCountMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbAdmnT1AckTimerMibEntry = CreateNextMibColFrom(
															pCurLapbAdmnT1AckTimerMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbAdmnT1AckTimerMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbAdmnT2AckDelayTimerMibEntry = CreateNextMibColFrom(
															pCurLapbAdmnT2AckDelayTimerMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbAdmnT2AckDelayTimerMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbAdmnT3DisconnectTimerMibEntry = CreateNextMibColFrom(
															pCurLapbAdmnT3DisconnectTimerMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbAdmnT3DisconnectTimerMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbAdmnT4IdleTimerMibEntry = CreateNextMibColFrom(
															pCurLapbAdmnT4IdleTimerMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbAdmnT4IdleTimerMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbAdmnActionInitiateMibEntry = CreateNextMibColFrom(
															pCurLapbAdmnActionInitiateMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbAdmnActionInitiateMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbAdmnActionRecvDMMibEntry = CreateNextMibColFrom(
															pCurLapbAdmnActionRecvDMMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbAdmnActionRecvDMMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}


		} //end_if
		
		// Update the pointers into SXB IOCTL linked list
		// This list is paired: 1st IOCTL = stats, 2nd = data
		pSxbAdmnStats = (pSxbAdmnStats->pNextLapbSxbInfo)->pNextLapbSxbInfo;

    } //end_for

    return initStatus;
} //end initAdmnTableMibEntries

//
// InitOperTableMibEntries
//    Initializes the Operational Table Entries in the Mib from the registry.
//    The port table is initialized in the same manner as the LAPB Port 
//    Table described above.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT InitOperTableMibEntries(MIB_ENTRY FAR * pMibBase,
								PLAPB_SXB_INFO pLapbSxbInfo)
{
    UINT	initStatus = SNMP_ERRORSTATUS_NOERROR;
	UINT	lastObjectIDDigitIndex;
	UINT *	pObjectIDArray;
    
	// Variables to be obtained from the registry
	AsnInteger	portNdx;

	// Pointers to the IOCTL structures used to collect data from the SXB interface
	// First IOCTL gets stats, second in chain gets config data
	PLAPB_SXB_INFO	pSxbOperStats = pLapbSxbInfo;

    // Pointers used to manipulate Mib table as it is built
    MIB_ENTRY FAR * pCurLapbOperIndexMibEntry 
		= &Mib[LAPBMIB_FIRST_OPER_NDX];
    
	MIB_ENTRY FAR * pCurLapbOperStationTypeMibEntry	
		= &Mib[LAPBMIB_FIRST_OPER_STATION_TYPE];
    
	MIB_ENTRY FAR * pCurLapbOperControlFieldMibEntry 
		= &Mib[LAPBMIB_FIRST_OPER_CONTROL_FIELD];
    
	MIB_ENTRY FAR * pCurLapbOperTransmitN1FrameSizeMibEntry
		= &Mib[LAPBMIB_FIRST_OPER_TRANSMIT_N1_FRAME_SIZE];
    
	MIB_ENTRY FAR * pCurLapbOperReceiveN1FrameSizeMibEntry
		= &Mib[LAPBMIB_FIRST_OPER_RECEIVE_N1_FRAME_SIZE];
    
	MIB_ENTRY FAR * pCurLapbOperTransmitKWindowSizeMibEntry
		= &Mib[LAPBMIB_FIRST_OPER_TRANSMIT_K_WINDOW_SIZE];
    
	MIB_ENTRY FAR * pCurLapbOperReceiveKWindowSizeMibEntry
		= &Mib[LAPBMIB_FIRST_OPER_RECEIVE_K_WINDOW_SIZE];
    
	MIB_ENTRY FAR * pCurLapbOperN2RxmitCountMibEntry
		= &Mib[LAPBMIB_FIRST_OPER_N2_RXMIT_COUNT];
    
	MIB_ENTRY FAR * pCurLapbOperT1AckTimerMibEntry
		= &Mib[LAPBMIB_FIRST_OPER_T1_ACK_TIMER];
    
	MIB_ENTRY FAR * pCurLapbOperT2AckDelayTimerMibEntry
		= &Mib[LAPBMIB_FIRST_OPER_T2_ACK_DELAY_TIMER];
    
	MIB_ENTRY FAR * pCurLapbOperT3DisconnectTimerMibEntry
		= &Mib[LAPBMIB_FIRST_OPER_T3_DISCONNECT_TIMER];
    
	MIB_ENTRY FAR * pCurLapbOperT4IdleTimerMibEntry
		= &Mib[LAPBMIB_FIRST_OPER_T4_IDLE_TIMER];
    
	MIB_ENTRY FAR * pCurLapbOperPortIdMibEntry
		= &Mib[LAPBMIB_FIRST_OPER_PORT_ID];
    
	MIB_ENTRY FAR * pCurLapbOperProtocolVersionMibEntry		
		= &Mib[LAPBMIB_FIRST_OPER_PROTOCOL_VERSION];

	for (portNdx=1; portNdx <= portCount; portNdx++)
    {
		// Initialize the Oper index
		*( (long *) pCurLapbOperIndexMibEntry->Storage ) = 
			pSxbOperStats->PortId;

		// Initialize the Control Field  variable mapping
		pCurLapbOperControlFieldMibEntry->pSxbRawData = 
			&pSxbOperStats->upData.pLapbConfigData->lapbAdmnControlField;

		// Initialize the transmit frame size  variable mapping
		pCurLapbOperTransmitN1FrameSizeMibEntry->pSxbRawData =
			&pSxbOperStats->upData.pLapbConfigData->lapbAdmnTransmitN1FrameSize;

		// Initialize the receive frame size variable mapping
		pCurLapbOperReceiveN1FrameSizeMibEntry->pSxbRawData = 
			&pSxbOperStats->upData.pLapbConfigData->lapbAdmnReceiveN1FrameSize;

		// Initialize the transmit window size variable mapping
		pCurLapbOperTransmitKWindowSizeMibEntry->pSxbRawData = 
			&pSxbOperStats->upData.pLapbConfigData->lapbAdmnTransmitKWindowsSize;
		
		// Initialize the receive window size variable mapping
		pCurLapbOperReceiveKWindowSizeMibEntry->pSxbRawData = 
			&pSxbOperStats->upData.pLapbConfigData->lapbAdmnReceiveKWindowsSize;

		// Initialize the retransmit count variable mapping
		pCurLapbOperN2RxmitCountMibEntry->pSxbRawData = 
			&pSxbOperStats->upData.pLapbConfigData->lapbAdmnN2RxmitCount;
		
		// Initialize the ack timer variable mapping
		pCurLapbOperT1AckTimerMibEntry->pSxbRawData = 
			&pSxbOperStats->upData.pLapbConfigData->lapbAdmnT1AckTimer;
		
		// Initialize the ack delay timer variable mapping
		pCurLapbOperT2AckDelayTimerMibEntry->pSxbRawData = 
			&pSxbOperStats->upData.pLapbConfigData->lapbAdmnT2AckDelayTimer;
		
		// Initialize the disconnect timer variable mapping
		pCurLapbOperT3DisconnectTimerMibEntry->pSxbRawData = 
			&pSxbOperStats->upData.pLapbConfigData->lapbAdmnT3DisconnectTimer;
		
		// Initialize the idle timer variable mapping
		pCurLapbOperT4IdleTimerMibEntry->pSxbRawData = 
			&pSxbOperStats->upData.pLapbConfigData->lapbAdmnT4IdleTimer;
		
		// We must modify the last digit of the port id to match the Reigstry index
		lastObjectIDDigitIndex = 
			(((AsnObjectIdentifier *)(pCurLapbOperPortIdMibEntry->Storage))->idLength) - 1;
		pObjectIDArray =
			((AsnObjectIdentifier *)(pCurLapbOperPortIdMibEntry->Storage))->ids;
		pObjectIDArray[lastObjectIDDigitIndex] = pSxbOperStats->PortId;

		
		// Create a new column for the next port if not the last one.
		// The new column element will chain itself in after the existing
		// one, and will increment the last digit in the oid to reflect the
		// next indexed element in a particular row.
		if (portNdx  < portCount)
		{
			pCurLapbOperIndexMibEntry = CreateNextMibColFrom(pCurLapbOperIndexMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbOperIndexMibEntry == NULL)
			{
				////DebugBreak();
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbOperStationTypeMibEntry = CreateNextMibColFrom(pCurLapbOperStationTypeMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbOperStationTypeMibEntry == NULL)
			{
				////DebugBreak();
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}
			
			pCurLapbOperControlFieldMibEntry = CreateNextMibColFrom(
															pCurLapbOperControlFieldMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbOperControlFieldMibEntry == NULL)
			{
				////DebugBreak();
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbOperTransmitN1FrameSizeMibEntry = CreateNextMibColFrom(
															pCurLapbOperTransmitN1FrameSizeMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbOperTransmitN1FrameSizeMibEntry == NULL)
			{
				////DebugBreak();
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbOperReceiveN1FrameSizeMibEntry = CreateNextMibColFrom(
															pCurLapbOperReceiveN1FrameSizeMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbOperReceiveN1FrameSizeMibEntry == NULL)
			{
				//DebugBreak();
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbOperTransmitKWindowSizeMibEntry = CreateNextMibColFrom(
															pCurLapbOperTransmitKWindowSizeMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbOperTransmitKWindowSizeMibEntry == NULL)
			{
				//DebugBreak();
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbOperReceiveKWindowSizeMibEntry = CreateNextMibColFrom(
															pCurLapbOperReceiveKWindowSizeMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbOperReceiveKWindowSizeMibEntry == NULL)
			{
				//DebugBreak();
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbOperN2RxmitCountMibEntry = CreateNextMibColFrom(
															pCurLapbOperN2RxmitCountMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbOperN2RxmitCountMibEntry == NULL)
			{
				//DebugBreak();
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbOperT1AckTimerMibEntry = CreateNextMibColFrom(
															pCurLapbOperT1AckTimerMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbOperT1AckTimerMibEntry == NULL)
			{
				//DebugBreak();
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbOperT2AckDelayTimerMibEntry = CreateNextMibColFrom(
															pCurLapbOperT2AckDelayTimerMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbOperT2AckDelayTimerMibEntry == NULL)
			{
				//DebugBreak();
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbOperT3DisconnectTimerMibEntry = CreateNextMibColFrom(
															pCurLapbOperT3DisconnectTimerMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbOperT3DisconnectTimerMibEntry == NULL)
			{
				//DebugBreak();
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbOperT4IdleTimerMibEntry = CreateNextMibColFrom(
															pCurLapbOperT4IdleTimerMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbOperT4IdleTimerMibEntry == NULL)
			{
				//DebugBreak();
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbOperPortIdMibEntry = CreateNextMibColFrom(
															pCurLapbOperPortIdMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbOperPortIdMibEntry == NULL)
			{
				//DebugBreak();
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbOperProtocolVersionMibEntry = CreateNextMibColFrom(
															pCurLapbOperProtocolVersionMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbOperProtocolVersionMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				//DebugBreak();
				break;
			}

		} //end_if
		
		// Update the pointers into SXB IOCTL linked list
		// This list is paired: 1st IOCTL = stats, 2nd = data
		pSxbOperStats = (pSxbOperStats->pNextLapbSxbInfo)->pNextLapbSxbInfo;

    } //end_for

    return initStatus;
}	//end initOperPortTableMibEntries

//
// InitFlowTableMibEntries
//    Initializes the LAPB Flow Table Entries in the Mib from the registry.
//    The port table is initialized in the same manner as the LAPB 
//    Table described above.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT InitFlowTableMibEntries(MIB_ENTRY FAR * pMibBase,
							 PLAPB_SXB_INFO pLapbSxbInfo)
{
    UINT        initStatus = SNMP_ERRORSTATUS_NOERROR;

    // Variables to be initialized
    AsnInteger portNdx;
    
	// Pointers to the IOCTL structures used to collect data from the SXB interface
	// First IOCTL gets stats, second in chain gets config data

	PLAPB_SXB_INFO	pSxbFlowStats = pLapbSxbInfo->pNextLapbSxbInfo;

    // Pointers used to manipulate Mib table as it is built

    MIB_ENTRY FAR * pCurLapbFlowIndexMibEntry 
						= &Mib[LAPBMIB_FIRST_FLOW_INDEX];
    
	MIB_ENTRY FAR * pCurLapbFlowStateChangesMibEntry  
						= &Mib[LAPBMIB_FIRST_FLOW_STATE_CHANGES];
    
	MIB_ENTRY FAR * pCurLapbFlowChangeReasonMibEntry
						= &Mib[LAPBMIB_FIRST_FLOW_CHANGE_REASON];
    
	MIB_ENTRY FAR * pCurLapbFlowCurrentModeMibEntry 
						= &Mib[LAPBMIB_FIRST_FLOW_CURRENT_MODE];
    
	MIB_ENTRY FAR * pCurLapbFlowBusyDefersMibEntry
						= &Mib[LAPBMIB_FIRST_FLOW_BUSY_DEFERS];
    
	MIB_ENTRY FAR * pCurLapbFlowRejOutPktsMibEntry   
						= &Mib[LAPBMIB_FIRST_FLOW_REJ_OUT_PKTS];
    
	MIB_ENTRY FAR * pCurLapbFlowRejInPktsMibEntry
						= &Mib[LAPBMIB_FIRST_FLOW_REJ_IN_PKTS];
    
	MIB_ENTRY FAR * pCurLapbFlowT1TimeoutsMibEntry
						= &Mib[LAPBMIB_FIRST_FLOW_T1_TIMEOUTS];
    
	MIB_ENTRY FAR * pCurLapbFlowFrmrSentMibEntry
						= &Mib[LAPBMIB_FIRST_FLOW_FRMR_SENT];
    
	MIB_ENTRY FAR * pCurLapbFlowFrmrReceivedMibEntry
						= &Mib[LAPBMIB_FIRST_FLOW_FRMR_RECEIVED];
    
	MIB_ENTRY FAR * pCurLapbFlowXidReceivedEntry
						= &Mib[LAPBMIB_FIRST_FLOW_XID_RECEIVED];
    for (portNdx=1; portNdx <= portCount; portNdx++)
    {
	    
		////Initialize the Mib variables 
	    *( (long *) pCurLapbFlowIndexMibEntry->Storage ) = 
			pSxbFlowStats->PortId;

		pCurLapbFlowStateChangesMibEntry->pSxbRawData = 
			&pSxbFlowStats->upData.pLapbFlowData->lapbFlowStateChanges;

		pCurLapbFlowChangeReasonMibEntry->pSxbRawData = 
			&pSxbFlowStats->upData.pLapbFlowData->lapbFlowChangeReason;

		pCurLapbFlowCurrentModeMibEntry->pSxbRawData =
			&pSxbFlowStats->upData.pLapbFlowData->lapbFlowCurrentMode;

		pCurLapbFlowBusyDefersMibEntry->pSxbRawData = 
			&pSxbFlowStats->upData.pLapbFlowData->lapbFlowBusyDefers;

		pCurLapbFlowRejOutPktsMibEntry->pSxbRawData = 
			&pSxbFlowStats->upData.pLapbFlowData->lapbFlowRejOutPkts;

		pCurLapbFlowRejInPktsMibEntry->pSxbRawData = 
			&pSxbFlowStats->upData.pLapbFlowData->lapbFlowRejInPkts;

		pCurLapbFlowT1TimeoutsMibEntry->pSxbRawData = 
			&pSxbFlowStats->upData.pLapbFlowData->lapbFlowT1Timeouts;

		pCurLapbFlowFrmrSentMibEntry->pSxbRawData = 
			&pSxbFlowStats->upData.pLapbFlowData->lapbFlowFrmrSent;

		pCurLapbFlowFrmrReceivedMibEntry->pSxbRawData = 
			&pSxbFlowStats->upData.pLapbFlowData->lapbFlowFrmrReceived;

		pCurLapbFlowXidReceivedEntry->pSxbRawData = 
			&pSxbFlowStats->upData.pLapbFlowData->lapbFlowXidReceived;

        // Create a new column for the next port if not the last one.
	    // The new column element will chain itself in after the existing
	    // one, and will increment the port id digit in the oid to reflect the
	    // next indexed element in a particular row.
		if (portNdx  < portCount)
		{
			pCurLapbFlowIndexMibEntry = CreateNextMibColFrom(
															pCurLapbFlowIndexMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbFlowIndexMibEntry  == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbFlowStateChangesMibEntry = CreateNextMibColFrom(
															pCurLapbFlowStateChangesMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbFlowStateChangesMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}
			
			pCurLapbFlowChangeReasonMibEntry = CreateNextMibColFrom(
															pCurLapbFlowChangeReasonMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbFlowChangeReasonMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbFlowCurrentModeMibEntry = CreateNextMibColFrom(
															pCurLapbFlowCurrentModeMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbFlowCurrentModeMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbFlowBusyDefersMibEntry = CreateNextMibColFrom(
															pCurLapbFlowBusyDefersMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbFlowBusyDefersMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbFlowRejOutPktsMibEntry = CreateNextMibColFrom(
															pCurLapbFlowRejOutPktsMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbFlowRejOutPktsMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbFlowRejInPktsMibEntry = CreateNextMibColFrom(
															pCurLapbFlowRejInPktsMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbFlowRejInPktsMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbFlowT1TimeoutsMibEntry = CreateNextMibColFrom(
															pCurLapbFlowT1TimeoutsMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbFlowT1TimeoutsMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbFlowFrmrSentMibEntry = CreateNextMibColFrom(
															pCurLapbFlowFrmrSentMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbFlowFrmrSentMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbFlowFrmrReceivedMibEntry = CreateNextMibColFrom(
															pCurLapbFlowFrmrReceivedMibEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbFlowFrmrReceivedMibEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

			pCurLapbFlowXidReceivedEntry = CreateNextMibColFrom(
															pCurLapbFlowXidReceivedEntry, 
															1, 
															TRUE,
															NULL);
			// Check for successful completion
			if (pCurLapbFlowXidReceivedEntry == NULL)
			{
				initStatus = SNMP_ERRORSTATUS_GENERR;
				break;
			}

		} //end_if

		// Update the pointers into SXB IOCTL linked list
		// This list is paired: 1st IOCTL = config, 2nd = flow
		pSxbFlowStats = pSxbFlowStats->pNextLapbSxbInfo;
		if (pSxbFlowStats != NULL)
		{
			pSxbFlowStats = pSxbFlowStats->pNextLapbSxbInfo;
		}
    }	//end_while

    return initStatus;
} //end initFlowTableMibEntries   

//
// InitXidTableMibEntries
//    Initializes the XID table Entries in the Mib from the registry.
//    The port table is initialized in the same manner as the LAPB 
//    Table described above.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT InitXidTableMibEntries(MIB_ENTRY FAR * pMibBase,
							 PLAPB_SXB_INFO pLapbSxbInfo)
{
    UINT        initStatus = SNMP_ERRORSTATUS_NOERROR;

    // Variables to be initialized
    AsnInteger portNdx;
    
	// Pointers to the IOCTL structures used to collect data from the SXB interface
	// First IOCTL gets stats, second in chain gets config data

	PLAPB_SXB_INFO	pSxbStats	= pLapbSxbInfo;
	PLAPB_SXB_INFO	pSxbConfig	= pLapbSxbInfo->pNextLapbSxbInfo;

    // Pointers used to manipulate Mib table as it is built

    MIB_ENTRY FAR * pCurLapbXidIndexMibEntry 
		= &Mib[LAPBMIB_FIRST_XID_INDEX];
    
	MIB_ENTRY FAR * pCurLapbXidAdRIdentifierMibEntry 
		= &Mib[LAPBMIB_FIRST_XID_AD_R_IDENTIFIER];
    
	MIB_ENTRY FAR * pCurLapbXidParameterUniqueIdentifierMibEntry
		= &Mib[LAPBMIB_FIRST_XID_PARAMETER_UNIQUE_IDENTIFIER];

    MIB_ENTRY FAR * pCurLapbXidGroupAddressMibEntry
		= &Mib[LAPBMIB_FIRST_XID_GROUP_ADDRESS];

    MIB_ENTRY FAR * pCurLapbXidPortNumberMibEntry
		= &Mib[LAPBMIB_FIRST_XID_PORT_NUMBER];
    
	MIB_ENTRY FAR * pCurLapbXidUserDataSubfieldMibEntry
		= &Mib[LAPBMIB_FIRST_XID_USER_DATA_SUBFIELD];

    for (portNdx=1; portNdx <= portCount; portNdx++)
    {
    
	    //Initialize the Mib variables with the registry values
	    *( (long *) pCurLapbXidIndexMibEntry->Storage ) = 
			pSxbStats->PortId;

        // Create a new column for the next port if not the last one.
	    // The new column element will chain itself in after the existing
	    // one, and will increment the port id digit in the oid to reflect the
	    // next indexed element in a particular row.
	    if (portNdx  < portCount)
	    {   

            // Insert the next port ndx element
            pCurLapbXidIndexMibEntry = CreateNextMibColFrom(
														pCurLapbXidIndexMibEntry, 
														1, 
														TRUE, 
														NULL);
	        // Check for successful completion
	        if (pCurLapbXidIndexMibEntry == NULL)
	        {
    		    initStatus = SNMP_ERRORSTATUS_GENERR;
	    	    break;
	        }
	    

            // Insert the Addr Resolution Id
            pCurLapbXidAdRIdentifierMibEntry = CreateNextMibColFrom(
															pCurLapbXidAdRIdentifierMibEntry, 
															1, 
															TRUE,
															NULL);
	        // Check for successful completion
	        if (pCurLapbXidAdRIdentifierMibEntry == NULL)
	        {
    		    initStatus = SNMP_ERRORSTATUS_GENERR;
	    	    break;
	        }
	    

            // Insert the next Parameter Unique Id
            pCurLapbXidParameterUniqueIdentifierMibEntry = CreateNextMibColFrom(
														pCurLapbXidParameterUniqueIdentifierMibEntry, 
														1, 
														TRUE, 
														NULL);
	        // Check for successful completion
	        if (pCurLapbXidParameterUniqueIdentifierMibEntry == NULL)
	        {
    		    initStatus = SNMP_ERRORSTATUS_GENERR;
	    	    break;
	        }
	    
            // Insert the next group addr element
            pCurLapbXidGroupAddressMibEntry = CreateNextMibColFrom(
														pCurLapbXidGroupAddressMibEntry, 
														1, 
														TRUE, 
														NULL);
	        // Check for successful completion
	        if (pCurLapbXidGroupAddressMibEntry == NULL)
	        {
    		    initStatus = SNMP_ERRORSTATUS_GENERR;
	    	    break;
	        }
	    
            // Insert the next user data subfield element
            pCurLapbXidUserDataSubfieldMibEntry = CreateNextMibColFrom(
														pCurLapbXidUserDataSubfieldMibEntry, 
														1, 
														TRUE, 
														NULL);
	        // Check for successful completion
	        if (pCurLapbXidUserDataSubfieldMibEntry == NULL)
	        {
    		    initStatus = SNMP_ERRORSTATUS_GENERR;
	    	    break;
	        }
	    
	    }	// end_if

		// Update the pointers into SXB IOCTL linked list
		// This list is paired: 1st IOCTL = stats, 2nd = data
		pSxbStats	= pSxbConfig->pNextLapbSxbInfo;
		if (pSxbStats != NULL)
		{
			pSxbConfig	= pSxbStats->pNextLapbSxbInfo;
		}

    }	//end_while

    return initStatus;
}	//end initXidTableMibEntries

//
// CreateNextMibColFrom
//    Creates the nest mib column element in a row fraom an existing row
//    MIB element. It will bump the last oid digit to indicate a new column.
//    It will also create the necessary storage as well. If a NULL mib pointer
//    is returned the operation has failed.
//
// Notes:
//    The Adjust Index parameter is included to tell the utility which parameter
//    in a multi-dimensional array must be adjusted. A value of 1 means the last
//    digit. A value of 2 means the nex to last digit, and so on. The insertInChain
//    parameter will insert the new element into the mib structure. This is used when
//    adding a one dimensional column. If a 2 dimensional array is being expanded, the
//    calling routine must manage linkage on a vector by vector basis.
//
// Return Codes:
//    Pointer to a new Mib Element. NULL if failure.
//
// Error Codes:
//    None.
//
PMIB_ENTRY CreateNextMibColFrom(
                MIB_ENTRY FAR * pOldMibEntry, 
                UINT			AdjustIndex,
                BOOL			insertInChain,
				PVOID			pRawData
           )
{
    UINT            createStatus = 0;
    MIB_ENTRY FAR * pNewMibEntry;
    AsnOctetString * pSrcOctetString;
    AsnOctetString * pDstOctetString;
	AsnObjectIdentifier *	pSrcObjectIdentifier;
	AsnObjectIdentifier *	pDstObjectIdentifier;

    // Allocate memory for the new MIB_ENTRY
    pNewMibEntry = SNMP_malloc(sizeof(MIB_ENTRY));
  
    if (pNewMibEntry == NULL)
    {
		////DebugBreak();
	    return NULL;
    }
    
    // Allocate storage for the Oid
    pNewMibEntry->Oid.ids = SNMP_malloc(pOldMibEntry->Oid.idLength);
    if (pNewMibEntry->Oid.ids == NULL)
    {
		////DebugBreak();
	    SNMP_free (pNewMibEntry);
	    return NULL;
    }

    // Copy the oid from old to new, and bump the last digit
    createStatus = SNMP_oidcpy(&(pNewMibEntry->Oid), &(pOldMibEntry->Oid));

    if (createStatus != TRUE)
	{
#ifdef SNMP_DLL_DEBUG
		OutputDebugString("***E*** LAPB: CreateNewColumn Failed on OID Copy!\n");
#endif
		SNMP_free(pNewMibEntry);
		return NULL;
	}

    // Bump the last digit of the Oid up by one
    pNewMibEntry->Oid.ids[pNewMibEntry->Oid.idLength-AdjustIndex] += 1;

    // Copy over the Mib Entry's type field
    pNewMibEntry->Type = pOldMibEntry->Type;

    // Copy over the Mib Entry's access field
    pNewMibEntry->Access = pOldMibEntry->Access;

    // Copy over the Mib Entry's function pointer field
    pNewMibEntry->MibFunc = pOldMibEntry->MibFunc;

    // If required, chain the new column element into the Mib by:
    //   1. Setting New.NextMib = Old.NextMib
    //   2. Setting Old.NextMib = &New
    //
    if (insertInChain == TRUE)
    {
        pNewMibEntry->MibNext = pOldMibEntry->MibNext;
        pOldMibEntry->MibNext = pNewMibEntry;
    }

    // Copy the Oid Storage area from old to new
    switch (pOldMibEntry->Type) {
    
/*
typedef struct {
    UINT   idLength;            // number of integers in oid
    UINT * ids;                 // pointer to integer stream
} AsnObjectIdentifier;

*/
    case ASN_OBJECTIDENTIFIER:
        pSrcObjectIdentifier = (AsnObjectIdentifier *) pOldMibEntry->Storage;
	    pNewMibEntry->Storage = SNMP_malloc(sizeof(AsnObjectIdentifier));
		pDstObjectIdentifier = (AsnObjectIdentifier *) pNewMibEntry->Storage;
        // First figure out how much storage to add;

        pDstObjectIdentifier->idLength = pSrcObjectIdentifier->idLength;

        if (pSrcObjectIdentifier->ids != NULL )
        {
            if ( pSrcObjectIdentifier->idLength != 0 )
            {
                pDstObjectIdentifier->ids = 
                    SNMP_malloc(pSrcObjectIdentifier->idLength * sizeof(UINT));

                memcpy( pDstObjectIdentifier->ids,
	                    pSrcObjectIdentifier->ids,
	                    (pSrcObjectIdentifier->idLength * sizeof(UINT)) );
            }
            else
            {
                pDstObjectIdentifier->ids = NULL;
                pDstObjectIdentifier->idLength = 0;
            }
        }
        else
        {
            pDstObjectIdentifier->ids = NULL;
            pDstObjectIdentifier->idLength = 0;
        }

		break;

    case ASN_OCTETSTRING:
        pSrcOctetString = (AsnOctetString *) pOldMibEntry->Storage;
	    pNewMibEntry->Storage = SNMP_malloc(sizeof(AsnOctetString));
        pDstOctetString = (AsnOctetString *) pNewMibEntry->Storage;

        // First figure out how much storage to add;
        pDstOctetString->length = pSrcOctetString->length;
        if (pSrcOctetString->stream != NULL )
        {
            if ( pSrcOctetString->length != 0 )
            {
                pDstOctetString->length = pSrcOctetString->length;
                pDstOctetString->stream = 
                    SNMP_malloc(pSrcOctetString->length * sizeof(UINT));

                memcpy( pDstOctetString->stream,
	                    pSrcOctetString->stream,
	                    pSrcOctetString->length *sizeof (UINT));
            }
            else
            {
                pDstOctetString->stream = NULL;
                pDstOctetString->length = 0;
            }
        }
        else
        {
            pDstOctetString->stream = NULL;
            pDstOctetString->length = 0;
        }
        pDstOctetString->dynamic = pSrcOctetString->length;
        break;

    case ASN_RFC1155_TIMETICKS:
	case ASN_RFC1155_COUNTER:
    case ASN_INTEGER:
	    pNewMibEntry->Storage = SNMP_malloc(sizeof(AsnInteger));
	    *( (long *) pNewMibEntry->Storage ) = *( (long *) pOldMibEntry->Storage );
	    break;
    
    default:
		//DebugBreak();
	    createStatus = 0;
    	break;
    }

    if (!createStatus)
    {
	    SNMP_free (pNewMibEntry->Storage);
	    SNMP_oidfree (&(pNewMibEntry->Oid));
	    SNMP_free (pNewMibEntry);
	    pNewMibEntry = NULL;
    }

    return pNewMibEntry;
}    

//
// ResolveVarBind
//    Resolves a single variable binding.  Modifies the variable on a GET
//    or a GET-NEXT.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT ResolveVarBind(
	IN OUT RFC1157VarBind *VarBind, // Variable Binding to resolve
	IN UINT PduAction               // Action specified in PDU
	 )

{
MIB_ENTRY            *MibPtr;
AsnObjectIdentifier  TempOid;
int                  CompResult;
UINT                 I;
UINT                 nResult;

   // Search for var bind name in the MIB
   I      = 0;
   MibPtr = Mib;

   
   while ( MibPtr != NULL )
   {
      // Construct OID with complete prefix for comparison purposes
      SNMP_oidcpy( &TempOid, &MIB_OidPrefix );
      SNMP_oidappend( &TempOid, &(MibPtr->Oid) );

      // Check for OID in MIB - On a GET-NEXT the OID does not have to exactly
      // match a variable in the MIB, it must only fall under the MIB root.
      CompResult = SNMP_oidcmp( &VarBind->name, &TempOid );
      if ( CompResult < 0 )
	  {
	     // Since there is not an exact match, the only valid action is GET-NEXT
	     if ( PduAction  != MIB_ACTION_GETNEXT )
	     {
		nResult = SNMP_ERRORSTATUS_NOSUCHNAME;
		goto Exit;
	     }

	     // Since the match was not exact, but var bind name is within MIB,
	     // we are at the NEXT MIB variable down from the one specified.
	     PduAction = MIB_ACTION_GET;
	     
	 // Replace var bind name with new name
	 SNMP_oidfree( &VarBind->name );
	 SNMP_oidcpy( &VarBind->name, &MIB_OidPrefix );
	 SNMP_oidappend( &VarBind->name, &MibPtr->Oid );
	 break;
      }
      else
      {
	 if (CompResult == 0)
	     break;
      }

      // Free OID memory before checking another variable
      SNMP_oidfree( &TempOid );

      MibPtr = MibPtr->MibNext;
   } // while

   // If OID not within scope of MIB, then no such name
   if ( MibPtr == NULL )
   {
      nResult = SNMP_ERRORSTATUS_NOSUCHNAME;
      goto Exit;
   }

   // Call function to process request.  Each MIB entry has a function pointer
   // that knows how to process its MIB variable.
   nResult = (*MibPtr->MibFunc)( PduAction, MibPtr, VarBind );

   // Free temp memory
   SNMP_oidfree( &TempOid );

Exit:
   return nResult;
} // ResolveVarBind


//
// MIB_leaf_func
//    Performs generic actions on LEAF variables in the MIB.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT MIB_leaf_func(
	IN UINT Action,
	    IN MIB_ENTRY *MibPtr,
	    IN RFC1157VarBind *VarBind
	 )

{
UINT   ErrStat;

   switch ( Action )
   {
      case MIB_ACTION_GETNEXT:
	     // If there is no GET-NEXT pointer, this is the end of this MIB
	     if ( MibPtr->MibNext == NULL )
	     {
		ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
		goto Exit;
	     }

	 // Setup var bind name of NEXT MIB variable
	 SNMP_oidfree( &VarBind->name );
	 SNMP_oidcpy( &VarBind->name, &MIB_OidPrefix );
	 SNMP_oidappend( &VarBind->name, &MibPtr->MibNext->Oid );

	 // Call function to process request.  Each MIB entry has a function
	 // pointer that knows how to process its MIB variable.
	 ErrStat = (*MibPtr->MibNext->MibFunc)( MIB_ACTION_GET,
			MibPtr->MibNext, VarBind );
	 break;

      case MIB_ACTION_GET:
         // Make sure that this variable's ACCESS is GET'able
	     if ( MibPtr->Access != MIB_ACCESS_READ &&
		  MibPtr->Access != MIB_ACCESS_READWRITE )
	     {
	        ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
		    goto Exit;
	     }

	     // Setup varbind's return value
	     VarBind->value.asnType = MibPtr->Type;
	     switch ( VarBind->value.asnType )
	     {
	     case ASN_RFC1155_COUNTER:
	     case ASN_RFC1155_GAUGE:
	     case ASN_INTEGER:
			 if (MibPtr->pSxbRawData != NULL)
			 {
				 VarBind->value.asnValue.number = *((AsnInteger*)MibPtr->pSxbRawData);
			 }
			 else
			 {
				 VarBind->value.asnValue.number = *(AsnInteger *)(MibPtr->Storage);
			 }
		     break;

	      case ASN_OCTETSTRING: // This entails ASN_RFC1213_DISPSTRING also
		     VarBind->value.asnValue.string.length =
			  strlen( (LPSTR)MibPtr->Storage );

		       if ( NULL == 
			    (VarBind->value.asnValue.string.stream =
			    SNMP_malloc(VarBind->value.asnValue.string.length *
				   sizeof(char))) )
		       {
			  ErrStat = SNMP_ERRORSTATUS_GENERR;
			  goto Exit;
		       }

		       memcpy( VarBind->value.asnValue.string.stream,
			       (LPSTR)MibPtr->Storage,
			       VarBind->value.asnValue.string.length );
		       VarBind->value.asnValue.string.dynamic = TRUE;

		       break;

	      case ASN_OBJECTIDENTIFIER:
			VarBind->value.asnValue.object.idLength =
				((AsnObjectIdentifier *)MibPtr->Storage)->idLength;

			if ( NULL == 
			    (VarBind->value.asnValue.object.ids =
			    SNMP_malloc(VarBind->value.asnValue.object.idLength *
				   sizeof(UINT))) )
			{
				ErrStat = SNMP_ERRORSTATUS_GENERR;
				goto Exit;
			}

			memcpy( VarBind->value.asnValue.object.ids,
			       (UINT *)(((AsnObjectIdentifier*)MibPtr->Storage)->ids),
			       VarBind->value.asnValue.object.idLength * sizeof(UINT));
			break;

			default:
				ErrStat = SNMP_ERRORSTATUS_GENERR;
				goto Exit;
	       }
	       break;

      case MIB_ACTION_SET:
	 // Make sure that this variable's ACCESS is SET'able
#ifdef WRITE_IMPLEMENTED
		if ( MibPtr->Access != MIB_ACCESS_READWRITE &&
			MibPtr->Access != MIB_ACCESS_WRITE )
	    {
#endif
			ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
			goto Exit;
#ifdef WRITE_IMPLEMENTED
	    }

		// Check for proper type before setting
		if ( MibPtr->Type != VarBind->value.asnType )
	    {
			ErrStat = SNMP_ERRORSTATUS_BADVALUE;
			goto Exit;
	    }

	    // Save value in MIB
	    switch ( VarBind->value.asnType )
	    {

		case ASN_RFC1155_COUNTER:
		case ASN_INTEGER:
			*(AsnInteger *)(MibPtr->Storage) = VarBind->value.asnValue.number;
			break;

	    default:
			ErrStat = SNMP_ERRORSTATUS_GENERR;
			goto Exit;
	    }
#endif
		break;

      default:
	 ErrStat = SNMP_ERRORSTATUS_GENERR;
	     goto Exit;
   } // switch

   // Signal no error occurred
   ErrStat = SNMP_ERRORSTATUS_NOERROR;

Exit:
   return ErrStat;

} // end MIB_leaf_func


//
// MIB_cfg_leaf_func
//    Performs generic actions on config LEAF variables in the MIB.
//	The stats structure in LAPB is a UINT32 array. The Config structure
//	is uint16, however. This complicates the data collection process, because
//	ASN_INTEGER expects uint32 values from the RAW DATA pointer. So, a totally
//  separate function is needed to map 16 bit values to 32 bit values.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT MIB_cfg_leaf_func(
	IN UINT Action,
	    IN MIB_ENTRY *MibPtr,
	    IN RFC1157VarBind *VarBind
	 )

{
UINT   ErrStat;

   switch ( Action )
   {
      case MIB_ACTION_GETNEXT:
	     // If there is no GET-NEXT pointer, this is the end of this MIB
	     if ( MibPtr->MibNext == NULL )
	     {
		ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
		goto Exit;
	     }

	 // Setup var bind name of NEXT MIB variable
	 SNMP_oidfree( &VarBind->name );
	 SNMP_oidcpy( &VarBind->name, &MIB_OidPrefix );
	 SNMP_oidappend( &VarBind->name, &MibPtr->MibNext->Oid );

	 // Call function to process request.  Each MIB entry has a function
	 // pointer that knows how to process its MIB variable.
	 ErrStat = (*MibPtr->MibNext->MibFunc)( MIB_ACTION_GET,
			MibPtr->MibNext, VarBind );
	 break;

      case MIB_ACTION_GET:
         // Make sure that this variable's ACCESS is GET'able
	     if ( MibPtr->Access != MIB_ACCESS_READ &&
		  MibPtr->Access != MIB_ACCESS_READWRITE )
	     {
	        ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
		    goto Exit;
	     }

	     // Setup varbind's return value
	     VarBind->value.asnType = MibPtr->Type;
	     switch ( VarBind->value.asnType )
	     {
		 case ASN_RFC1155_COUNTER:
		 case ASN_RFC1155_GAUGE:
		 case ASN_INTEGER:
			 if (MibPtr->pSxbRawData != NULL)
			 {
				 VarBind->value.asnValue.number = (AsnInteger)*((uint16*)MibPtr->pSxbRawData);
			 }
			 else
			 {
				 VarBind->value.asnValue.number = *((AsnInteger*)MibPtr->Storage);
			 }
			 break;

		default:
				ErrStat = SNMP_ERRORSTATUS_GENERR;
				goto Exit;
		}
		break;

      case MIB_ACTION_SET:
	 // Make sure that this variable's ACCESS is SET'able
#ifdef WRITE_IMPLEMENTED
		if ( MibPtr->Access != MIB_ACCESS_READWRITE &&
			MibPtr->Access != MIB_ACCESS_WRITE )
	    {
#endif
			ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
			goto Exit;
#ifdef WRITE_IMPLEMENTED
	    }

		// Check for proper type before setting
		if ( MibPtr->Type != VarBind->value.asnType )
	    {
			ErrStat = SNMP_ERRORSTATUS_BADVALUE;
			goto Exit;
	    }

	    // Save value in MIB
	    switch ( VarBind->value.asnType )
	    {

		case ASN_RFC1155_COUNTER:
		case ASN_INTEGER:
			*(AsnInteger *)(MibPtr->Storage) = VarBind->value.asnValue.number;
			break;

	    default:
			ErrStat = SNMP_ERRORSTATUS_GENERR;
			goto Exit;
	    }
#endif
		break;

      default:
	 ErrStat = SNMP_ERRORSTATUS_GENERR;
	     goto Exit;
   } // switch

   // Signal no error occurred
   ErrStat = SNMP_ERRORSTATUS_NOERROR;

Exit:
   return ErrStat;

} // end MIB_cfg_leaf_func

//
// MIB_flow_mode_leaf_func
//    Performs actions on flow table mode LEAF variables in the MIB.
//	The stats structure in LAPB is a UINT32 array. The state value used
//	is uint8, however. This complicates the data collection process, because
//	ASN_INTEGER expects uint32 values from the RAW DATA pointer. So, a totally
//  separate function is needed to map 8 bit values to 32 bit values.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT MIB_flow_mode_leaf_func(
	IN UINT Action,
	    IN MIB_ENTRY *MibPtr,
	    IN RFC1157VarBind *VarBind
	 )

{
UINT	ErrStat;
uint8	flowState;

   switch ( Action )
   {
      case MIB_ACTION_GETNEXT:
	     // If there is no GET-NEXT pointer, this is the end of this MIB
	     if ( MibPtr->MibNext == NULL )
	     {
		ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
		goto Exit;
	     }

	 // Setup var bind name of NEXT MIB variable
	 SNMP_oidfree( &VarBind->name );
	 SNMP_oidcpy( &VarBind->name, &MIB_OidPrefix );
	 SNMP_oidappend( &VarBind->name, &MibPtr->MibNext->Oid );

	 // Call function to process request.  Each MIB entry has a function
	 // pointer that knows how to process its MIB variable.
	 ErrStat = (*MibPtr->MibNext->MibFunc)( MIB_ACTION_GET,
			MibPtr->MibNext, VarBind );
	 break;

      case MIB_ACTION_GET:
         // Make sure that this variable's ACCESS is GET'able
	     if ( MibPtr->Access != MIB_ACCESS_READ &&
		  MibPtr->Access != MIB_ACCESS_READWRITE )
	     {
	        ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
		    goto Exit;
	     }

	     // Setup varbind's return value
	     VarBind->value.asnType = MibPtr->Type;
	     switch ( VarBind->value.asnType )
	     {
		 case ASN_INTEGER:
			if (MibPtr->pSxbRawData != NULL)
			{
				flowState = *((uint8*)MibPtr->pSxbRawData);
				switch (flowState) {
				case LAPB_STATE_OFF:
					VarBind->value.asnValue.number = LAPBMIB_FLOW_MODE_OTHER;
					break;

				case LAPB_STATE_START:
					VarBind->value.asnValue.number = LAPBMIB_FLOW_MODE_LINK_SETUP;
					break;

				case LAPB_STATE_D_CONN:
					VarBind->value.asnValue.number = LAPBMIB_FLOW_MODE_DISCONNECTED;
					break;

				case LAPB_STATE_ADM:
					VarBind->value.asnValue.number = LAPBMIB_FLOW_MODE_DISCONNECTED;
					break;

				case LAPB_STATE_ARM:
					VarBind->value.asnValue.number = LAPBMIB_FLOW_MODE_INFO_XFER;
					break;

				case LAPB_STATE_POLLING:
					VarBind->value.asnValue.number = LAPBMIB_FLOW_MODE_WAITING_ACK;
					break;

				case LAPB_STATE_PRESETUP:
					VarBind->value.asnValue.number = LAPBMIB_FLOW_MODE_LINK_SETUP;
					break;

				case LAPB_STATE_SETUP:
					VarBind->value.asnValue.number = LAPBMIB_FLOW_MODE_LINK_SETUP;
					break;
				
				case LAPB_STATE_RESET:
					VarBind->value.asnValue.number = LAPBMIB_FLOW_MODE_OTHER;
					break;
				
				case LAPB_STATE_ERROR:
					VarBind->value.asnValue.number = LAPBMIB_FLOW_MODE_ERROR;
					break;

				case LAPB_STATE_NORMAL:
					VarBind->value.asnValue.number = LAPBMIB_FLOW_MODE_INFO_XFER;
					break;

				default:
					VarBind->value.asnValue.number = LAPBMIB_FLOW_MODE_OTHER;
					break;
				}
			 }
			 else
			 {
				 VarBind->value.asnValue.number = *((AsnInteger*)MibPtr->Storage);
			 }
			 break;

		default:
				ErrStat = SNMP_ERRORSTATUS_GENERR;
				goto Exit;
		}
		break;

      case MIB_ACTION_SET:
	 // Make sure that this variable's ACCESS is SET'able
#ifdef WRITE_IMPLEMENTED
		if ( MibPtr->Access != MIB_ACCESS_READWRITE &&
			MibPtr->Access != MIB_ACCESS_WRITE )
	    {
#endif
			ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
			goto Exit;
#ifdef WRITE_IMPLEMENTED
	    }

		// Check for proper type before setting
		if ( MibPtr->Type != VarBind->value.asnType )
	    {
			ErrStat = SNMP_ERRORSTATUS_BADVALUE;
			goto Exit;
	    }

	    // Save value in MIB
	    switch ( VarBind->value.asnType )
	    {

		case ASN_RFC1155_COUNTER:
		case ASN_INTEGER:
			*(AsnInteger *)(MibPtr->Storage) = VarBind->value.asnValue.number;
			break;

	    default:
			ErrStat = SNMP_ERRORSTATUS_GENERR;
			goto Exit;
	    }
#endif
		break;

      default:
	 ErrStat = SNMP_ERRORSTATUS_GENERR;
	     goto Exit;
   } // switch

   // Signal no error occurred
   ErrStat = SNMP_ERRORSTATUS_NOERROR;

Exit:
   return ErrStat;

} // end MIB_flow_mode_leaf_func

//
// lapb_Update_MIB_Data
//    Collects all the data needed to update the MIB structure
//
// Notes:
//      This routine is spawned as a thread from the init routine. It runs
//  off a timer tick in .1 second increments.
//
// Return Codes:
//
// Error Codes:
//
long WINAPI lapb_Update_MIB_Data(long pLapbSxbInfo)
{
	BOOL                bSuccess;
	PLAPB_SXB_INFO		pBaseLapbSxbInfo = (PLAPB_SXB_INFO)pLapbSxbInfo;
	PLAPB_SXB_INFO		pCurLapbSxbInfo;
	PX_IOCTL            pCurSxbIoctl;
	int					ndx=0;
	DWORD				returnStatus = 0;
	CHAR				curDevName[SXB_DEVICE_NAME_SIZE];
	BOOL				handleOpen = FALSE;
	UINT				retryCount;

	// Open a file handle to each port required
	if (( pCurLapbSxbInfo = pBaseLapbSxbInfo ) == NULL ) {
		//DebugBreak();
		returnStatus = -1;
		goto Exit;
	}
	
	while (pCurLapbSxbInfo != NULL)
	{
		// APP FILE HANDLE OPEN:
		// Open an application file handle to the SXB driver
		strcpy(curDevName,pCurLapbSxbInfo->DeviceName);

#ifdef SNMP_DLL_DEBUG
		wsprintf(DbgBuf,"***I*** Opening file for port %s\n",curDevName);
		OutputDebugString(DbgBuf);
#endif

        if( (pCurLapbSxbInfo->hSxbPort = 
                    CreateFile(  pCurLapbSxbInfo->DeviceName,
			                     GENERIC_READ | GENERIC_WRITE,
			                     FILE_SHARE_READ | FILE_SHARE_WRITE,
			                     NULL, OPEN_EXISTING,
			                     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
			                     NULL )) == INVALID_HANDLE_VALUE )
        {
			returnStatus = GetLastError();
			wsprintf(DbgBuf,
					"***E*** LAPB: Couldn't open file handle to SXB adapter, Error = 0x%x\n",
					returnStatus);
			goto Exit;
        }

		// SXB INTERNAL HANDLE OPEN:
		// First, Open a USER PORT inside SXB for IOCTL use
		bSuccess = FALSE;
		retryCount = 0;
		while (!bSuccess && (retryCount < 5))
		{
			DS_OPEN Open;
			DWORD dwBytes;
			ZeroMemory( &Open, sizeof( Open ) );
			strcpy( (PVOID)&Open.DeviceName, ID_STR_SXB_LAPB_STATS );
			strcpy(curDevName,pCurLapbSxbInfo->DeviceName);
			bSuccess = DeviceIoControl( pCurLapbSxbInfo->hSxbPort,
										(ULONG)IOCTL_DS_OPEN,
										&Open, sizeof( Open ),
										&Open, sizeof( Open ),
										&dwBytes, NULL );
			pCurLapbSxbInfo->VCLapbDevice = Open.hStream;
			if( !bSuccess || Open.ErrorCode )
			{
				returnStatus = GetLastError();
#ifdef SNMP_DLL_DEBUG
				wsprintf(DbgBuf,
				         "***E*** LAPB: IOCTL_DS_OPEN failed 0x%x, ErrorCode 0x%x\n",
								returnStatus, Open.ErrorCode);
				OutputDebugString(DbgBuf);
#endif
				retryCount++;
			}
			SleepEx(20,FALSE);
		}

		if (!bSuccess)
			goto Exit;

		// ACCOUNTING FOR several IOCTLs PER FREL PORT
        // You need to init each subsequent IOCTL in each group. It is the same
		// Port handle as the first
		do
		{
			if ((pCurLapbSxbInfo = pCurLapbSxbInfo->pNextLapbSxbInfo) != NULL)
			{
#ifdef SNMP_DLL_DEBUG
				wsprintf(DbgBuf,
						"Current Port Device Name='%s', Compare Device Name = '%s'\n", 
						curDevName,
						pCurLapbSxbInfo->DeviceName);
				OutputDebugString(DbgBuf);
#endif

				if (strcmp(curDevName,pCurLapbSxbInfo->DeviceName) == 0)
				{
					pCurLapbSxbInfo->hSxbPort = 
						pCurLapbSxbInfo->pPreviousLapbSxbInfo->hSxbPort;
					pCurLapbSxbInfo->VCLapbDevice =
						pCurLapbSxbInfo->pPreviousLapbSxbInfo->VCLapbDevice;
				}
			}
			else
			{
				break;
			}
		} while ((pCurLapbSxbInfo != NULL) && 
				(strcmp(curDevName,pCurLapbSxbInfo->DeviceName) == 0));
	}
	
	// If the LAPB User ports opened okay, start collecting stats
	ndx = 0;
	do
	{

 		pCurLapbSxbInfo = pBaseLapbSxbInfo; 

		while (pCurLapbSxbInfo != 0)
		{
			//
			// Okay, lets send down the requests for stats and config!
			//
			pCurSxbIoctl = &(pCurLapbSxbInfo->XIoctl);
			// Do IOCTL specific init
			if (pCurLapbSxbInfo->IoctlType == PORT_IOCTL_NDX_FLOW)
			{
				pCurSxbIoctl->DsIoctl.Command = RFC_1381_FLOW;
				pCurLapbSxbInfo->upData.pLapbFlowData = (PVOID)&(pCurSxbIoctl->Data);
				pCurLapbSxbInfo->upData.pLapbFlowData->lapbFlowIndex = pCurLapbSxbInfo->Snid;
				pCurSxbIoctl->DsIoctl.cbDataIn = pCurSxbIoctl->DsIoctl.cbMaxDataOut = sizeof(struct rfc_1381_flow_struct);
			}
			else // The config ioctl comes first in the linked list
			{
				pCurSxbIoctl->DsIoctl.Command = RFC_1381_ADMN;
				pCurLapbSxbInfo->upData.pLapbConfigData = (PVOID)&(pCurSxbIoctl->Data);
				pCurLapbSxbInfo->upData.pLapbConfigData->lapbAdmnIndex = pCurLapbSxbInfo->Snid;
				pCurSxbIoctl->DsIoctl.cbDataIn = pCurSxbIoctl->DsIoctl.cbMaxDataOut = sizeof(struct rfc_1381_admn_struct);
			}

			// Do IOCTL independent init
			pCurSxbIoctl->DsIoctl.hStream = pCurLapbSxbInfo->VCLapbDevice;
			pCurSxbIoctl->DsIoctl.pDataIn = pCurSxbIoctl->DsIoctl.pDataOut = (PVOID)&pCurSxbIoctl->Data;
			
			// Now that we've built an IOCTL, send it down to SXB
			bSuccess = DeviceIoControl( pCurLapbSxbInfo->hSxbPort,
										(ULONG)IOCTL_DS_IOCTL,
										&pCurSxbIoctl->DsIoctl, sizeof(DS_IOCTL),
										&pCurSxbIoctl->DsIoctl, sizeof(DS_IOCTL),
										&(pCurLapbSxbInfo->dwBytesWritten), NULL );
			if( !bSuccess  )
			{
				returnStatus = GetLastError();
#ifdef SNMP_DLL_DEBUG
				wsprintf(DbgBuf,"***E*** LAPB: Failure on USER IOCTL = %x\n",
								returnStatus);
				OutputDebugString(DbgBuf);
#endif
			}
			else
			{
				if ( pCurSxbIoctl->DsIoctl.ReturnValue || 
				     pCurSxbIoctl->DsIoctl.ErrorCode )
				{
#ifdef SNMP_DLL_DEBUG
					wsprintf(DbgBuf,"***E*** LAPB: IOCTL failed, 0x%x, Error 0x%x\n",
									pCurSxbIoctl->DsIoctl.ReturnValue,
									pCurSxbIoctl->DsIoctl.ErrorCode);
					OutputDebugString(DbgBuf);
#endif
				}
				else
				{
				}
			}
			// Go to the next IOCTL
			pCurLapbSxbInfo = pCurLapbSxbInfo->pNextLapbSxbInfo;
		} //end_while

		// Here we need to wait on the timer tick to schedule the next update cycle
		SleepEx(500,FALSE);
	
	} while ( WaitForSingleObject( terminateEvent, 0) != WAIT_OBJECT_0);

Exit:
	return returnStatus;
}  // end DoLapbStats

//
// lapb_Terminate_MIB_Data_Collection
//    Collects all the data needed to update the MIB structure
//
// Notes:
//      This routine is spawned as a thread from the init routine. It runs
//  off a timer tick in .1 second increments.
//
// Return Codes:
//
// Error Codes:
//
void lapb_Terminate_MIB_Data_Collection(PLAPB_SXB_INFO pBaseLapbSxbInfo,
										 PSXB_PORT_INFO pBaseSxbPortInfo)
{
BOOL				bSuccess;
BOOL				firstPassFlag;
PLAPB_SXB_INFO		pDelLapbSxbInfo;
PLAPB_SXB_INFO		pCurLapbSxbInfo;
HANDLE				hSxbDevice = NULL;       
PSXB_CIRCUIT_INFO	pCircuitInfo;
PSXB_CIRCUIT_INFO	pNextCircuitInfo;
PSXB_PORT_INFO		pCurSxbPortInfo;
PSXB_PORT_INFO		pDelSxbPortInfo;
DWORD				returnStatus = 0;

	// Signal to stop
	SetEvent(terminateEvent); //This will notify the collection threads

	// Wait on the data collector thread to terminate
	returnStatus = WaitForSingleObject(hSxbCollectionThread,1000);
	switch (returnStatus) {
	case WAIT_OBJECT_0:
#ifdef SNMP_DLL_DEBUG
		wsprintf(DbgBuf,"***I*** LAPB: Wait for collection terminate completed\n");
		OutputDebugString(DbgBuf);
#endif
		break;
	
	case WAIT_TIMEOUT:
#ifdef SNMP_DLL_DEBUG
		wsprintf(DbgBuf,"***E*** LAPB: Wait for collection terminate timed out\n");
		OutputDebugString(DbgBuf);
#endif
		break;
	
	case WAIT_ABANDONED:
	default:
#ifdef SNMP_DLL_DEBUG
		wsprintf(DbgBuf,
				"***E*** LAPB: Wait for collection terminate failed, error=%x\n",
				GetLastError()
				);
		OutputDebugString(DbgBuf);
#endif
		break;
	}

 	if ((pCurLapbSxbInfo = pBaseLapbSxbInfo) == NULL) {
#ifdef SNMP_DLL_DEBUG
	    OutputDebugString("***E*** LAPB: lapb_Terminate_MIB_Data_Collection Failure\n");
#endif
		return;
	}
    
	firstPassFlag = TRUE;
	while (pCurLapbSxbInfo != NULL)
    {
		if (hSxbDevice != pCurLapbSxbInfo->hSxbPort)
		{
			DS_CLOSE Close;
			Close.hStream = pCurLapbSxbInfo->VCLapbDevice;
			hSxbDevice = pCurLapbSxbInfo->hSxbPort;

			// Close the handle depending on the snid
			bSuccess = DeviceIoControl( hSxbDevice,
										(ULONG)IOCTL_DS_CLOSE,
										&Close, sizeof( Close ),
										&Close, sizeof( Close ),
										&(pCurLapbSxbInfo->dwBytesWritten),
										NULL );

			if( !bSuccess  )
			{
				// There was some sort of problem opening the device!
				// Make him try it again
				returnStatus = GetLastError();
#ifdef SNMP_DLL_DEBUG
				wsprintf(DbgBuf,"***E*** LAPB: Failure on IOCTL_S_CLOSE\n= %x\n",
								returnStatus);
				OutputDebugString(DbgBuf);
#endif
				hSxbDevice = NULL;
			}
			else
			{
				CloseHandle( pCurLapbSxbInfo->hSxbPort);
			}
		} // end if
		pCurLapbSxbInfo->hSxbPort = NULL;
		pDelLapbSxbInfo = pCurLapbSxbInfo;
		pCurLapbSxbInfo = pCurLapbSxbInfo->pNextLapbSxbInfo;

		if (firstPassFlag)
		{
			// Don't free the first SXB Info structure. It was a global var
			firstPassFlag = FALSE;
		}
		else
		{
			// Free the other SXB Info structures. They were allocated
			SNMP_free(pDelLapbSxbInfo);
		}
	} // end while

	// Delete the SxbPortInfo structures
	pCurSxbPortInfo = pBaseSxbPortInfo;
	while (pCurSxbPortInfo != NULL)
	{
		// Delete the circuit tables for each port
		pCircuitInfo = pCurSxbPortInfo->pCircuitList;
		while (pCircuitInfo !=NULL)
		{
			pNextCircuitInfo = pCircuitInfo->pNextSxbCircuitInfo;
			SNMP_free(pCircuitInfo);
			pCircuitInfo = pNextCircuitInfo;
		}

		// Release the port Info structures
		pDelSxbPortInfo = pCurSxbPortInfo;
		pCurSxbPortInfo = pCurSxbPortInfo->pNextSxbPortInfo;
		SNMP_free (pDelSxbPortInfo);
	}
}  // end 

//
// lapb_Init_MIB_Data_Structures
//    Collects all the data needed to update the MIB structure
//
// Notes:
//      This routine is spawned as a thread from the init routine. It runs
//  off a timer tick in .1 second increments.
//
// Return Codes:
//
// Error Codes:
//
long lapb_Init_MIB_Data_Structures(
					IN PLAPB_SXB_INFO pLapbSxbInfo,
					IN PSXB_PORT_INFO pSxbPortInfo
					)
{
   
	PLAPB_SXB_INFO   pCurLapbSxbInfo;
	PSXB_PORT_INFO  pCurSxbPortInfo;
	BOOL			firstTimeThrough = TRUE;

	if ((pCurLapbSxbInfo = pLapbSxbInfo) == NULL)
	{
		//DebugBreak();
		return -1;
	}

	if ((pCurSxbPortInfo = pSxbPortInfo) == NULL)
	{
		//DebugBreak();
		return -1;
	}

    // Initialize the LapbSxbInfo structures to access the Data Structures

	// The first list item has no previous list item
	pCurLapbSxbInfo->pPreviousLapbSxbInfo = NULL;

	while (pCurSxbPortInfo != NULL)
	{
		// Build the first one for admn/oper cnfg
		strcpy(pCurLapbSxbInfo->DeviceName, "\\\\.\\");
		strcat(pCurLapbSxbInfo->DeviceName, pCurSxbPortInfo->DeviceName);
		pCurLapbSxbInfo->IoctlType = PORT_IOCTL_NDX_CONFIG;
		pCurLapbSxbInfo->AdapterId = 0;
		pCurLapbSxbInfo->PortId = pCurSxbPortInfo->WANLinksPortId;
		pCurLapbSxbInfo->Snid = pCurSxbPortInfo->Snid;
		pCurLapbSxbInfo->upData.pLapbConfigData = (PVOID)&(pCurLapbSxbInfo->XIoctl.Data);
		//The lapb statistics for the port are identified by LAPB_PORT_NAME
		StrToWStr(  pCurLapbSxbInfo->LapbName,
					ID_STR_SXB_LAPB_STATS,
					strlen(ID_STR_SXB_LAPB_STATS) + 1 );

		// Allocate the first config IOCTL
		pCurLapbSxbInfo->pNextLapbSxbInfo = SNMP_malloc(sizeof(struct LAPB_SXB_INFO));
		if (pCurLapbSxbInfo->pNextLapbSxbInfo == NULL )
		{
#ifdef SNMP_DLL_DEBUG
			OutputDebugString("***E*** LAPB: Couldn't Allocate Memory!\n");
#endif
			return 4;
		}

		// Go ahead and init the previous pointer for the CONFIG IOCTL
		pCurLapbSxbInfo->pNextLapbSxbInfo->pPreviousLapbSxbInfo = pCurLapbSxbInfo;

		// Build the first one for flow
		pCurLapbSxbInfo = pCurLapbSxbInfo->pNextLapbSxbInfo;

		pCurLapbSxbInfo->IoctlType = PORT_IOCTL_NDX_FLOW;
		strcpy(pCurLapbSxbInfo->DeviceName, "\\\\.\\");
		strcat(pCurLapbSxbInfo->DeviceName, pCurSxbPortInfo->DeviceName);
		pCurLapbSxbInfo->AdapterId = 0;
		pCurLapbSxbInfo->PortId = pCurSxbPortInfo->WANLinksPortId;
		pCurLapbSxbInfo->Snid = pCurSxbPortInfo->Snid;
		pCurLapbSxbInfo->upData.pLapbFlowData = (PVOID)&(pCurLapbSxbInfo->XIoctl.Data);

		//The lapb statistics for the port are identified by LAPB_PORT_NAME
		StrToWStr(  pCurLapbSxbInfo->LapbName,
					ID_STR_SXB_LAPB_STATS,
					strlen(ID_STR_SXB_LAPB_STATS) + 1 );

		// Advance to the next Port Info Object after the config IOCTL
		pCurSxbPortInfo = pCurSxbPortInfo->pNextSxbPortInfo;

		if (pCurSxbPortInfo != NULL)
		{
			pCurLapbSxbInfo->pNextLapbSxbInfo = SNMP_malloc(sizeof(struct LAPB_SXB_INFO));
			if (pCurLapbSxbInfo->pNextLapbSxbInfo == NULL )
			{
#ifdef SNMP_DLL_DEBUG
				OutputDebugString("***E*** LAPB: Couldn't Allocate Memory!\n");
#endif
				return 4;
			}

			// Go ahead and init the previous pointer for the port 2 stats IOCTL
			pCurLapbSxbInfo->pNextLapbSxbInfo->pPreviousLapbSxbInfo = pCurLapbSxbInfo;
		}
		else
		{
				pCurLapbSxbInfo->pNextLapbSxbInfo = NULL;
		}

		// Work on the next Config Ioctl
		pCurLapbSxbInfo = pCurLapbSxbInfo ->pNextLapbSxbInfo; 
	}
	return 0;
} //end lapb_Init_MIB_Data_Structures()

//
// lapb_Init_MIB_Data_Structures
//    Initialize the SXB port information from the registry
//
// Notes:
//      The \\HKEY_LOCAL_MACHINE\Sotware\Microsoft\SNMP\Parameters\LapbLinksHDLCMIB\
//  key will be used to enumerate adaters and their ports to collect information
//	needed to build the port info structure list. This list is used to build the 
//	MIB and IOCTL list as well.
//
// Return Codes:
//
// Error Codes:
//
long lapb_Init_Sxb_Port_Info(
		IN PSXB_PORT_INFO pSxbPortInfo
		)
{
	HKEY			hKeyMib;
	HKEY			hKeyAdapter;
	HKEY			hKeyPort;
    DWORD			retCodeAdapter;
	DWORD			retCodePort;
	DWORD			retCodePortValue;
	CHAR			AdapterKeyName[MAX_PATH];
	CHAR			AdapterKeyPathName[MAX_PATH];
	CHAR			PortKeyName[MAX_PATH];
	CHAR			PortKeyPathName[MAX_PATH];
	int				i;
	int				j;
	int				k;
	BYTE *			pbData;
	DWORD			cbData;
	CHAR			ValueName[MAX_VALUE_NAME];
	DWORD			cbValueName = MAX_VALUE_NAME;
	DWORD			dwType;
	BOOL			firstTimeThrough = TRUE;
	PSXB_PORT_INFO	pCurSxbPortInfo = pSxbPortInfo;
	BOOL			portIsLapb = FALSE;
	int				lapbPortCount = 0;
	int				totalPortCount = 0;
	ULONG           Snid = 0;

	CHAR			WANLinksParamtersPath[MAX_PATH];

	//Build the pathname to the WANLinks SNMP Info
	strcpy(WANLinksParamtersPath, c_pszNTServiceBase);
	strcat(WANLinksParamtersPath,"\\SNMP\\Parameters\\");
	strcat(WANLinksParamtersPath, c_pszSNMP_RS232);


	// Open the key to the LAPB Links SNMP area
	retCodeAdapter = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
							WANLinksParamtersPath,
							0,
							KEY_ENUMERATE_SUB_KEYS |
							KEY_EXECUTE |
							KEY_QUERY_VALUE,
							&hKeyMib);

	if (retCodeAdapter != ERROR_SUCCESS)
	{
		//DebugBreak();
		return -1;
	}

	// Walk the Adapter sub-keys
	for (i=0, retCodeAdapter = ERROR_SUCCESS; retCodeAdapter == ERROR_SUCCESS; i++)
	{
		retCodeAdapter = RegEnumKey (hKeyMib, i, AdapterKeyName, MAX_PATH);
		if (retCodeAdapter == (DWORD)ERROR_SUCCESS)
		{
			// Build the name of the Adapter Key
			strcpy(AdapterKeyPathName, WANLinksParamtersPath);
			strcat(AdapterKeyPathName,"\\");
			strcat(AdapterKeyPathName, AdapterKeyName);

			// Open the key to the Adapter
			retCodeAdapter = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
									AdapterKeyPathName,
									0,
									KEY_ENUMERATE_SUB_KEYS |
									KEY_EXECUTE |
									KEY_QUERY_VALUE,
									&hKeyAdapter);
			
			if (retCodeAdapter != ERROR_SUCCESS)
			{
				//DebugBreak();
				return -1;
			}

			pbData  = SNMP_malloc(MAX_DATA_LEN);

			// Walk the Port sub-keys
			for (j=0, retCodePort = ERROR_SUCCESS; retCodePort == ERROR_SUCCESS; j++)
			{
				retCodePort = RegEnumKey (hKeyAdapter, j, PortKeyName, MAX_PATH);
				if (retCodePort == (DWORD)ERROR_SUCCESS)
				{
					// Update the ports found counter
					totalPortCount++;

					// Build the name of the Adapter Key
					strcpy(PortKeyPathName, AdapterKeyPathName);
					strcat(PortKeyPathName,"\\");
					strcat(PortKeyPathName, PortKeyName);

					// Open the key to the Adapter
					retCodePort = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
											PortKeyPathName,
											0,
											KEY_ENUMERATE_SUB_KEYS |
											KEY_EXECUTE |
											KEY_QUERY_VALUE,
											&hKeyPort);
					
					if ( retCodePort != ERROR_SUCCESS )
					{
						//DebugBreak();
						return -1;
					}


					for (k=0, retCodePortValue=ERROR_SUCCESS; retCodePortValue==ERROR_SUCCESS; k++)
					{
						cbData = MAX_DATA_LEN;
						cbValueName = MAX_DATA_LEN;
		
						retCodePortValue = RegEnumValue ( hKeyPort,		// Key handle returned from RegOpenKeyEx.
														  k,			// Value index, taken from listbox.
														  ValueName,	// Name of value.
														  &cbValueName,	// Size of value name.
														  NULL,			// Reserved, dword = NULL.
														  &dwType,		// Type of data.
														  pbData,		// Data buffer.
														  &cbData);		// Size of data buffer.

						if ( retCodePortValue != ERROR_SUCCESS )
						{
							if (retCodePortValue != ERROR_NO_MORE_ITEMS) 
							{
								//DebugBreak();
								return -1;
							}
						}
						else
						{
							// Determine the type of key and add it into the port structure
							if (strcmp(ValueName, c_pszSNMP_Circuits) == 0)
							{
								// Parse the ciruits list and add them in

							}
							// Check for protocol identifier registry key
							else if (strcmp(ValueName, c_pszSNMP_ProtocolIdentifier) == 0)
							{
								// Test for X.25
								if (strcmp(pbData, c_pszValueX25Protocol) == 0)
								{
									lapbPortCount++;
									portIsLapb = TRUE;
								}
								else //(strcmp(pbData, c_pszValueFRProtocol) == 0)
								{
									portIsLapb = FALSE;
								}
							}
							else if (strcmp(ValueName, DXB_REGVAL_LINE_SNID) == 0)
							{
								PULONG pSnid = (PVOID)pbData;
								if ( dwType == REG_DWORD && cbData >= sizeof *pSnid )
									Snid = *pSnid;
							}
					
						} // end if
					} // end for

					//// If this is the first pass, we have a port info structure,
					if (portIsLapb)
					{
						//// If not we need to tack a new one on the end of thelist
						if (firstTimeThrough)
						{
							firstTimeThrough = FALSE;
							pCurSxbPortInfo->pPreviousSxbPortInfo = NULL;
						}
						else
						{	
							pCurSxbPortInfo->pNextSxbPortInfo = SNMP_malloc(sizeof(SXB_PORT_INFO));
							pCurSxbPortInfo->pNextSxbPortInfo->pPreviousSxbPortInfo = pCurSxbPortInfo;
							pCurSxbPortInfo = pCurSxbPortInfo->pNextSxbPortInfo;
							pCurSxbPortInfo->pNextSxbPortInfo = NULL;
						}

						// Initialize the Variables in the port info structure
						portIsLapb = FALSE;
						
						// Build the name of the adapter port object for SXB driver IOCTLs
						// It looks like "DigiSxb2A"
						strcpy(pCurSxbPortInfo->DeviceName, AdapterKeyName);
						//strcat(pCurSxbPortInfo->DeviceName, PortKeyName);
						
						pCurSxbPortInfo->Snid = Snid;
						
						//Update the remainder of the SXB Info
						pCurSxbPortInfo->ProtocolId = SXB_PROTOCOL_ID_X25;
						pCurSxbPortInfo->WANLinksPortId = totalPortCount;

						// Add the Sxb Circuit List to the Port Info structure
						pCurSxbPortInfo->pCircuitList = NULL;
					}
					else //Port isn't x.25, so release the circuit list elements
					{
					}
					
					// Close the Port Key
					RegCloseKey (hKeyPort);   // Close the key handle.
				}
			}

			// Free the memory for the key
			SNMP_free(pbData);
		

			// Close the Adapter Key
			RegCloseKey (hKeyAdapter);   // Close the key handle.
		}
	}

	// We now know the Port Count
	if ((portCount = lapbPortCount) == 0)
	{
		return -1;
	}
	RegCloseKey (hKeyMib);   // Close the key handle.
	return 0;
}

// Utilities to play games with unicode strings
int StrToWStr( LPWSTR wszDest, LPSTR szSrc, LONG count )
{
   LONG i;

   for( i = 0; i < count; i++ )
   {
      wszDest[i] = szSrc[i];
   }

   return( i-1 );
}

USHORT WstrLength( IN LPWSTR Wstr )   
/*++

Routine Description:

   Determines the number of bytes used by the passed in Wstr

Arguments:

   Wstr - Pointer to wide character string.

Return Value:

   Length, in bytes.
   
--*/
{
   USHORT Length=0;

   while( *Wstr++ )
   {
      Length += sizeof(WCHAR);
   }
   return( Length );
}  // end DigiWstrLength

