/*++
*******************************************************************************
* Copyright (c) 1999-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:

   frelmib.c

Abstract:

    Frame Relay MIB SNMP Extension Agent.

    These files (freldll.c, frelmib.c, and frelmib.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 frel MIB. It's definition is found 
	in the IETF RFC 1317.

Author:

   Carl Caldwell

--*/
#include <windows.h>
#include <snmp.h>
#include <stdlib.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 "frelmib.h"

// Contains definitions for the structures for collecting data from the SXB
// driver interface.
#include "frelsxb.h"

// Buffer for formatted Debug messages
#ifdef SNMP_DLL_DEBUG
char	DbgBuf[80];
#endif

// Extension Agent DLLs need access to elapsed time agent has been active.
// This is implemented by initializing the Extension Agent with a time zero
// reference, and allowing the agent to compute elapsed time by subtracting
// the time zero reference from the current system time.  This example
// Extension Agent implements this reference with dwTimeZero.
extern DWORD dwTimeZero;

// Termination event handle
extern HANDLE	terminateEvent;

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

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

//Global var to shut down 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
UINT OID_Prefix[] = { 1, 3, 6, 1, 2, 1, 10, 32 };
AsnObjectIdentifier MIB_OidPrefix = { OID_SIZEOF(OID_Prefix), OID_Prefix };

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

UINT MIB_FREL[]  = { 32 };

// The following definitions are those of the dlcmi table
// These entries are a little different in that they indicate
// The presence of the first configured dlcmi. If no dlcmi's are 
// configured, the DLL will fail to initialize. If more than one 
// dlcmi 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_frDlcmiIfIndex[]                   = { 1, 1, 1, 1 };
UINT MIB_frDlcmiState[]                     = { 1, 1, 2, 1 };
UINT MIB_frDlcmiAddress[]                   = { 1, 1, 3, 1 };
UINT MIB_frDlcmiAddressLen[]                = { 1, 1, 4, 1 };
UINT MIB_frDlcmiPollingInterval[]           = { 1, 1, 5, 1 };
UINT MIB_frDlcmiFullEnquiryInterval[]       = { 1, 1, 6, 1 };
UINT MIB_frDlcmiErrorThreshold[]            = { 1, 1, 7, 1 };
UINT MIB_frDlcmiMonitoredEvents[]           = { 1, 1, 8, 1 };
UINT MIB_frDlcmiMaxSupportedVCs[]           = { 1, 1, 9, 1 };
UINT MIB_frDlcmiMulticast[]                 = { 1, 1, 10, 1 };

// The following definitions are those of the curcuit table
// These entries are a little different also. They indicate
// The presence of the first configured circuit. If none are 
// configured, the DLL will fail to initialize. If more than one 
// circuit exists, they will be added during initialization too.

UINT MIB_frCircuitIfIndex[]                 = { 2, 1, 1, 1 };
UINT MIB_frCircuitDlci[]                    = { 2, 1, 2, 1 };
UINT MIB_frCircuitState[]                   = { 2, 1, 3, 1 };
UINT MIB_frCircuitRcvdFECNs[]               = { 2, 1, 4, 1 };
UINT MIB_frCircuitRcvdBECNs[]               = { 2, 1, 5, 1 };
UINT MIB_frCircuitSentFrames[]              = { 2, 1, 6, 1 };
UINT MIB_frCircuitSentOctets[]              = { 2, 1, 7, 1 };
UINT MIB_frCircuitRcvdFrames[]              = { 2, 1, 8, 1 };
UINT MIB_frCircuitRcvdOctets[]              = { 2, 1, 9, 1 };
UINT MIB_frCircuitCreationTime[]            = { 2, 1, 10, 1 };
UINT MIB_frCircuitLastTimeChange[]          = { 2, 1, 11, 1 };
UINT MIB_frCircuitCommittedBurst[]          = { 2, 1, 12, 1 };
UINT MIB_frCircuitExcessBurst[]             = { 2, 1, 13, 1 };
UINT MIB_frCircuitThroughput[]              = { 2, 1, 14, 1 };

// The following definitions are those of the error table
// These entries describe the last error seen at each frame-relay
// interface. The same table contruction rules apply as do to 
// the circuit table.

UINT MIB_frErrIfIndex[]                     = { 3, 1, 1, 1 };
UINT MIB_frErrType[]                        = { 3, 1, 2, 1 };
UINT MIB_frErrData[]                        = { 3, 1, 3, 1 };
UINT MIB_frErrTime[]                        = { 3, 1, 4, 1 };

//                             //
// Storage definitions for MIB //
//                             //
AsnInteger      MIB_frDlcmiIfIndexStor               = 1;
AsnInteger      MIB_frDlcmiStateStor                 = 1;
AsnInteger      MIB_frDlcmiAddressStor               = 0;
AsnInteger      MIB_frDlcmiAddressLenStor            = 0;
AsnInteger      MIB_frDlcmiPollingIntervalStor       = 1;
AsnInteger      MIB_frDlcmiFullEnquiryIntervalStor   = 1;
AsnInteger      MIB_frDlcmiErrorThresholdStor        = 1;
AsnInteger      MIB_frDlcmiMonitoredEventsStor       = 1;
AsnInteger      MIB_frDlcmiMaxSupportedVCsStor       = 1;
AsnInteger      MIB_frDlcmiMulticastStor             = 1;

AsnInteger      MIB_frCircuitIfIndexStor             = 1;
AsnInteger      MIB_frCircuitDlciStor                = 1;
AsnInteger      MIB_frCircuitStateStor               = 3;
AsnCounter      MIB_frCircuitRcvdFECNsStor           = 0;
AsnCounter      MIB_frCircuitRcvdBECNsStor           = 0;
AsnCounter      MIB_frCircuitSentFramesStor          = 0;
AsnCounter      MIB_frCircuitSentOctetsStor          = 0;
AsnCounter      MIB_frCircuitRcvdFramesStor          = 0;
AsnCounter      MIB_frCircuitRcvdOctetsStor          = 0;
AsnTimeticks    MIB_frCircuitCreationTimeStor        = 0;
AsnTimeticks    MIB_frCircuitLastTimeChangeStor      = 0;
AsnInteger      MIB_frCircuitCommittedBurstStor      = 0;
AsnInteger      MIB_frCircuitExcessBurstStor         = 0;
AsnInteger      MIB_frCircuitThroughputStor          = 0;

AsnInteger      MIB_frErrIfIndexStor                 = 1;
AsnInteger      MIB_frErrTypeStor                    = 10;
AsnOctetString  MIB_frErrDataStor                    = {{0}, 0, 0};
AsnTimeticks    MIB_frErrTimeStor                    = 0;

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

      { { OID_SIZEOF(MIB_frDlcmiState), MIB_frDlcmiState },
    	&MIB_frDlcmiStateStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READWRITE, 
		MIB_lmi_type_leaf_func, 
		&Mib[2] },   //Index 1

      { { OID_SIZEOF(MIB_frDlcmiAddress), MIB_frDlcmiAddress },
    	&MIB_frDlcmiAddressStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READWRITE, 
		MIB_leaf_func, 
		&Mib[3] },   //Index 2

      { { OID_SIZEOF(MIB_frDlcmiAddressLen), MIB_frDlcmiAddressLen },
	    &MIB_frDlcmiAddressLenStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READWRITE, 
		MIB_leaf_func, 
		&Mib[4] },   //Index 3
      
      { { OID_SIZEOF(MIB_frDlcmiPollingInterval), MIB_frDlcmiPollingInterval },
    	&MIB_frDlcmiPollingIntervalStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READWRITE, 
		MIB_leaf_func, 
		&Mib[5] },   //Index 4
      
      { { OID_SIZEOF(MIB_frDlcmiFullEnquiryInterval), MIB_frDlcmiFullEnquiryInterval },
	    &MIB_frDlcmiFullEnquiryIntervalStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READWRITE, 
		MIB_leaf_func, 
		&Mib[6] },   //Index 5
      
      
      { { OID_SIZEOF(MIB_frDlcmiErrorThreshold), MIB_frDlcmiErrorThreshold },
	    &MIB_frDlcmiErrorThresholdStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READWRITE, 
		MIB_leaf_func, 
		&Mib[7] },   //Index 6
      
      { { OID_SIZEOF(MIB_frDlcmiMonitoredEvents), MIB_frDlcmiMonitoredEvents },
	    &MIB_frDlcmiMonitoredEventsStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READWRITE, 
		MIB_leaf_func, 
		&Mib[8] },   //Index 7
      
      { { OID_SIZEOF(MIB_frDlcmiMaxSupportedVCs), MIB_frDlcmiMaxSupportedVCs},
	    &MIB_frDlcmiMaxSupportedVCsStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READWRITE, 
		MIB_leaf_func, 
		&Mib[9] },   //Index 8
      
      { { OID_SIZEOF(MIB_frDlcmiMulticast), MIB_frDlcmiMulticast},
	    &MIB_frDlcmiMulticastStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READWRITE, 
		MIB_leaf_func, 
		&Mib[10] },  //Index 9
      
      { { OID_SIZEOF(MIB_frCircuitIfIndex), MIB_frCircuitIfIndex},
	    &MIB_frCircuitIfIndexStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READ, 
		MIB_leaf_func, 
		&Mib[11] },  //Index 10
            
      { { OID_SIZEOF(MIB_frCircuitDlci), MIB_frCircuitDlci},
	    &MIB_frCircuitDlciStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READWRITE, 
		MIB_leaf_func, 
		&Mib[12] },  //Index 11
            
      { { OID_SIZEOF(MIB_frCircuitState), MIB_frCircuitState},
	    &MIB_frCircuitStateStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READWRITE, 
		MIB_leaf_func, 
		&Mib[13] },  //Index 12
            
      { { OID_SIZEOF(MIB_frCircuitRcvdFECNs), MIB_frCircuitRcvdFECNs},
	    &MIB_frCircuitRcvdFECNsStor, NULL,
		ASN_RFC1155_COUNTER,
	    MIB_ACCESS_READ, 
		MIB_leaf_func, 
		&Mib[14] },  //Index 13
            
      { { OID_SIZEOF(MIB_frCircuitRcvdBECNs), MIB_frCircuitRcvdBECNs},
	    &MIB_frCircuitRcvdBECNsStor, NULL,
		ASN_RFC1155_COUNTER,
	    MIB_ACCESS_READ, 
		MIB_leaf_func, 
		&Mib[15] },  //Index 14
            
      { { OID_SIZEOF(MIB_frCircuitSentFrames), MIB_frCircuitSentFrames},
	    &MIB_frCircuitSentFramesStor, NULL,
		ASN_RFC1155_COUNTER,
	    MIB_ACCESS_READ, 
		MIB_leaf_func, 
		&Mib[16] },  //Index 15
            
      { { OID_SIZEOF(MIB_frCircuitSentOctets), MIB_frCircuitSentOctets},
	    &MIB_frCircuitSentOctetsStor, NULL,
		ASN_RFC1155_COUNTER,
	    MIB_ACCESS_READ, 
		MIB_leaf_func, 
		&Mib[17] },  //Index 16
            
      { { OID_SIZEOF(MIB_frCircuitRcvdFrames), MIB_frCircuitRcvdFrames},
	    &MIB_frCircuitRcvdFramesStor, NULL,
		ASN_RFC1155_COUNTER,
	    MIB_ACCESS_READ, 
		MIB_leaf_func, 
		&Mib[18] },  //Index 17
            
      { { OID_SIZEOF(MIB_frCircuitRcvdOctets), MIB_frCircuitRcvdOctets},
	    &MIB_frCircuitRcvdOctetsStor, NULL,
		ASN_RFC1155_COUNTER,
	    MIB_ACCESS_READ, 
		MIB_leaf_func, 
		&Mib[19] },  //Index 18
            
      { { OID_SIZEOF(MIB_frCircuitCreationTime), MIB_frCircuitCreationTime},
	    &MIB_frCircuitCreationTimeStor, NULL,
		ASN_RFC1155_TIMETICKS,
	    MIB_ACCESS_READ, 
		MIB_time_leaf_func, 
		&Mib[20] },  //Index 19
            
      { { OID_SIZEOF(MIB_frCircuitLastTimeChange), MIB_frCircuitLastTimeChange},
	    &MIB_frCircuitLastTimeChangeStor, NULL,
		ASN_RFC1155_TIMETICKS,
	    MIB_ACCESS_READ, 
		MIB_leaf_func, 
		&Mib[21] },  //Index 20
            
      { { OID_SIZEOF(MIB_frCircuitCommittedBurst), MIB_frCircuitCommittedBurst},
	    &MIB_frCircuitCommittedBurstStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READWRITE, 
		MIB_leaf_func, 
		&Mib[22] },  //Index 21
            
      { { OID_SIZEOF(MIB_frCircuitExcessBurst), MIB_frCircuitExcessBurst},
	    &MIB_frCircuitExcessBurstStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READWRITE, 
		MIB_leaf_func, 
		&Mib[23] },  //Index 22
            
      { { OID_SIZEOF(MIB_frCircuitThroughput), MIB_frCircuitThroughput},
	    &MIB_frCircuitThroughputStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READWRITE, 
		MIB_leaf_func, 
		&Mib[24] },  //Index 23
            
      { { OID_SIZEOF(MIB_frErrIfIndex), MIB_frErrIfIndex },
	    &MIB_frErrIfIndexStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READ, 
		MIB_leaf_func, 
		&Mib[25] },   //Index 24

      { { OID_SIZEOF(MIB_frErrType), MIB_frErrType },
	    &MIB_frErrTypeStor, NULL,
		ASN_INTEGER,
	    MIB_ACCESS_READ, 
		MIB_err_type_leaf_func, 
		&Mib[26] },   //Index 25

      { { OID_SIZEOF(MIB_frErrData), MIB_frErrData },
	    &MIB_frErrDataStor, NULL,
		ASN_OCTETSTRING,
	    MIB_ACCESS_READ, 
		MIB_leaf_func, 
		&Mib[27] },   //Index 26

      { { OID_SIZEOF(MIB_frErrTime), MIB_frErrTime },
	    &MIB_frErrTimeStor, NULL,
		ASN_RFC1155_TIMETICKS,
	    MIB_ACCESS_READ, 
		MIB_time_leaf_func, 
		NULL },   //Index 27

      };

UINT MIB_num_variables = sizeof Mib / sizeof( MIB_ENTRY );



//
// InitMibTableEntries
//    Initializes the Table Entries in a Mib. 
//
// Notes:
//    
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT InitMibTableEntries(
		IN PFREL_SXB_INFO pFrelSxbInfo,
		IN PSXB_PORT_INFO pSxbPortInfo )
{
    UINT initStatus;


    initStatus = InitDlcmiTableMibEntries(Mib, pFrelSxbInfo, pSxbPortInfo);
    if (!initStatus)
    { 
        initStatus = InitCircuitTableMibEntries(Mib, pFrelSxbInfo, pSxbPortInfo);
        if (!initStatus)
            initStatus = InitErrTableMibEntries(Mib, pFrelSxbInfo, pSxbPortInfo);
    }
    return initStatus;
}    



//
// InitCircuitTableMibEntries
//    Initializes the Circuit Table Entries in the Mib from the registry 
//    values, or SXB interface.
//    The table is initialized in the same manner as the dlcmi table.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT InitCircuitTableMibEntries(
		IN PMIB_ENTRY pMibBase, 
		IN PFREL_SXB_INFO pFrelSxbInfo,
		IN PSXB_PORT_INFO pSxbPortInfo )
{

    UINT initStatus = SNMP_ERRORSTATUS_NOERROR;
	PFREL_SXB_INFO  pCurCktInfo = pFrelSxbInfo;
	PSXB_PORT_INFO  pCurSxbPortInfo	= pSxbPortInfo;
    AsnInteger		cktNdx; 
    int				portNdx; 
    int				totalCircuitCount = 0;

    AsnInteger      curCktNdx = 1;
    AsnInteger      cktCount;
    AsnInteger      cktDlci;
    AsnInteger      cktState;
    AsnCounter      cktRcvdFECNs;
    AsnCounter      cktRcvdBECNs;
    AsnCounter      cktSentFrames;
    AsnCounter      cktSentOctets;
    AsnCounter      cktRcvdFrames;
    AsnCounter      cktRcvdOctets;
    AsnCounter      cktCreateTime;
    AsnCounter      cktLastTimeChange;
    AsnInteger      cktCommitBurst;
    AsnInteger      cktExcessBurst;
    AsnInteger      cktThroughput;
	
    MIB_ENTRY * pMIB_curIfIndexMibEntry = &Mib[FRELMIB_FIRST_CKT_NDX];
    MIB_ENTRY * pMIB_curDlciMibEntry = &Mib[FRELMIB_FIRST_CKT_DLCI_NDX];
    MIB_ENTRY * pMIB_curStateMibEntry = &Mib[FRELMIB_FIRST_CKT_STATE_NDX];
    MIB_ENTRY * pMIB_curRcvdFECNsMibEntry = &Mib[FRELMIB_FIRST_CKT_RCV_FECNS_NDX];
    MIB_ENTRY * pMIB_curRcvdBECNsMibEntry = &Mib[FRELMIB_FIRST_CKT_RCV_BECNS_NDX];
    MIB_ENTRY * pMIB_curSentFramesMibEntry = &Mib[FRELMIB_FIRST_SENT_FRAMES_NDX];
    MIB_ENTRY * pMIB_curSentOctetsMibEntry = &Mib[FRELMIB_FIRST_SENT_OCTETS_NDX];
    MIB_ENTRY * pMIB_curRcvdFramesMibEntry = &Mib[FRELMIB_FIRST_CKT_RCV_FRAMES_NDX];
    MIB_ENTRY * pMIB_curRcvdOctetsMibEntry = &Mib[FRELMIB_FIRST_CKT_RCV_OCTETS_NDX];
    MIB_ENTRY * pMIB_curCreationTimeMibEntry = &Mib[FRELMIB_FIRST_CKT_CREATE_TIME_NDX];
    MIB_ENTRY * pMIB_curLastTimeChangeMibEntry = &Mib[FRELMIB_FIRST_CKT_LAST_TIME_CHANGE_NDX];
    MIB_ENTRY * pMIB_curCommittedBurstMibEntry = &Mib[FRELMIB_FIRST_CKT_COMMIT_BURST_NDX];
    MIB_ENTRY * pMIB_curExcessBurstMibEntry = &Mib[FRELMIB_FIRST_CKT_EXCESS_BURST_NDX];
    MIB_ENTRY * pMIB_curThroughputMibEntry = &Mib[FRELMIB_FIRST_CKT_THRUPUT_NDX];

	// Compute the total number of circuits
	for (portNdx=1; portNdx <= portCount; portNdx++)
	{
		totalCircuitCount += pCurSxbPortInfo->CircuitCount;
		pCurSxbPortInfo = pCurSxbPortInfo->pNext;
	}
	pCurSxbPortInfo = pSxbPortInfo;

	for (portNdx=1; portNdx <= portCount; portNdx++)
	{
		// Get the circuit count for this port
		cktCount = pCurSxbPortInfo->CircuitCount;
#ifdef SNMP_DLL_DEBUG
		wsprintf(DbgBuf,"port %1x has %1x circuits\n",portNdx, cktCount);
		OutputDebugString(DbgBuf);
#endif

		//Locate the first Ioctls for the port by skipping the error and dlcmi ioctls
		pCurCktInfo	= (pCurCktInfo->pNext)->pNext;

		for (cktNdx=1; cktNdx <= cktCount; cktNdx++)
		{
#ifdef SNMP_DLL_DEBUG
			wsprintf(DbgBuf,"CKT Stats Ioctl Type is %01x\n",pCurCktInfo->IoctlType);
			OutputDebugString(DbgBuf);
#endif

			cktDlci             = 1;
			cktState            = 1;
			cktRcvdFECNs        = 0;
			cktRcvdBECNs        = 0;
			cktSentFrames       = 0;
			cktSentOctets       = 0;
			cktRcvdFrames       = 0;
			cktRcvdOctets       = 0;
			cktCreateTime       = 0;
			cktLastTimeChange   = 0;
			cktCommitBurst      = 0;
			cktExcessBurst      = 0;
			cktThroughput       = 0;
				
			//Initialize the Mib variables with the registry values
			*( (long *) pMIB_curIfIndexMibEntry->Storage ) 
				= 	pCurSxbPortInfo->WANLinksPortId;

			
			*( (long *) pMIB_curDlciMibEntry->Storage ) 
				= cktDlci;
			pMIB_curDlciMibEntry->pSxbRawData = 
				&pCurCktInfo->upData.pFrelCircuitData->frCircuitDlci;
        
			*( (long *) pMIB_curStateMibEntry->Storage ) 
				= cktState;
			pMIB_curStateMibEntry->pSxbRawData = 
				&pCurCktInfo->upData.pFrelCircuitData->frCircuitState;
			
			*( (long *) pMIB_curRcvdFECNsMibEntry->Storage ) 
				= cktRcvdFECNs;
			pMIB_curRcvdFECNsMibEntry->pSxbRawData = 
				&pCurCktInfo->upData.pFrelCircuitData->frCircuitReceivedFECNs;
			
			*( (long *) pMIB_curRcvdBECNsMibEntry->Storage ) 
				= cktRcvdBECNs;
			pMIB_curRcvdBECNsMibEntry->pSxbRawData = 
				&pCurCktInfo->upData.pFrelCircuitData->frCircuitReceivedBECNs;
			
			*( (long *) pMIB_curSentFramesMibEntry->Storage )  
				= cktSentFrames;
			pMIB_curSentFramesMibEntry->pSxbRawData = 
				&pCurCktInfo->upData.pFrelCircuitData->frCircuitSentFrames;
			
			*( (long *) pMIB_curSentOctetsMibEntry->Storage )  
				= cktSentOctets;
			pMIB_curSentOctetsMibEntry->pSxbRawData = 
				&pCurCktInfo->upData.pFrelCircuitData->frCircuitSentOctets;
			
			*( (long *) pMIB_curRcvdFramesMibEntry->Storage ) 
				= cktRcvdFrames;
			pMIB_curRcvdFramesMibEntry->pSxbRawData = 
				&pCurCktInfo->upData.pFrelCircuitData->frCircuitReceivedFrames;
			
			*( (long *) pMIB_curRcvdOctetsMibEntry->Storage ) 
				= cktRcvdOctets;
			pMIB_curRcvdOctetsMibEntry->pSxbRawData = 
				&pCurCktInfo->upData.pFrelCircuitData->frCircuitReceivedOctets;
			
			*( (long *) pMIB_curCreationTimeMibEntry->Storage ) 
				= cktCreateTime;
			pMIB_curCreationTimeMibEntry->pSxbRawData = 
				&pCurCktInfo->upData.pFrelCircuitData->frCircuitCreationTime;
			
			*( (long *) pMIB_curLastTimeChangeMibEntry->Storage ) 
				= cktLastTimeChange;
			pMIB_curLastTimeChangeMibEntry->pSxbRawData = 
				&pCurCktInfo->upData.pFrelCircuitData->frCircuitLastTimeChange;
			
			*( (long *) pMIB_curCommittedBurstMibEntry->Storage ) 
				= cktCommitBurst;
			pMIB_curCommittedBurstMibEntry->pSxbRawData = 
				&pCurCktInfo->upData.pFrelCircuitData->frCircuitCommittedBurst;

			*( (long *) pMIB_curExcessBurstMibEntry->Storage ) 
				= cktExcessBurst;
			pMIB_curExcessBurstMibEntry->pSxbRawData = 
				&pCurCktInfo->upData.pFrelCircuitData->frCircuitExcessBurst;

			*( (long *) pMIB_curThroughputMibEntry->Storage ) 
				= cktThroughput;
			pMIB_curThroughputMibEntry->pSxbRawData = 
				&pCurCktInfo->upData.pFrelCircuitData->frCircuitThroughput;
        
			// 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 (curCktNdx++  < totalCircuitCount)
			{
				pMIB_curIfIndexMibEntry = 
					CreateNextMibColFrom(pMIB_curIfIndexMibEntry);
				// Check for successful completion
				if (pMIB_curIfIndexMibEntry  == NULL)
				{
					initStatus = SNMP_ERRORSTATUS_GENERR;
					break;
				}

				pMIB_curDlciMibEntry = 
					CreateNextMibColFrom(pMIB_curDlciMibEntry);
				// Check for successful completion
				if (pMIB_curDlciMibEntry == NULL)
				{
					initStatus = SNMP_ERRORSTATUS_GENERR;
					break;
				}

				pMIB_curStateMibEntry = 
					CreateNextMibColFrom(pMIB_curStateMibEntry);
				// Check for successful completion
				if (pMIB_curStateMibEntry == NULL)
				{
					initStatus = SNMP_ERRORSTATUS_GENERR;
					break;
				}

				pMIB_curRcvdFECNsMibEntry = 
					CreateNextMibColFrom(pMIB_curRcvdFECNsMibEntry);
				// Check for successful completion
				if (pMIB_curRcvdFECNsMibEntry == NULL)
				{
					initStatus = SNMP_ERRORSTATUS_GENERR;
					break;
				}

				pMIB_curRcvdBECNsMibEntry = 
					CreateNextMibColFrom(pMIB_curRcvdBECNsMibEntry);
				// Check for successful completion
				if (pMIB_curRcvdBECNsMibEntry == NULL)
				{
					initStatus = SNMP_ERRORSTATUS_GENERR;
					break;
				}

				pMIB_curSentFramesMibEntry = 
					CreateNextMibColFrom(pMIB_curSentFramesMibEntry);
				// Check for successful completion
				if (pMIB_curSentFramesMibEntry == NULL)
				{
					initStatus = SNMP_ERRORSTATUS_GENERR;
					break;
				}

				pMIB_curSentOctetsMibEntry = 
					CreateNextMibColFrom(pMIB_curSentOctetsMibEntry);
				// Check for successful completion
				if (pMIB_curSentOctetsMibEntry == NULL)
				{
					initStatus = SNMP_ERRORSTATUS_GENERR;
					break;
				}

				pMIB_curRcvdFramesMibEntry = 
					CreateNextMibColFrom(pMIB_curRcvdFramesMibEntry);
				// Check for successful completion
				if (pMIB_curRcvdFramesMibEntry == NULL)
				{
					initStatus = SNMP_ERRORSTATUS_GENERR;
					break;
				}

				pMIB_curRcvdOctetsMibEntry = 
					CreateNextMibColFrom(pMIB_curRcvdOctetsMibEntry);
				// Check for successful completion
				if (pMIB_curRcvdOctetsMibEntry == NULL)
				{
					initStatus = SNMP_ERRORSTATUS_GENERR;
					break;
				}

				pMIB_curCreationTimeMibEntry = 
					CreateNextMibColFrom(pMIB_curCreationTimeMibEntry);
				// Check for successful completion
				if (pMIB_curCreationTimeMibEntry == NULL)
				{
					initStatus = SNMP_ERRORSTATUS_GENERR;
					break;
				}

				pMIB_curLastTimeChangeMibEntry = 
					CreateNextMibColFrom(pMIB_curLastTimeChangeMibEntry);
				// Check for successful completion
				if (pMIB_curLastTimeChangeMibEntry == NULL)
				{
					initStatus = SNMP_ERRORSTATUS_GENERR;
					break;
				}

				pMIB_curCommittedBurstMibEntry = 
					CreateNextMibColFrom(pMIB_curCommittedBurstMibEntry);
				// Check for successful completion
				if (pMIB_curCommittedBurstMibEntry == NULL)
				{
					initStatus = SNMP_ERRORSTATUS_GENERR;
					break;
				}

				pMIB_curExcessBurstMibEntry = 
					CreateNextMibColFrom(pMIB_curExcessBurstMibEntry);
				// Check for successful completion
				if (pMIB_curExcessBurstMibEntry == NULL)
				{
					initStatus = SNMP_ERRORSTATUS_GENERR;
					break;
				}

				pMIB_curThroughputMibEntry = 
					CreateNextMibColFrom(pMIB_curThroughputMibEntry);
				// Check for successful completion
				if (pMIB_curThroughputMibEntry == NULL)
				{
					initStatus = SNMP_ERRORSTATUS_GENERR;
					break;
				}


			} // end if more than one circuit per port

			//Locate the next IOCTL structure for this port
			pCurCktInfo	= pCurCktInfo->pNext;

		} // end for each circuit on port

		// Go to the next port's info structure
		pCurSxbPortInfo = pCurSxbPortInfo->pNext;

	} //end for each port

    return initStatus;
}

//
// InitDlcmiTableMibEntries
//    Initializes the Data Link Connection Management Interface (Dlcmi)
//    Table Entries in the Mib 
//    The table is initialized by appending columns to the vector
//    of specfic Mib entries. In the initial data structuring stage,
//    The first 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 index is chained to the first.
//    subsequently, the second row entry 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 InitDlcmiTableMibEntries(
		IN PMIB_ENTRY pMibBase, 
		IN PFREL_SXB_INFO pFrelSxbInfo,
		IN PSXB_PORT_INFO pSxbPortInfo )
{

    UINT			initStatus = SNMP_ERRORSTATUS_NOERROR;
    AsnInteger		dlcmiNdx; 
	PFREL_SXB_INFO  pCurFrelSxbInfo = pFrelSxbInfo;
	PSXB_PORT_INFO  pCurSxbPortInfo = pSxbPortInfo;
	int				cktNdx; 
	int				cktIoctlCnt;

    AsnInteger		dlcmiState;
    AsnInteger		dlcmiAddress;
    AsnInteger		dlcmiAddressLen;
    AsnInteger		dlcmiPollInterval;
    AsnInteger		dlcmiFullEnqInterval;
    AsnInteger		dlcmiErrorThreshold;
    AsnInteger		dlcmiMonitoredEvents;
    AsnInteger		dlcmiMaxSptVCs;
    AsnInteger		dlcmiMulticast;
    
    // Pointers used to manipulate Mib table as it is built
    MIB_ENTRY * pCurDlcmiNdxMibEntry = &Mib[FRELMIB_FIRST_DLCMI_NDX];
    MIB_ENTRY * pCurDlcmiStateMibEntry = &Mib[FRELMIB_FIRST_DLCMI_STATE];
    MIB_ENTRY * pCurDlcmiAddressMibEntry = &Mib[FRELMIB_FIRST_DLCMI_ADDR];
    MIB_ENTRY * pCurDlcmiAddressLenMibEntry = &Mib[FRELMIB_FIRST_DLCMI_ADDR_LEN];
    MIB_ENTRY * pCurDlcmiPollIntervalMibEntry = &Mib[FRELMIB_FIRST_DLCMI_POLL_INTERVAL];
    MIB_ENTRY * pCurDlcmiFullEnqIntervalMibEntry = &Mib[FRELMIB_FIRST_DLCMI_FULL_ENQ_INTERVAL];
    MIB_ENTRY * pCurDlcmiErrorThresholdMibEntry = &Mib[FRELMIB_FIRST_DLCMI_ERR_THRESHOLD];
    MIB_ENTRY * pCurDlcmiMonEventsEntry = &Mib[FRELMIB_FIRST_DLCMI_MON_EVENTS];
    MIB_ENTRY * pCurDlcmiMaxSptVCsMibEntry= &Mib[FRELMIB_FIRST_DLCMI_MAX_SPT_VCS];
    MIB_ENTRY * pCurDlcmiMulticastEntry= &Mib[FRELMIB_FIRST_DLCMI_MULTICAST];
    
    for (dlcmiNdx=1; dlcmiNdx <= portCount; dlcmiNdx++)
    {	

		// locate the next FR Link Config IOCTL to map storage locations to
		// It is the second IOCTL in the list for this port
		pCurFrelSxbInfo = pCurFrelSxbInfo->pNext;

#ifdef SNMP_DLL_DEBUG
		wsprintf(DbgBuf," DLCMI Ioctl Type is %01x\n",pCurFrelSxbInfo->IoctlType);
		OutputDebugString(DbgBuf);
#endif
		
        dlcmiState = 1;                 //Default to no lmi configured
        dlcmiAddress = 4;               //Default to q922     
        dlcmiAddressLen = 2;            //Default to 2 octets
        dlcmiPollInterval = 20;         //Default value of 20 seconds
        dlcmiFullEnqInterval = 6;       //Default to 6 poll cycles
        dlcmiErrorThreshold = 3;        //Default to 3 enquiry threshold
        dlcmiMonitoredEvents = 4;       //Default to 4 polls
        dlcmiMaxSptVCs = 64;            //Default for Syncport firmware
        dlcmiMulticast = 1;             //Default to non-broadcast
	    
	    //Initialize the Mib variables with the registry values
	    *( (long *) pCurDlcmiNdxMibEntry->Storage )
			= pCurFrelSxbInfo->PortId;

		*( (long *) pCurDlcmiStateMibEntry->Storage )
			= dlcmiState;
		pCurDlcmiStateMibEntry->pSxbRawData = 
			&pCurFrelSxbInfo->upData.pFrelDlcmiData->frDlcmiDlcmiState;

        *( (long *) pCurDlcmiAddressMibEntry->Storage )
			= dlcmiAddress;
		pCurDlcmiAddressMibEntry->pSxbRawData = 
			&pCurFrelSxbInfo->upData.pFrelDlcmiData->frDlcmiAddress;
        
		*( (long *) pCurDlcmiAddressLenMibEntry->Storage )
			= dlcmiAddressLen;
		pCurDlcmiAddressLenMibEntry->pSxbRawData = 
			&pCurFrelSxbInfo->upData.pFrelDlcmiData->frDlcmiAddressLen;
        
		*( (long *) pCurDlcmiPollIntervalMibEntry->Storage )
			= dlcmiPollInterval;
		pCurDlcmiPollIntervalMibEntry->pSxbRawData = 
			&pCurFrelSxbInfo->upData.pFrelDlcmiData->frDlcmiPollingInterval;
		
		*( (long *) pCurDlcmiFullEnqIntervalMibEntry->Storage ) 
			= dlcmiFullEnqInterval;
        pCurDlcmiFullEnqIntervalMibEntry->pSxbRawData = 
			&pCurFrelSxbInfo->upData.pFrelDlcmiData->frDlcmiFullEnquiryInterval;
		
		*( (long *) pCurDlcmiErrorThresholdMibEntry->Storage )
			= dlcmiErrorThreshold;
        pCurDlcmiErrorThresholdMibEntry->pSxbRawData = 
			&pCurFrelSxbInfo->upData.pFrelDlcmiData->frDlcmiErrorThreshold;

		*( (long *) pCurDlcmiMonEventsEntry->Storage ) 
			= dlcmiMonitoredEvents;
		pCurDlcmiMonEventsEntry->pSxbRawData = 
			&pCurFrelSxbInfo->upData.pFrelDlcmiData->frDlcmiMonitoredEvents;
        
		*( (long *) pCurDlcmiMaxSptVCsMibEntry->Storage ) 
			= dlcmiMaxSptVCs;
		pCurDlcmiMaxSptVCsMibEntry->pSxbRawData = 
			&pCurFrelSxbInfo->upData.pFrelDlcmiData->frDlcmiMaxSupportedVCs;
        
		*( (long *) pCurDlcmiMulticastEntry->Storage ) = 
			dlcmiMulticast;
		pCurDlcmiMulticastEntry->pSxbRawData = 
			&pCurFrelSxbInfo->upData.pFrelDlcmiData->frDlcmiMulticast;

	    // 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 (dlcmiNdx  < portCount)
	    {
	        pCurDlcmiNdxMibEntry      = 
                CreateNextMibColFrom(pCurDlcmiNdxMibEntry);
	        // Check for successful completion
	        if (pCurDlcmiNdxMibEntry == NULL)
	        {
		        initStatus = SNMP_ERRORSTATUS_GENERR;
		        break;
	        }

	        pCurDlcmiStateMibEntry     = 
                CreateNextMibColFrom(pCurDlcmiStateMibEntry);
	        // Check for successful completion
	        if (pCurDlcmiStateMibEntry == NULL)
	        {
		        initStatus = SNMP_ERRORSTATUS_GENERR;
		        break;
	        }

	        pCurDlcmiAddressMibEntry   = 
                CreateNextMibColFrom(pCurDlcmiAddressMibEntry);
	        // Check for successful completion
	        if (pCurDlcmiAddressMibEntry == NULL)
	        {
		        initStatus = SNMP_ERRORSTATUS_GENERR;
		        break;
	        }

	        pCurDlcmiAddressLenMibEntry = 
                CreateNextMibColFrom(pCurDlcmiAddressLenMibEntry);
	        // Check for successful completion
	        if (pCurDlcmiAddressLenMibEntry == NULL)
	        {
		        initStatus = SNMP_ERRORSTATUS_GENERR;
		        break;
	        }

            pCurDlcmiPollIntervalMibEntry = 
                CreateNextMibColFrom(pCurDlcmiPollIntervalMibEntry);
	        // Check for successful completion
	        if (pCurDlcmiPollIntervalMibEntry == NULL)
	        {
		        initStatus = SNMP_ERRORSTATUS_GENERR;
		        break;
	        }

            pCurDlcmiFullEnqIntervalMibEntry = 
                CreateNextMibColFrom(pCurDlcmiFullEnqIntervalMibEntry);
	        // Check for successful completion
	        if (pCurDlcmiFullEnqIntervalMibEntry == NULL)
	        {
		        initStatus = SNMP_ERRORSTATUS_GENERR;
		        break;
	        }

            pCurDlcmiErrorThresholdMibEntry = 
                CreateNextMibColFrom(pCurDlcmiErrorThresholdMibEntry);
	        // Check for successful completion
	        if (pCurDlcmiErrorThresholdMibEntry == NULL)
	        {
		        initStatus = SNMP_ERRORSTATUS_GENERR;
		        break;
	        }

            pCurDlcmiMonEventsEntry = 
                CreateNextMibColFrom(pCurDlcmiMonEventsEntry);
	        // Check for successful completion
	        if (pCurDlcmiMonEventsEntry == NULL)
	        {
		        initStatus = SNMP_ERRORSTATUS_GENERR;
		        break;
	        }

            pCurDlcmiMaxSptVCsMibEntry = 
                CreateNextMibColFrom(pCurDlcmiMaxSptVCsMibEntry);
	        // Check for successful completion
	        if (pCurDlcmiMaxSptVCsMibEntry == NULL)
	        {
		        initStatus = SNMP_ERRORSTATUS_GENERR;
		        break;
	        }

            pCurDlcmiMulticastEntry = 
                CreateNextMibColFrom(pCurDlcmiMulticastEntry);
	        // Check for successful completion
	        if (pCurDlcmiMulticastEntry == NULL)
	        {
		        initStatus = SNMP_ERRORSTATUS_GENERR;
		        break;
	        }

			// Locate the next port's FIRST IOCTL in the list
			// skip past all the circuit IOCTLs
			cktIoctlCnt = pCurSxbPortInfo->CircuitCount;
			for (cktNdx = 0; cktNdx<=cktIoctlCnt; cktNdx++)
			{
				pCurFrelSxbInfo = pCurFrelSxbInfo->pNext;
			}

			// Locate the next port's information stream
			pCurSxbPortInfo = pCurSxbPortInfo->pNext;
	    } // end if more than one port
		
    } //end for each port

    return initStatus;
}



//
// InitErrTableMibEntries
//    Initializes the Error Table Entries in the Mib from the registry 
//    values, or SXB interface.
//    The table is initialized by appending columns to the vector
//    of specfic Mib entries. In the initial data structuring stage,
//    The first 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 index is chained to the first.
//    subsequently, the second row entry 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 InitErrTableMibEntries(		
		IN PMIB_ENTRY pMibBase, 
		IN PFREL_SXB_INFO pFrelSxbInfo,
		IN PSXB_PORT_INFO pSxbPortInfo )
{

    UINT			initStatus = SNMP_ERRORSTATUS_NOERROR;
    AsnInteger		portNdx; 
	PFREL_SXB_INFO  pCurFrelSxbInfo = pFrelSxbInfo;
	PSXB_PORT_INFO  pCurSxbPortInfo = pSxbPortInfo;
	int				cktNdx; 
	int				cktIoctlCnt;
    
    AsnInteger      errType;
    AsnOctetString  errData = {{0},0,FALSE};
    AsnTimeticks    errTime;
    
    // Pointers used to manipulate Mib table as it is built
    MIB_ENTRY * pCurErrNdxMibEntry = &Mib[FRELMIB_FIRST_ERR_NDX];
    MIB_ENTRY * pCurErrTypeMibEntry = &Mib[FRELMIB_FIRST_ERR_TYPE_NDX];
    MIB_ENTRY * pCurErrDataMibEntry = &Mib[FRELMIB_FIRST_ERR_DATA_NDX];
    MIB_ENTRY * pCurErrTimeMibEntry = &Mib[FRELMIB_FIRST_ERR_TIME_NDX];
    
    for (portNdx=1; portNdx <= portCount; portNdx++)
    {
#ifdef SNMP_DLL_DEBUG
		wsprintf(DbgBuf," Error Ioctl Type is %01x\n",pCurFrelSxbInfo->IoctlType);
		OutputDebugString(DbgBuf);
#endif

        errType = FREL_ERR_TYPE_NO_ERR_SINCE_RESET;
        errTime = (AsnTimeticks) 0;
        
	    //Initialize the Mib variables with the registry values
	    *( (long *) pCurErrNdxMibEntry->Storage ) = pCurFrelSxbInfo->PortId;

		*( (long *) pCurErrTypeMibEntry->Storage ) = errType;
		pCurErrTypeMibEntry->pSxbRawData = 
			&pCurFrelSxbInfo->upData.pFrelErrStats->link_err_type;


        *( (long *) pCurErrTimeMibEntry->Storage ) = errTime;
		pCurErrTypeMibEntry->pSxbRawData = 
			&pCurFrelSxbInfo->upData.pFrelErrStats->link_err_time;

        //Have to handle a structure copy differently (objects would be nicer)
        // NOTICE: We are assigning NO storage area to the octet string
        // This storage will have to be added when an error shows up
        ( (AsnOctetString *) pCurErrDataMibEntry->Storage)->stream    = NULL;
        ( (AsnOctetString *) pCurErrDataMibEntry->Storage)->length    = 0x00;
        ( (AsnOctetString *) pCurErrDataMibEntry->Storage)->dynamic   = FALSE;

        // 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)
	    {
	        pCurErrNdxMibEntry = CreateNextMibColFrom(pCurErrNdxMibEntry);
	        // Check for successful completion
	        if (pCurErrNdxMibEntry == NULL)
	        {
		        initStatus = SNMP_ERRORSTATUS_GENERR;
		        break;
	        }

	        pCurErrTypeMibEntry = CreateNextMibColFrom(pCurErrTypeMibEntry);
	        // Check for successful completion
	        if (pCurErrTypeMibEntry == NULL)
	        {
		        initStatus = SNMP_ERRORSTATUS_GENERR;
		        break;
	        }

	        pCurErrDataMibEntry = CreateNextMibColFrom(pCurErrDataMibEntry);
	        // Check for successful completion
	        if (pCurErrDataMibEntry == NULL)
	        {
		        initStatus = SNMP_ERRORSTATUS_GENERR;
		        break;
	        }

	        pCurErrTimeMibEntry = CreateNextMibColFrom(pCurErrTimeMibEntry);
	        // Check for successful completion
	        if (pCurErrTimeMibEntry == NULL)
	        {
		        initStatus = SNMP_ERRORSTATUS_GENERR;
		        break;
	        }
			// Locate the next port's FIRST IOCTL in the list
			// skip past all the circuit IOCTLs
			// also skip the second (DLCMI) IOCTL for this port
			cktIoctlCnt = (pCurSxbPortInfo->CircuitCount) + 1;
			for (cktNdx = 0; cktNdx<=cktIoctlCnt; cktNdx++)
			{
				pCurFrelSxbInfo = pCurFrelSxbInfo->pNext;
			}

			// Locate the next port's information stream
			pCurSxbPortInfo = pCurSxbPortInfo->pNext;

	    }	//end if more than one port
    }	//end_for

    return initStatus;
}


//
// 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:
//
// Return Codes:
//    Pointer to a new Mib Element. NULL if failure.
//
// Error Codes:
//    None.
//
PMIB_ENTRY CreateNextMibColFrom( IN MIB_ENTRY FAR * pOldMibEntry )
{
    UINT             createStatus = 0;
    MIB_ENTRY FAR *  pNewMibEntry;
    AsnOctetString * pSrcOctetString;
    AsnOctetString * pDstOctetString;

    // Allocate memory for the new MIB_ENTRY
    pNewMibEntry = SNMP_malloc(sizeof(MIB_ENTRY));
  
    if (pNewMibEntry == NULL)
    {
		return NULL;
    }
    
    // Allocate storage for the Oid
    pNewMibEntry->Oid.ids = SNMP_malloc(pOldMibEntry->Oid.idLength);
    if (pNewMibEntry->Oid.ids == NULL)
    {
		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-1] += 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;

    // Chain the new column element into the Mib by:
    //   1. Setting New.NextMib = Old.NextMib
    //   2. Setting Old.NextMib = &New
    //
    pNewMibEntry->MibNext = pOldMibEntry->MibNext;
    pOldMibEntry->MibNext = pNewMibEntry;

    // Copy the Oid Storage area from old to new
    switch (pOldMibEntry->Type) {
    
    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(char));

                memcpy( pDstOctetString->stream,
	                    pSrcOctetString->stream,
	                    pSrcOctetString->length );
            }
            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:
	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_TIMETICKS:
		case ASN_RFC1155_COUNTER:
		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;

		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_RFC1155_TIMETICKS:
		case ASN_INTEGER:
			*(AsnInteger *)(MibPtr->Storage) = VarBind->value.asnValue.number;
			break;

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

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

	// Signal no error occurred
	ErrStat = SNMP_ERRORSTATUS_NOERROR;

Exit:
	return ErrStat;
} // MIB_leaf_func


//
// MIB_time_leaf_func
//    Performs actions on time driven LEAF variables in the MIB.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT MIB_time_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_TIMETICKS:
			if (MibPtr->pSxbRawData != NULL)
			 {
				 VarBind->value.asnValue.number = *((AsnInteger*)MibPtr->pSxbRawData);
			 }
			 else
			 {
				 VarBind->value.asnValue.number = *(AsnInteger *)(MibPtr->Storage);
			 }

			 // Add in the time base from SNMP
			 VarBind->value.asnValue.number += dwTimeZero;
		     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_TIMETICKS:
			*(AsnInteger *)(MibPtr->Storage) = VarBind->value.asnValue.number - dwTimeZero;
			break;

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

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

	// Signal no error occurred
	ErrStat = SNMP_ERRORSTATUS_NOERROR;

Exit:
	return ErrStat;
} // MIB_time_leaf_func



// MIB_short_to_int_leaf_func
//    Performs generic actions on config LEAF variables in the MIB.
//	The variable requested is a UINT32. The sxb variable 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_short_to_int_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_short_to_int_leaf_func



// MIB_multicast_leaf_func
//    Performs actions on the dlcmi multicast variable in the MIB.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT MIB_multicast_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_INTEGER:
			if (MibPtr->pSxbRawData != NULL)
				 VarBind->value.asnValue.number = *((uint16*)MibPtr->pSxbRawData);
			 else
				 VarBind->value.asnValue.number = *(AsnInteger *)(MibPtr->Storage);

			 // Readjust to be NonBraoadcast, or Broadcast
			 if (VarBind->value.asnValue.number == 0)
				 VarBind->value.asnValue.number = FREL_DLCMI_MULTICAST_NON_BROADCAST;
			 else
				 VarBind->value.asnValue.number = FREL_DLCMI_MULTICAST_BROADCAST;
		     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_TIMETICKS:
			*(AsnInteger *)(MibPtr->Storage) = VarBind->value.asnValue.number - 
			                                   dwTimeZero;
			break;

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

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

	// Signal no error occurred
	ErrStat = SNMP_ERRORSTATUS_NOERROR;

Exit:
	return ErrStat;
} // MIB_multicast_leaf_func



// MIB_err_type_leaf_func
//    Performs actions on the dlcmi multicast variable in the MIB.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT MIB_err_type_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_INTEGER:
			if (MibPtr->pSxbRawData != NULL)
				 VarBind->value.asnValue.number = *((AsnInteger*)MibPtr->pSxbRawData);
			 else
				 VarBind->value.asnValue.number = *(AsnInteger *)(MibPtr->Storage);

			 // Readjust to be NonBraoadcast, or Broadcast
			 switch (VarBind->value.asnValue.number)
			 {
			 case 0:
				 VarBind->value.asnValue.number = FREL_ERR_TYPE_NO_ERR_SINCE_RESET;
				 break;
			 case 1:
				 VarBind->value.asnValue.number = FREL_ERR_TYPE_RECEIVE_SHORT;
				 break;
			 case 2:
				 VarBind->value.asnValue.number = FREL_ERR_TYPE_RECEIVE_LONG;
				 break;
			 case 3:
				 VarBind->value.asnValue.number = FREL_ERR_TYPE_ILLEGAL_DLCI;
				 break;
			 case 4:
				 VarBind->value.asnValue.number = FREL_ERR_TYPE_UNKNOWN_DLCI;
				 break;
			 case 5:
				 VarBind->value.asnValue.number = FREL_ERR_TYPE_DLCMI_PROTO_ERR;
				 break;
			 case 6:
				 VarBind->value.asnValue.number = FREL_ERR_TYPE_DLCMI_UNKNOWN_IE;
				 break;
			 case 7:
				 VarBind->value.asnValue.number = FREL_ERR_TYPE_DLCMI_SEQUENCE_ERR;
				 break;
			 case 8:
				 VarBind->value.asnValue.number = FREL_ERR_TYPE_DLCMI_UNKNOWN_RPT;
				 break;
			 default:
				 VarBind->value.asnValue.number = FREL_ERR_TYPE_UNKNOWN_ERR;
				 break;
			 }
			 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_TIMETICKS:
			*(AsnInteger *)(MibPtr->Storage) = VarBind->value.asnValue.number - dwTimeZero;
			break;

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

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

	// Signal no error occurred
	ErrStat = SNMP_ERRORSTATUS_NOERROR;

Exit:
	return ErrStat;
} // MIB_err_type_leaf_func



// MIB_circuit_state_leaf_func
//    Performs actions on the circuit state variable in the MIB.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT MIB_circuit_state_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_INTEGER:
			if (MibPtr->pSxbRawData != NULL)
				 VarBind->value.asnValue.number = *((AsnInteger*)MibPtr->pSxbRawData);
			 else
				 VarBind->value.asnValue.number = *(AsnInteger *)(MibPtr->Storage);

			 // Readjust to be NonBraoadcast, or Broadcast
			 if (VarBind->value.asnValue.number == 0)
				 VarBind->value.asnValue.number = FREL_CKT_STATE_INACTIVE;
			 else
				 VarBind->value.asnValue.number = FREL_CKT_STATE_ACTIVE;
		     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_TIMETICKS:
			*(AsnInteger *)(MibPtr->Storage) = VarBind->value.asnValue.number - dwTimeZero;
			break;

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

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

	// Signal no error occurred
	ErrStat = SNMP_ERRORSTATUS_NOERROR;

Exit:
	return ErrStat;
} // MIB_circuit_state_leaf_func



// MIB_lmi_type_leaf_func
//    Performs actions on the circuit state variable in the MIB.
//
// Notes:
//
// Return Codes:
//    Standard PDU error codes.
//
// Error Codes:
//    None.
//
UINT MIB_lmi_type_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_INTEGER:
			if (MibPtr->pSxbRawData != NULL)
			{
				switch(*((uint16*)MibPtr->pSxbRawData) & FRS_LINK_FLAGS_LMI_MASK) {
				case FRS_LINK_FLAGS_LMI_REV1:
					VarBind->value.asnValue.number = FREL_DLCMI_STATE_LMI_REV_1;
					break;
				
				case FRS_LINK_FLAGS_GANG_OF_4:
					VarBind->value.asnValue.number = FREL_DLCMI_STATE_NO_LMI_CONFIGURED;
					break;
				
				case FRS_LINK_FLAGS_ANNEX_D:
					VarBind->value.asnValue.number = FREL_DLCMI_STATE_ANSI_T1_617_D;
					break;
				
				case FRS_LINK_FLAGS_ANNEX_A:
					VarBind->value.asnValue.number = FREL_DLCMI_STATE_ANSI_T1_617_B;
					break;
				
				default:
					VarBind->value.asnValue.number = FREL_DLCMI_STATE_NO_LMI_CONFIGURED;
					break;
				}
			 }
			 else
			 {
				 VarBind->value.asnValue.number = *(AsnInteger *)(MibPtr->Storage);
			 }

			 // Readjust to be NonBraoadcast, or Broadcast
			 if (VarBind->value.asnValue.number == 0)
				 VarBind->value.asnValue.number = FREL_CKT_STATE_INACTIVE;
			 else
				 VarBind->value.asnValue.number = FREL_CKT_STATE_ACTIVE;
		     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_TIMETICKS:
			*(AsnInteger *)(MibPtr->Storage) = VarBind->value.asnValue.number - dwTimeZero;
			break;

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

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

	// Signal no error occurred
	ErrStat = SNMP_ERRORSTATUS_NOERROR;

Exit:
	return ErrStat;
} // MIB_lmi_type_leaf_func



// frel_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 frel_Update_MIB_Data(long pFrelSxbInfo)
{
	BOOL						bSuccess;
	BOOL						done = FALSE;
	PFREL_SXB_INFO				pBaseFrelSxbInfo = (PFREL_SXB_INFO)pFrelSxbInfo;
	PFREL_SXB_INFO				pCurFrelSxbInfo;
	PX_IOCTL					pCurSxbIoctl;
	int							ndx=0;
	DWORD						returnStatus = 0;
	FRI_GetLinkStatistics_t  *	pFrelErrStats;		// Pointer to the error statistics area
	rfc_1315_dlcmi_t		 *	pFrelDlcmiConfig;		// Pointer to the dlcmi config area
	rfc_1315_circuit_t	     *	pFrelCircuitStats;	// Pointer to the circuit config area
	ULONG						curSnid;
	CHAR						curDevName[SXB_DEVICE_NAME_SIZE];
	BOOL						handleOpen = FALSE;
	UINT					retryCount;

	// Open a file handle to each port required
	if (( pCurFrelSxbInfo = pBaseFrelSxbInfo ) == NULL ) {
#ifdef SNMP_DLL_DEBUG
		OutputDebugString("***E*** FREL: No input parameter to SXB collector\n");
#endif
		return -1;
	}

    while (pCurFrelSxbInfo != NULL)
    {
		// APP FILE HANDLE OPEN:
		// Open an application file handle to the SXB driver
		curSnid = pCurFrelSxbInfo->Snid;
		strcpy(curDevName,pCurFrelSxbInfo->DeviceName);

        if( (pCurFrelSxbInfo->hSxbPort = 
                    CreateFile(  pCurFrelSxbInfo->DeviceName,
			                     GENERIC_READ | GENERIC_WRITE,
			                     FILE_SHARE_READ | FILE_SHARE_WRITE,
			                     NULL,
			                     OPEN_EXISTING,
			                     FILE_ATTRIBUTE_NORMAL,
			                     NULL )) == INVALID_HANDLE_VALUE )
        {
			returnStatus = GetLastError();
#ifdef SNMP_DLL_DEBUG
			wsprintf(DbgBuf,
					"***E*** Frel: Couldn't open file handle to adapter %s, Error = 0x%x\n", pCurFrelSxbInfo->DeviceName, returnStatus);
			OutputDebugString(DbgBuf);
#endif
			return returnStatus;
        }

		// 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(Open.DeviceName, ID_STR_SXB_FREL_STATS );
			bSuccess = DeviceIoControl( pCurFrelSxbInfo->hSxbPort,
										(ULONG)IOCTL_DS_OPEN,
										(LPVOID)&Open, sizeof( Open ),
										(LPVOID)&Open, sizeof( Open ),
										&dwBytes, NULL );
			pCurFrelSxbInfo->VCFrelDevice = Open.hStream;
			if( !bSuccess || Open.ErrorCode )
			{
				returnStatus = GetLastError();
#ifdef SNMP_DLL_DEBUG
				wsprintf(DbgBuf,
				         "***E*** FREL: IOCTL_DS_OPEN failed 0x%x, ErrorCode = 0x%x\n",
							returnStatus, Open.ErrorCode);
				OutputDebugString(DbgBuf);
#endif
				retryCount++;
				StrToWStr(  pCurFrelSxbInfo->FrelName,
							ID_STR_SXB_FREL_STATS,
							strlen(ID_STR_SXB_FREL_STATS) + 1 );
				SleepEx(10,FALSE);
			}
		}

		if (!bSuccess)
		{
			return returnStatus;
		}

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

				if (strcmp(curDevName,pCurFrelSxbInfo->DeviceName) == 0)
				{
					pCurFrelSxbInfo->hSxbPort = 
						pCurFrelSxbInfo->pPrevious->hSxbPort;
					pCurFrelSxbInfo->VCFrelDevice =
						pCurFrelSxbInfo->pPrevious->VCFrelDevice;
				}
			}
			else
			{
				break;
			}
		} while ((pCurFrelSxbInfo != NULL) && 
				(strcmp(curDevName,pCurFrelSxbInfo->DeviceName) == 0));

    } //end_while

    // If the FREL User ports opened okay, start collecting stats
	ndx = 0;
	do
    {
		pCurFrelSxbInfo = pBaseFrelSxbInfo; 

		while (pCurFrelSxbInfo != 0)
		{
			//
			// Okay, lets send down the requests for stats and config!
			//
			pCurSxbIoctl = &(pCurFrelSxbInfo->XIoctl);

				// Do IOCTL specific init
				switch (pCurFrelSxbInfo->IoctlType) {
				case PORT_IOCTL_NDX_ERROR_STATS:
					pFrelErrStats = (FRI_GetLinkStatistics_t *)&pCurSxbIoctl->Data;
					// Clear out the storage area
					memset( pFrelErrStats, '\0', sizeof(FRI_GetLinkStatistics_t) );

					pCurSxbIoctl->DsIoctl.Command = FRI_GetLinkStatistics;
					pCurFrelSxbInfo->upData.pFrelErrStats = (PVOID)&(pCurSxbIoctl->Data);
					pCurSxbIoctl->DsIoctl.cbDataIn = pCurSxbIoctl->DsIoctl.cbMaxDataOut = sizeof(FRI_GetLinkStatistics_t);
//------------------------------------------------------------ MLZ 7/9/97 {
#ifdef OLD_FR_WITHOUT_SNIDS
					pFrelErrStats->link = (USHORT)pCurFrelSxbInfo->Snid;
#else
					pFrelErrStats->snid = pCurFrelSxbInfo->Snid;
#endif
//------------------------------------------------------------ MLZ 7/9/97 }
					break;

				case PORT_IOCTL_NDX_DLCMI_CONFIG:
					pFrelDlcmiConfig = (rfc_1315_dlcmi_t *)&pCurSxbIoctl->Data;
					// Clear out the storage area
					memset( pFrelDlcmiConfig, '\0', sizeof(rfc_1315_dlcmi_t) );

					pCurSxbIoctl->DsIoctl.Command = RFC_1315_DLCMI;
					pCurFrelSxbInfo->upData.pFrelDlcmiData = (PVOID)&(pCurSxbIoctl->Data);
					pCurSxbIoctl->DsIoctl.cbDataIn = pCurSxbIoctl->DsIoctl.cbMaxDataOut = sizeof(rfc_1315_dlcmi_t);
					pFrelDlcmiConfig->frDlcmiIndex = pCurFrelSxbInfo->Snid;
					break;

				case PORT_IOCTL_NDX_CIRCUIT_STATS:
					pFrelCircuitStats = (rfc_1315_circuit_t *)&pCurSxbIoctl->Data;
					// Clear out the storage area
					memset( pFrelCircuitStats, '\0', sizeof(rfc_1315_circuit_t) );

					pCurSxbIoctl->DsIoctl.Command = RFC_1315_CIRCUIT;
					pCurFrelSxbInfo->upData.pFrelCircuitData = (PVOID)&(pCurSxbIoctl->Data);
					pCurSxbIoctl->DsIoctl.cbDataIn = pCurSxbIoctl->DsIoctl.cbMaxDataOut = sizeof(rfc_1315_circuit_t);
					pFrelCircuitStats->frCircuitIndex = pCurFrelSxbInfo->Snid;
					pFrelCircuitStats->frCircuitDlci = pCurFrelSxbInfo->Dlci;
					break;

				default:
				// The IOCTL list is corrupt, we can't continue to collect data
#ifdef SNMP_DLL_DEBUG
					OutputDebugString("***E*** WAN: Built BAD IOCTL type\n");
#endif
					return -1;
					break;
				}

				// Do IOCTL independent init
				pCurSxbIoctl->DsIoctl.hStream = pCurFrelSxbInfo->VCFrelDevice;
				pCurSxbIoctl->DsIoctl.pDataIn = pCurSxbIoctl->DsIoctl.pDataOut = (PVOID)&pCurSxbIoctl->Data;

				// Now that we've built an IOCTL, send it down to SXB
				bSuccess = DeviceIoControl( pCurFrelSxbInfo->hSxbPort,
											(ULONG)IOCTL_DS_IOCTL,
											(PVOID)&pCurSxbIoctl->DsIoctl,
											sizeof(DS_IOCTL),
											(PVOID)&pCurSxbIoctl->DsIoctl,
											sizeof(DS_IOCTL),
											&(pCurFrelSxbInfo->dwBytesWritten),
											NULL );
				if( !bSuccess  )
				{
					returnStatus = GetLastError();
#ifdef SNMP_DLL_DEBUG
					wsprintf(DbgBuf,
					         "***W*** FR: Failure on IOCTL_DS_IOCTL, error 0x%x\n", 
					         returnStatus);
					OutputDebugString(DbgBuf);
#endif
				}
				else
				{	
					if ( pCurSxbIoctl->DsIoctl.ReturnValue || 
					     pCurSxbIoctl->DsIoctl.ErrorCode )
					{
#ifdef SNMP_DLL_DEBUG
						wsprintf(DbgBuf,"***E*** FR: Failure on RETURN of USER IOCTL = %d\n",
										pCurSxbIoctl->DsIoctl.ReturnValue);
						OutputDebugString(DbgBuf);
#endif
						returnStatus = pCurSxbIoctl->DsIoctl.ReturnValue * -1;
					}
					else
					{
					}
				}
			// Go to the next IOCTL
			pCurFrelSxbInfo = pCurFrelSxbInfo->pNext;
		} //end_while

		SleepEx(500, FALSE);
	} while ( WaitForSingleObject( terminateEvent, 0) != WAIT_OBJECT_0);

	return returnStatus;
}  // end frel_Update_MIB_Data



// frel_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 frel_Terminate_MIB_Data_Collection(
			IN PFREL_SXB_INFO pFrelSxbInfo,
			IN PSXB_PORT_INFO pBaseSxbPortInfo)
{
BOOL				bSuccess;
BOOL				firstPassFlag;
PFREL_SXB_INFO      pDelFrelSxbInfo;
PFREL_SXB_INFO      pBaseFrelSxbInfo = pFrelSxbInfo;
PFREL_SXB_INFO      pCurFrelSxbInfo;
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*** FREL: Wait for collection terminate completed\n");
		OutputDebugString(DbgBuf);
#endif
		break;
	
	case WAIT_TIMEOUT:
#ifdef SNMP_DLL_DEBUG
		wsprintf(DbgBuf,"***E*** FREL: Wait for collection terminate timed out\n");
		OutputDebugString(DbgBuf);
#endif
		break;
	
	case WAIT_ABANDONED:
	default:
#ifdef SNMP_DLL_DEBUG
		wsprintf(DbgBuf,
				"***E*** FREL: Wait for collection terminate failed, error=%x\n",
				GetLastError()
				);
		OutputDebugString(DbgBuf);
#endif
		break;
	}

 	if ((pCurFrelSxbInfo = pBaseFrelSxbInfo) == NULL) {
#ifdef SNMP_DLL_DEBUG
	    OutputDebugString("***E*** frel_Terminate_MIB_Data_Collection Failure\n");
#endif
		return;
	}
    
	firstPassFlag = TRUE;
	while (pCurFrelSxbInfo != NULL)
    {
		if (hSxbDevice != pCurFrelSxbInfo->hSxbPort)
		{
            DS_CLOSE Close;
			hSxbDevice = pCurFrelSxbInfo->hSxbPort;
            Close.hStream = pCurFrelSxbInfo->VCFrelDevice;

			bSuccess = DeviceIoControl( hSxbDevice, 
			                            (ULONG)IOCTL_DS_CLOSE,
			                            &Close, sizeof(Close),
			                            &Close, sizeof(Close),
			                            &(pCurFrelSxbInfo->dwBytesWritten), NULL );

			if( !bSuccess )
			{
				returnStatus = GetLastError();
#ifdef SNMP_DLL_DEBUG
				wsprintf(DbgBuf,"***W*** FREL: Failure on IOCTL_DS_CLOSE\n= %x\n",
								returnStatus);
				OutputDebugString(DbgBuf);
#endif
				hSxbDevice = NULL;
			}
			else
			{
				CloseHandle( pCurFrelSxbInfo->hSxbPort);
			}
		} // end if
		pCurFrelSxbInfo->hSxbPort = NULL;
		pDelFrelSxbInfo = pCurFrelSxbInfo;
		pCurFrelSxbInfo = pCurFrelSxbInfo->pNext;

		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(pDelFrelSxbInfo);
		}
	} // 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->pNext;
			SNMP_free(pCircuitInfo);
			pCircuitInfo = pNextCircuitInfo;
		}

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



// frel_Init_MIB_Data_Structures:
//  Initializes the structures needed to update the MIB structure
//	and collect data from the SXB driver
//
// Notes:
//	The routine will build IOCTls in a linked list structure that will
//	update the Mib variables as required by RFC 1315. At his time, the 
//	SXB firmware structures for config and stats will be required for the 
//	DLCMI and error tables, and the link stats and config structures will
//	be required for the circuit tables
//
// Return Codes:
//
// Error Codes:
//
long frel_Init_MIB_Data_Structures(
			IN PFREL_SXB_INFO pFrelSxbInfo,
			IN PSXB_PORT_INFO pSxbPortInfo )
{
   
	PFREL_SXB_INFO		pCurFrelSxbInfo;
	PSXB_PORT_INFO		pCurSxbPortInfo;
	BOOL				firstTimeThrough = TRUE;
	PSXB_CIRCUIT_INFO	pCurCircuitList = NULL;
	int					vcNdx;

	if ((pCurFrelSxbInfo = pFrelSxbInfo) == NULL)
	{
		//DebugBreak();
		return -1;
	}

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

    // Initialize the FrelSxbInfo structures to access the Data Structures

	// The first list item has no previous list item
	pCurFrelSxbInfo->pPrevious = NULL;

	while (pCurSxbPortInfo != NULL)
	{
		// Build the first one for stats
		strcpy(pCurFrelSxbInfo->DeviceName, "\\\\.\\");
		strcat(pCurFrelSxbInfo->DeviceName, pCurSxbPortInfo->DeviceName);
		pCurFrelSxbInfo->IoctlType = PORT_IOCTL_NDX_ERROR_STATS;
		pCurFrelSxbInfo->AdapterId = 0;
		pCurFrelSxbInfo->PortId = 0;
		pCurFrelSxbInfo->Snid = pCurSxbPortInfo->Snid;
		pCurFrelSxbInfo->PortId = pCurSxbPortInfo->WANLinksPortId;
		pCurFrelSxbInfo->upData.pFrelErrStats = (PVOID)&(pCurFrelSxbInfo->XIoctl.Data);
		//The frel statistics for the port are identified by REL_PORT_NAME
		StrToWStr(  pCurFrelSxbInfo->FrelName,
					ID_STR_SXB_FREL_STATS,
					strlen(ID_STR_SXB_FREL_STATS) + 1 );

		// Allocate the first config IOCTL
		pCurFrelSxbInfo->pNext = SNMP_malloc(sizeof(struct FREL_SXB_INFO));
		if (pCurFrelSxbInfo->pNext == NULL )
		{
			//DebugBreak();
			return 4;
		}

		// Go ahead and init the previous pointer for the CONFIG IOCTL
		pCurFrelSxbInfo->pNext->pPrevious = pCurFrelSxbInfo;

		// Build the first one for config
		pCurFrelSxbInfo = pCurFrelSxbInfo->pNext;

		pCurFrelSxbInfo->IoctlType = PORT_IOCTL_NDX_DLCMI_CONFIG;
		strcpy(pCurFrelSxbInfo->DeviceName, "\\\\.\\");
		strcat(pCurFrelSxbInfo->DeviceName, pCurSxbPortInfo->DeviceName);
		pCurFrelSxbInfo->AdapterId = 0;
		pCurFrelSxbInfo->PortId = 0;
		pCurFrelSxbInfo->Snid = pCurSxbPortInfo->Snid;
		pCurFrelSxbInfo->PortId = pCurSxbPortInfo->WANLinksPortId;
		pCurFrelSxbInfo->upData.pFrelDlcmiData = (PVOID)&(pCurFrelSxbInfo->XIoctl.Data);
		//The frel statistics for the port are identified by FREL_PORT_NAME
		StrToWStr(  pCurFrelSxbInfo->FrelName,
					ID_STR_SXB_FREL_STATS,
					strlen(ID_STR_SXB_FREL_STATS) + 1 );

		// Work off the Circuit structures required.
		pCurCircuitList = pCurSxbPortInfo->pCircuitList;
		for (vcNdx = 0; vcNdx < pCurSxbPortInfo->CircuitCount; vcNdx++)
		{
			// Allocate the VC stats IOCTL
			pCurFrelSxbInfo->pNext = SNMP_malloc(sizeof(struct FREL_SXB_INFO));
			if (pCurFrelSxbInfo->pNext == NULL )
			{
				//DebugBreak();
				return 4;
			}

			// Go ahead and init the previous pointer for the Circuit Stats IOCTL
			pCurFrelSxbInfo->pNext->pPrevious = pCurFrelSxbInfo;

			// Build the first one for circuit stats
			pCurFrelSxbInfo = pCurFrelSxbInfo->pNext;

			pCurFrelSxbInfo->IoctlType = PORT_IOCTL_NDX_CIRCUIT_STATS;
			strcpy(pCurFrelSxbInfo->DeviceName, "\\\\.\\");
			strcat(pCurFrelSxbInfo->DeviceName, pCurSxbPortInfo->DeviceName);
			pCurFrelSxbInfo->AdapterId = 0;
			pCurFrelSxbInfo->Dlci = pCurCircuitList->circuit;							;
			pCurFrelSxbInfo->PortId = 0;
			pCurFrelSxbInfo->PortId = pCurSxbPortInfo->WANLinksPortId;
			pCurFrelSxbInfo->Snid = pCurSxbPortInfo->Snid;
			pCurFrelSxbInfo->upData.pFrelCircuitData = (PVOID)&(pCurFrelSxbInfo->XIoctl.Data);
			//The frel statistics for the port are identified by FREL_PORT_NAME
			StrToWStr(  pCurFrelSxbInfo->FrelName,
						ID_STR_SXB_FREL_STATS,
						strlen(ID_STR_SXB_FREL_STATS) + 1 );

			// Allocate the VC config IOCTL
			pCurFrelSxbInfo->pNext = SNMP_malloc(sizeof(struct FREL_SXB_INFO));
			if (pCurFrelSxbInfo->pNext == NULL )
			{
				//DebugBreak();
				return 4;
			}

			// Go ahead and init the previous pointer for the Circuit Stats IOCTL
			pCurFrelSxbInfo->pNext->pPrevious = pCurFrelSxbInfo;

			// Advance to the next circuit in the circuit list
			pCurCircuitList = pCurCircuitList->pNext;
		} //end for (circuit list)

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

		if (pCurSxbPortInfo != NULL)
		{
			pCurFrelSxbInfo->pNext = SNMP_malloc(sizeof(struct FREL_SXB_INFO));
			if (pCurFrelSxbInfo->pNext == NULL )
			{
				//DebugBreak();
				return 4;
			}

			// Go ahead and init the previous pointer for the port 2 stats IOCTL
			pCurFrelSxbInfo->pNext->pPrevious = pCurFrelSxbInfo;
		}
		else
		{
				pCurFrelSxbInfo->pNext = NULL;
		}

		// Work on the next Stats Ioctl
		pCurFrelSxbInfo = pCurFrelSxbInfo ->pNext; 
	} // end while

	return 0;
} //end frel_Init_MIB_Data_Structures()


// frel_Init_Sxb_port_info
//    Initialize the SXB port information from the registry
//
// Notes:
//      The \\HKEY_LOCAL_MACHINE\Sotware\Microsoft\SNMP\Parameters\WanLinksHDLCMIB\
//  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 frel_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];
	UINT				ndx;
	int					i;
	int					j;
	int					k;
	UINT				l;
	BYTE *				pbData;
	DWORD				cbData;
	CHAR				ValueName[MAX_VALUE_NAME];
	DWORD				cbValueName = MAX_VALUE_NAME;
	DWORD				dwType;
	BOOL				firstTimeThrough = TRUE;
	PSXB_PORT_INFO		pCurSxbPortInfo = pSxbPortInfo;
	PSXB_CIRCUIT_INFO   pSxbCircuitInfo = NULL;
	PSXB_CIRCUIT_INFO   pCurSxbCircuitInfo = NULL;
	PSXB_CIRCUIT_INFO   pNextCircuitInfo = NULL;
	CHAR *				pVcStr;
	short				vcVal;
	BOOL				firstCircuit = TRUE;
	CHAR				buildVc[10];
	DWORD				buildNdx;
	DWORD				vcCount;
	BOOL				newStr;
	BOOL				portIsFrameRelay = FALSE;
	int					framePortCount = 0;
	int					totalPortCount = 0;
	CHAR				WANLinksParamtersPath[MAX_PATH];
	DWORD               Snid = 0;

	//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)
				return -1;

			pbData = SNMP_malloc(MAX_DATA_LEN);
			if ( !pbData )
				return -1;

			// 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 port's 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;
					}
					
					// The key for the port has opened, determine it's contents
					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) 
								return -1;
						}
						else
						{
							// Determine the type of key and add it into the port structure
							if (strcmp(ValueName, c_pszSNMP_Circuits) == 0)
							{

								// Parse the circuits list and add them in
								//Calculate the number of VC's in the key
								newStr = TRUE; //Keeps from counting NULL strings
								for (l=0, vcCount=0; l < cbData; l++)
								{
									if (!pbData[l] && !newStr)
									{
										vcCount++;
										newStr = TRUE;
									}
									else
									{
										newStr = FALSE;
									}
								}

								// Gather all the VCs from the registry data
								// Convert them into numbers
								// Add into the VC list
								// Update the circuit count for the port in it's sxb info structure
								firstCircuit = TRUE;
								pVcStr = pbData;
								for (ndx = 0; ndx < vcCount; ndx++)
								{	
									//Look for the next NULL character that delimits UNICODE strings
									buildNdx = 0;
									while ((buildVc[buildNdx++] = *pVcStr++) != '\0')
									{
									}
									vcVal = (short)atoi(buildVc);

									// The first circuit in the chain needs to attach 
									// to the  Port Info Structure
									if (firstCircuit)
									{
										if ((pSxbCircuitInfo
											= SNMP_malloc(sizeof(struct SXB_CIRCUIT_INFO))) 
												== NULL)
										{
											//DebugBreak();
											return -1;
										}
										pCurSxbCircuitInfo = pSxbCircuitInfo;
										pCurSxbCircuitInfo->pPrevious = NULL;
										pCurSxbCircuitInfo->pNext = NULL;
										firstCircuit = FALSE;
									}
									else
									{
										if ((pCurSxbCircuitInfo->pNext
											= SNMP_malloc(sizeof(struct SXB_CIRCUIT_INFO))) 
												== NULL)
										{
											//DebugBreak();
											return -1;
										}
										pCurSxbCircuitInfo->pNext->pPrevious 
											= pCurSxbCircuitInfo;
										pCurSxbCircuitInfo->pNext->pNext
											= NULL;
										pCurSxbCircuitInfo
											= pCurSxbCircuitInfo->pNext;
									} // end if
									pCurSxbCircuitInfo->circuit = vcVal;
								} //end for
							}
							// Check for protocol identifier registry key
							else if (strcmp(ValueName, c_pszSNMP_ProtocolIdentifier) == 0)
							{
								// Test for frame relay
								if (strcmp(pbData, c_pszValueFRProtocol) == 0)
								{
									framePortCount++;
									portIsFrameRelay = TRUE;
								}
								else 
								{
									portIsFrameRelay = FALSE;
								}
							}
							else if ( strcmp(ValueName, DXB_REGVAL_LINE_SNID)==0 )
							{
								PDWORD 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 (portIsFrameRelay)
					{
						//// If not we need to tack a new one on the end of thelist
						if (firstTimeThrough)
						{
							firstTimeThrough = FALSE;
							pCurSxbPortInfo->pPrevious = NULL;
						}
						else
						{	
							pCurSxbPortInfo->pNext = SNMP_malloc(sizeof(SXB_PORT_INFO));
							pCurSxbPortInfo->pNext->pPrevious = pCurSxbPortInfo;
							pCurSxbPortInfo = pCurSxbPortInfo->pNext;
							pCurSxbPortInfo->pNext = NULL;
						}

						// Initialize the Variables in the port info structure
						portIsFrameRelay = FALSE;
						
						// Save the name of the NT Device used to access this port
						// It looks like "DigiSxb2"
						strcpy(pCurSxbPortInfo->DeviceName, AdapterKeyName);

//------------------------------------------------------------ MLZ 1/3/2000 {
						// Use the snid we read from registry.  Old code that builds
						// snid based on regkey names has been deleted.
						pCurSxbPortInfo->Snid = Snid;
//------------------------------------------------------------ MLZ 1/3/2000 }

						//Update the remainder of the SXB Info
						pCurSxbPortInfo->ProtocolId = SXB_PROTOCOL_ID_FRAME_RELAY;
						pCurSxbPortInfo->WANLinksPortId = totalPortCount;

						// Add the Sxb Circuit List to the Port Info structure
						pCurSxbPortInfo->CircuitCount = vcCount;
						pCurSxbPortInfo->pCircuitList = pSxbCircuitInfo;
						pCurSxbCircuitInfo = NULL; //The list has been reallocated
					}
					else //Port isn't frame relay, so release the circuit list elements
					{
						pCurSxbCircuitInfo = pSxbCircuitInfo;
						while ( pCurSxbCircuitInfo != NULL )
						{
							pNextCircuitInfo = pCurSxbCircuitInfo->pNext;
							SNMP_free(pCurSxbCircuitInfo);
							pCurSxbCircuitInfo = pNextCircuitInfo;
						}
					}
					
					// 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 = framePortCount) == 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
