/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    llcapi.h

Abstract:

    This module defined the kernel API of data link driver.
    All function prototypes and typedefs of the interface
    have been defined here.

Author:

    Antti Saarenheimo (o-anttis) 17-MAY-1991

Revision History:

--*/

#ifndef _LLC_API_
#define _LLC_API_

#include "llcmem.h"

//
//  The debug switches
//

//
//  LLC_DBG:
//
//  0   => No debug code is generated
//  1   => Enables the memory allocation accounting, state machine tracing,
//         procedure call tracing and internal consistency checks
//  2   => Enables memory block overflow checking
//
//

#define LLC_DBG  0
#define DBG_MP   0   // enables the MP safe versions of the accounting macros

#define DLC_TRACE_ENABLED 1	// Procedure call tracing, debug only.

extern NDIS_SPIN_LOCK DlcDriverLock;

//
// ANY_IRQL: pseudo value used in ASSUME_IRQL to mean routine is not IRQL sensitive.
// Use this value with ASSUME_IRQL instead of omitting ASSUME_IRQL from a routine
// (shows that we didn't forget this routine)
//

#define ANY_IRQL    ((ULONG)-1)

#if defined(LOCK_CHECK)

extern LONG DlcDriverLockLevel;
extern ULONG __line;
extern PCHAR __file;
extern LONG __last;
extern HANDLE __process;
extern HANDLE __thread;

//
// _strip - quick functionette to strip out the path garbage from __FILE__
//

__inline char* _strip(char* s) {

    char* e = s + strlen(s) - 1;

    while (e != s) {
        if (*e == '\\') {
            return e + 1;
        }
        --e;
    }
    return s;
}

#define $$_PLACE            "%s!%d"
#define $$_FILE_AND_LINE    _strip(__FILE__), __LINE__

//
// ACQUIRE_DRIVER_LOCK - acquires the global DLC driver Spin Lock, using
// NdisAcquireSpinLock(). We also check for re-entrancy and incorrect ordering
// of spin lock calls
//

#define ACQUIRE_DRIVER_LOCK() \
{ \
    KIRQL currentIrql; \
    HANDLE hprocess; \
    HANDLE hthread; \
\
    currentIrql = KeGetCurrentIrql(); \
    if (currentIrql == PASSIVE_LEVEL) { \
\
        PETHREAD pthread; \
\
        pthread = PsGetCurrentThread(); \
        hprocess = pthread->Cid.UniqueProcess; \
        hthread = pthread->Cid.UniqueThread; \
    } else { \
        hprocess = (HANDLE)-1; \
        hthread = (HANDLE)-1; \
    } \
    NdisAcquireSpinLock(&DlcDriverLock); \
    if (++DlcDriverLockLevel != 1) { \
        __last = DlcDriverLockLevel; \
        DbgPrint("%d.%d:" $$_PLACE ": ACQUIRE_DRIVER_LOCK: level = %d. Last = %d.%d:" $$_PLACE "\n", \
                 hprocess, \
                 hthread, \
                 $$_FILE_AND_LINE, \
                 DlcDriverLockLevel, \
                 __process, \
                 __thread, \
                 __file, \
                 __line \
                 ); \
        DbgBreakPoint(); \
    } \
    __file = _strip(__FILE__); \
    __line = __LINE__; \
    __process = hprocess; \
    __thread = hthread; \
    ASSUME_IRQL(DISPATCH_LEVEL); \
}

//
// RELEASE_DRIVER_LOCK - releases the global DLC driver Spin Lock, using
// NdisReleaseSpinLock(). We also check for re-entrancy and incorrect ordering
// of spin lock calls
//

#define RELEASE_DRIVER_LOCK() \
    if (DlcDriverLockLevel != 1) { \
        DbgPrint($$_PLACE ": RELEASE_DRIVER_LOCK: level = %d. Last = %d.%d:" $$_PLACE "\n", \
                 $$_FILE_AND_LINE, \
                 DlcDriverLockLevel, \
                 __process, \
                 __thread, \
                 __file, \
                 __line \
                 ); \
        DbgBreakPoint(); \
    } \
    --DlcDriverLockLevel; \
    __file = _strip(__FILE__); \
    __line = __LINE__; \
    NdisReleaseSpinLock(&DlcDriverLock);

//
// ASSUME_IRQL - used to check that a routine is being called at the IRQL we
// expect. Due to the design of DLC, most functions are called at raised IRQL
// (DISPATCH_LEVEL). Used mainly to assure that IRQL is at PASSIVE_LEVEL when
// we do something which may incur a page fault e.g.
//

#define ASSUME_IRQL(level) \
    if (((level) != ANY_IRQL) && (KeGetCurrentIrql() != (level))) { \
        DbgPrint($$_PLACE ": ASSUME_IRQL(%d): Actual is %d\n", \
                 $$_FILE_AND_LINE, \
                 level, \
                 KeGetCurrentIrql() \
                 ); \
        DbgBreakPoint(); \
    }

//
// MY_ASSERT - since ASSERT only expands to something meaningful in the checked
// build, we use this when we want an assertion check in a free build
//

#define MY_ASSERT(x) \
    if (!(x)) { \
        DbgPrint($$_PLACE ": Assertion Failed: " # x "\n", \
                 $$_FILE_AND_LINE \
                 ); \
        DbgBreakPoint(); \
    }

//
// IF_LOCK_CHECK - conditional compilation made cleaner
//

#define IF_LOCK_CHECK \
    if (TRUE)

#else

#define ACQUIRE_DRIVER_LOCK()   NdisAcquireSpinLock(&DlcDriverLock)
#define RELEASE_DRIVER_LOCK()   NdisReleaseSpinLock(&DlcDriverLock)
#define ASSUME_IRQL(level)      /* NOTHING */
#define MY_ASSERT(x)            /* NOTHING */
#define IF_LOCK_CHECK           if (FALSE)

#endif

//
// in the Unilock DLC, we do not need the LLC spin-lock
//

#if defined(DLC_UNILOCK)

#define ACQUIRE_LLC_LOCK(i)
#define RELEASE_LLC_LOCK(i)

#define ACQUIRE_SPIN_LOCK(p)
#define RELEASE_SPIN_LOCK(p)

#define ALLOCATE_SPIN_LOCK(p)
#define DEALLOCATE_SPIN_LOCK(p)

#else

#define ACQUIRE_LLC_LOCK(i)     KeAcquireSpinLock(&LlcSpinLock, (i))
#define RELEASE_LLC_LOCK(i)     KeReleaseSpinLock(&LlcSpinLock, (i))

#define ACQUIRE_SPIN_LOCK(p)    NdisAcquireSpinLock((p))
#define RELEASE_SPIN_LOCK(p)    NdisReleaseSpinLock((p))

#define ALLOCATE_SPIN_LOCK(p)   KeInitializeSpinLock(&(p)->SpinLock)
#define DEALLOCATE_SPIN_LOCK(p)

#endif

//
// IS_SNA_DIX_FRAME - TRUE if the frame just received & therefore described in
// the ADAPTER_CONTEXT (p) has DIX framing (SNA)
//

#define IS_SNA_DIX_FRAME(p) \
    (((PADAPTER_CONTEXT)(p))->IsSnaDixFrame)

//
// IS_AUTO_BINDING - TRUE if the BINDING_CONTEXT was created with
// LLC_ETHERNET_TYPE_AUTO
//

#define IS_AUTO_BINDING(p) \
    (((PBINDING_CONTEXT)(p))->EthernetType == LLC_ETHERNET_TYPE_AUTO)

#define FRAME_MASK_LLC_LOCAL_DEST       0x0001
#define FRAME_MASK_NON_LLC_LOCAL_DEST   0x0002
#define FRAME_MASK_NON_LOCAL_DEST       0x0004
#define FRAME_MASK_ALL_FRAMES           0x0007

#define LLC_EXCLUSIVE_ACCESS            0x0001
#define LLC_HANDLE_XID_COMMANDS         0x0002

//
// Direct station receive flags (bits 0 and 1 are inverted from dlcapi!!!)
//

#define DLC_RCV_SPECIFIC_DIX            0
#define DLC_RCV_MAC_FRAMES              1
#define DLC_RCV_8022_FRAMES             2
#define DLC_RCV_DIX_FRAMES              4
#define LLC_VALID_RCV_MASK              7
#define DLC_RCV_OTHER_DESTINATION       8

#define MAX_LLC_FRAME_TYPES 10

//
// The DLC link states reported by DLC API
//

enum _DATA_LINK_STATES {

    //
    // Primary states
    //

    LLC_LINK_CLOSED             = 0x80,
    LLC_DISCONNECTED            = 0x40,
    LLC_DISCONNECTING           = 0x20,
    LLC_LINK_OPENING            = 0x10,
    LLC_RESETTING               = 0x08,
    LLC_FRMR_SENT               = 0x04,
    LLC_FRMR_RECEIVED           = 0x02,
    LLC_LINK_OPENED             = 0x01,

    //
    // Secondary states (when primary state is LLC_LINK_OPENED)
    //

    LLC_CHECKPOINTING           = 0x80,
    LLC_LOCAL_BUSY_USER_SET     = 0x40,
    LLC_LOCAL_BUSY_BUFFER_SET   = 0x20,
    LLC_REMOTE_BUSY             = 0x10,
    LLC_REJECTING               = 0x08,
    LLC_CLEARING                = 0x04,
    LLC_DYNMIC_WIN_ALG_RUNNIG   = 0x02,
    LLC_NO_SECONDARY_STATE      = 0
};

//
// LAN802_ADDRESS - 8 bytes of frame address. Typically 6 bytes LAN address
// plus 1 byte destination SAP, plus 1 byte source SAP
//

typedef union {

    struct {
        UCHAR DestSap;
        UCHAR SrcSap;
        USHORT usHigh;
        ULONG ulLow;
    } Address;

    struct {
        ULONG High;
        ULONG Low;
    } ul;

    struct {
        USHORT Raw[4];
    } aus;

    struct {
        UCHAR DestSap;
        UCHAR SrcSap;
        UCHAR auchAddress[6];
    }  Node;

    UCHAR auchRawAddress[8];

} LAN802_ADDRESS, *PLAN802_ADDRESS;

//
// Structure is used by DlcNdisRequest function
//

typedef struct {
    NDIS_STATUS AsyncStatus;
    NDIS_REQUEST Ndis;
} LLC_NDIS_REQUEST, *PLLC_NDIS_REQUEST;

#define NDIS_INFO_BUF_SIZE          20

#define DLC_ANY_STATION             (-1)

//
// Internal event flags used by Timer, DlcConnect
// and DlcClose commands.
//

#define DLC_REPEATED_FLAGS          0x0700
#define LLC_TIMER_TICK_EVENT        0x0100
#define LLC_STATUS_CHANGE_ON_SAP    0x0800

//
// These enum types are used also as the index of a mapping table!
//

enum _LLC_OBJECT_TYPES {
    LLC_DIRECT_OBJECT,
    LLC_SAP_OBJECT,
    LLC_GROUP_SAP_OBJECT,
    LLC_LINK_OBJECT,
    LLC_DIX_OBJECT
};

//
// We moved these defines here because the macro is used by data link
//

#define MIN_DLC_BUFFER_SEGMENT      256
////#define MAX_DLC_BUFFER_SEGMENT      4096
//#define MAX_DLC_BUFFER_SEGMENT      8192
#define MAX_DLC_BUFFER_SEGMENT      PAGE_SIZE

#define BufGetPacketSize( PacketSize ) \
                (((PacketSize) + 2 * MIN_DLC_BUFFER_SEGMENT - 1) & \
                -MIN_DLC_BUFFER_SEGMENT)

//
// READ Event flags:
//

#define DLC_READ_FLAGS              0x007f
#define LLC_SYSTEM_ACTION           0x0040
#define LLC_NETWORK_STATUS          0x0020
#define LLC_CRITICAL_EXCEPTION      0x0010
#define LLC_STATUS_CHANGE           0x0008
#define LLC_RECEIVE_DATA            0x0004
#define LLC_TRANSMIT_COMPLETION     0x0002
#define DLC_COMMAND_COMPLETION      0x0001

#define ALL_DLC_EVENTS              -1

//
// LLC_STATUS_CHANGE indications:
//

#define INDICATE_LINK_LOST              0x8000
#define INDICATE_DM_DISC_RECEIVED       0x4000
#define INDICATE_FRMR_RECEIVED          0x2000
#define INDICATE_FRMR_SENT              0x1000
#define INDICATE_RESET                  0x0800
#define INDICATE_CONNECT_REQUEST        0x0400
#define INDICATE_REMOTE_BUSY            0x0200
#define INDICATE_REMOTE_READY           0x0100
#define INDICATE_TI_TIMER_EXPIRED       0x0080
#define INDICATE_DLC_COUNTER_OVERFLOW   0x0040
#define INDICATE_ACCESS_PRTY_LOWERED    0x0020
#define INDICATE_LOCAL_STATION_BUSY     0x0001

//
// LLC Command completion indications.
//

enum _LLC_COMPLETION_CODES {
    LLC_RECEIVE_COMPLETION,
    LLC_SEND_COMPLETION,
    LLC_REQUEST_COMPLETION,
    LLC_CLOSE_COMPLETION,
    LLC_RESET_COMPLETION,
    LLC_CONNECT_COMPLETION,
    LLC_DISCONNECT_COMPLETION
};

typedef union {
    LLC_ADAPTER_INFO Adapter;
    DLC_LINK_PARAMETERS LinkParms;
    LLC_TICKS Timer;
    DLC_LINK_LOG LinkLog;
    DLC_SAP_LOG SapLog;
    UCHAR PermanentAddress[6];
    UCHAR auchBuffer[1];
} LLC_QUERY_INFO_BUFFER, *PLLC_QUERY_INFO_BUFFER;

typedef union {
    DLC_LINK_PARAMETERS LinkParms;
    LLC_TICKS Timers;
    UCHAR auchFunctionalAddress[4];
    UCHAR auchGroupAddress[4];
    UCHAR auchBuffer[1];
} LLC_SET_INFO_BUFFER, *PLLC_SET_INFO_BUFFER;

//
// LLC_FRMR_INFORMATION - 5 bytes of FRaMe Reject code
//

typedef struct {
    UCHAR Command;              // format: mmmpmm11, m=modifiers, p=poll/final.
    UCHAR Ctrl;                 // control field of rejected frame.
    UCHAR Vs;                   // our next send when error was detected.
    UCHAR Vr;                   // our next receive when error was detected.
    UCHAR Reason;               // reason for sending FRMR: 000VZYXW.
} LLC_FRMR_INFORMATION, *PLLC_FRMR_INFORMATION;

//
// DLC_STATUS_TABLE - format of status information returned in a READ command
//

typedef struct {
    USHORT StatusCode;
    LLC_FRMR_INFORMATION FrmrData;
    UCHAR uchAccessPriority;
    UCHAR auchRemoteNode[6];
    UCHAR uchRemoteSap;
    UCHAR uchLocalSap;
    PVOID hLlcLinkStation;
} DLC_STATUS_TABLE, *PDLC_STATUS_TABLE;

typedef struct {
    ULONG IsCompleted;
    ULONG Status;
} ASYNC_STATUS, *PASYNC_STATUS;

union _LLC_OBJECT;
typedef union _LLC_OBJECT LLC_OBJECT, *PLLC_OBJECT;

struct _BINDING_CONTEXT;
typedef struct _BINDING_CONTEXT BINDING_CONTEXT, *PBINDING_CONTEXT;

//
// LLC packet headers
//

//
// LLC_XID_INFORMATION - 3 information bytes in a standard LLC XID packet
//

typedef struct {
    UCHAR FormatId;             // format of this XID frame.
    UCHAR Info1;                // first information byte.
    UCHAR Info2;                // second information byte.
} LLC_XID_INFORMATION, *PLLC_XID_INFORMATION;

//
// LLC_TEST_INFORMATION - information field for TEST frame
//

typedef struct {
    UCHAR Padding[4];
    PMDL pMdl;                  // we keep test MDL in the same slot as U-MDL
} LLC_TEST_INFORMATION, *PLLC_TEST_INFORMATION;

typedef union {
    LLC_XID_INFORMATION Xid;    // XID information.
    LLC_FRMR_INFORMATION Frmr;  // FRMR information.
    LLC_TEST_INFORMATION Test;  // Test MDL pointer
    UCHAR Padding[8];           //
} LLC_RESPONSE_INFO, *PLLC_RESPONSE_INFO;

//
// LLC_U_HEADER - Unnumbered format frame LLC header
//

typedef struct {
    UCHAR Dsap;                 // Destination Service Access Point.
    UCHAR Ssap;                 // Source Service Access Point.
    UCHAR Command;              // command code.
} LLC_U_HEADER, *PLLC_U_HEADER;

//
// LLC_S_HEADER - Supervisory format frame LLC header
//

typedef struct {
    UCHAR Dsap;                 // Destination Service Access Point.
    UCHAR Ssap;                 // Source Service Access Point.
    UCHAR Command;              // RR, RNR, REJ command code.
    UCHAR Nr;                   // receive seq #, bottom bit is poll/final.
} LLC_S_HEADER, *PLLC_S_HEADER;

//
// LLC_I_HEADER - Information frame LLC header
//

typedef struct {
    UCHAR Dsap;                 // Destination Service Access Point.
    UCHAR Ssap;                 // Source Service Access Point.
    UCHAR Ns;                   // send sequence number, bottom bit 0.
    UCHAR Nr;                   // rcv sequence number, bottom bit p/f.
} LLC_I_HEADER, *PLLC_I_HEADER;

typedef struct {
    LLC_U_HEADER U;             // normal U- frame
    UCHAR Type;                 // its lan header conversion type
} LLC_U_PACKET_HEADER, *PLLC_U_PACKET_HEADER;

typedef union {
    LLC_S_HEADER S;
    LLC_I_HEADER I;
    LLC_U_HEADER U;
    ULONG ulRawLLc;
    UCHAR auchRawBytes[4];
    USHORT EthernetType;
} LLC_HEADER, *PLLC_HEADER;

typedef struct _LLC_PACKET {

    struct _LLC_PACKET* pNext;
    struct _LLC_PACKET* pPrev;
    UCHAR CompletionType;
    UCHAR cbLlcHeader;
    USHORT InformationLength;
    PBINDING_CONTEXT pBinding;

    union {

        struct {
            PUCHAR pLanHeader;
            LLC_HEADER LlcHeader;
            PLLC_OBJECT pLlcObject;
            PMDL pMdl;
        } Xmit;

        struct {
            PUCHAR pLanHeader;
            UCHAR TranslationType;
            UCHAR Dsap;
            UCHAR Ssap;
            UCHAR Command;
            PLLC_OBJECT pLlcObject;
            PMDL pMdl;
        } XmitU;

        struct {
            PUCHAR pLanHeader;
            UCHAR TranslationType;
            UCHAR EthernetTypeHighByte;
            UCHAR EthernetTypeLowByte;
            UCHAR Padding;
            PLLC_OBJECT pLlcObject;
            PMDL pMdl;
        } XmitDix;

        struct {
            PUCHAR pLanHeader;
            UCHAR TranslationType;
            UCHAR Dsap;
            UCHAR Ssap;
            UCHAR Command;
            LLC_RESPONSE_INFO Info;
        } Response;

        //
        // Link station data packet may be acknowledged by the other
        // side, before it is completed by NDIS.  Ndis completion
        // routine expects to find pLlcObject link => we must not change
        // that field, when the xmit packet is translated to
        // a completion packet.  Otherwise is corrupt pFileContext pointer,
        // when NdisSendCount is incremented.
        //

        struct {
            ULONG Status;
            ULONG CompletedCommand;
            PLLC_OBJECT pLlcObject;
            PVOID hClientHandle;
        } Completion;

    } Data;

} LLC_PACKET, *PLLC_PACKET;

//
// Data link indication handler prototypes.
// The protocols registering to data link driver
// must provide these entry points.
//

typedef
UINT
(*PFLLC_RECEIVE_INDICATION)(
    IN PVOID hClientContext,
    IN PVOID hClientHandle,
    IN USHORT FrameType,
    IN PVOID pLookBuf,
    IN UINT cbLookBuf
    );

typedef
VOID
(*PFLLC_COMMAND_COMPLETE)(
    IN PVOID hClientContext,
    IN PVOID hClientHandle,
    IN PVOID hPacket
    );

typedef
VOID
(*PFLLC_EVENT_INDICATION)(
    IN PVOID hClientContext,
    IN PVOID hClientHandle,
    IN UINT uiEvent,
    IN PVOID pDlcStatus,
    IN ULONG SecondaryInformation
    );

//
// DLC API return codes
//
// The base value of the error codes is not compatible with the other
// nt error codes, but it doesn't matter because these are internal
// for DLC driver (and its data link layer).
// 16 bit- error codes are used, because in MIPS they need less
// instructions (MIPS cannot load directly over 16 bits constants)
// and this code can also be emuulated on OS/2.
//

typedef enum _DLC_STATUS {
    DLC_STATUS_SUCCESS                          = 0,
    DLC_STATUS_ERROR_BASE                       = 0x6000,
    DLC_STATUS_INVALID_COMMAND                  = 0x01 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_DUPLICATE_COMMAND                = 0x02 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_ADAPTER_OPEN                     = 0x03 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_ADAPTER_CLOSED                   = 0x04 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_PARAMETER_MISSING                = 0x05 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_OPTION                   = 0x06 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_COMMAND_CANCELLED_FAILURE        = 0x07 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_CANCELLED_BY_USER                = 0x0A + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_SUCCESS_NOT_OPEN                 = 0x0C + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_TIMER_ERROR                      = 0x11 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_NO_MEMORY                        = 0x12 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_LOST_LOG_DATA                    = 0x15 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_BUFFER_SIZE_EXCEEDED             = 0x16 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_BUFFER_LENGTH            = 0x18 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INADEQUATE_BUFFERS               = 0x19 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_USER_LENGTH_TOO_LARGE            = 0x1A + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_CCB_POINTER              = 0x1B + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_POINTER                  = 0x1C + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_ADAPTER                  = 0x1D + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_FUNCTIONAL_ADDRESS       = 0x1E + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_LOST_DATA_NO_BUFFERS             = 0x20 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_TRANSMIT_ERROR_FS                = 0x22 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_TRANSMIT_ERROR                   = 0x23 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_UNAUTHORIZED_MAC                 = 0x24 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_LINK_NOT_TRANSMITTING            = 0x27 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_FRAME_LENGTH             = 0x28 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_NODE_ADDRESS             = 0x32 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_RECEIVE_BUFFER_LENGTH    = 0x33 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_TRANSMIT_BUFFER_LENGTH   = 0x34 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_STATION_ID               = 0x40 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_LINK_PROTOCOL_ERROR              = 0x41 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_PARMETERS_EXCEEDED_MAX           = 0x42 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_SAP_VALUE                = 0x43 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_ROUTING_INFO             = 0x44 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_LINK_STATIONS_OPEN               = 0x47 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INCOMPATIBLE_COMMAND_IN_PROGRESS = 0x4A + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_CONNECT_FAILED                   = 0x4D + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_REMOTE_ADDRESS           = 0x4F + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_CCB_POINTER_FIELD                = 0x50 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INADEQUATE_LINKS                 = 0x57 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_PARAMETER_1              = 0x58 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_DIRECT_STATIONS_NOT_AVAILABLE    = 0x5C + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_DEVICE_DRIVER_NOT_INSTALLED      = 0x5d + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_ADAPTER_NOT_INSTALLED            = 0x5e + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_CHAINED_DIFFERENT_ADAPTERS       = 0x5f + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INIT_COMMAND_STARTED             = 0x60 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_CANCELLED_BY_SYSTEM_ACTION       = 0x62 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_MEMORY_LOCK_FAILED               = 0x69 + DLC_STATUS_ERROR_BASE,

    //
    // New Nt DLC specific error codes begin from 0x80
    // These error codes are for new Windows/Nt DLC apps.
    // This far we have tried too much use the OS/2 error codes,
    // that results often uninformative return codes.
    //

    DLC_STATUS_INVALID_BUFFER_ADDRESS           = 0x80 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_BUFFER_ALREADY_RELEASED          = 0x81 + DLC_STATUS_ERROR_BASE,


    DLC_STATUS_INVALID_VERSION                  = 0xA1 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INVALID_BUFFER_HANDLE            = 0xA2 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_NT_ERROR_STATUS                  = 0xA3 + DLC_STATUS_ERROR_BASE,

    //
    // These error codes are just internal for LLC- kernel level interface
    // and they are not returned to application level.
    //

    DLC_STATUS_UNKNOWN_MEDIUM                   = 0xC0 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_DISCARD_INFO_FIELD               = 0xC1 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_NO_ACTION                        = 0xC2 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_ACCESS_DENIED                    = 0xC3 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_IGNORE_FRAME                     = 0xC4 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_WAIT_TIMEOUT                     = 0xC5 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_NO_RECEIVE_COMMAND               = 0xC6 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_FILE_CONTEXT_DELETED             = 0xC7 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_EXPAND_BUFFER_POOL               = 0xC8 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_INTERNAL_ERROR                   = 0xC9 + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_ASYNC_DATA_TRANSFER_FAILED       = 0xCA + DLC_STATUS_ERROR_BASE,
    DLC_STATUS_OUT_OF_RCV_BUFFERS               = 0xCB + DLC_STATUS_ERROR_BASE,

    DLC_STATUS_PENDING                          = 0xFF + DLC_STATUS_ERROR_BASE,

    DLC_STATUS_MAX_ERROR                        = 0xFF + DLC_STATUS_ERROR_BASE
} DLC_STATUS;

UINT
LlcBuildAddressFromLanHeader(
    IN NDIS_MEDIUM NdisMedium,
    IN PUCHAR pRcvFrameHeader,
    IN OUT PUCHAR pLanHeader
    );

DLC_STATUS
LlcInitialize(
    VOID
    );

UINT
LlcBuildAddress(
    IN NDIS_MEDIUM NdisMedium,
    IN PUCHAR DestinationAddress,
    IN PVOID pSrcRouting,
    IN OUT PUCHAR pLanHeader
    );

USHORT
LlcGetMaxInfoField(
    IN NDIS_MEDIUM NdisMedium,
    IN PVOID hBinding,
    IN PUCHAR pLanHeader
    );

DLC_STATUS
LlcQueryInformation(
    IN PVOID hObject,
    IN UINT InformationType,
    IN PLLC_QUERY_INFO_BUFFER pQuery,
    IN UINT QueryBufferSize
    );

DLC_STATUS
LlcSetInformation(
    IN PVOID hObject,
    IN UINT InformationType,
    IN PLLC_SET_INFO_BUFFER pSetInfo,
    IN UINT ParameterBufferSize
    );

DLC_STATUS
LlcNdisRequest(
    IN PVOID hBindingContext,
    IN PLLC_NDIS_REQUEST pDlcParms
    );

DLC_STATUS
LlcOpenAdapter(
    IN PWSTR pAdapterName,
    IN PVOID hClientContext,
    IN PFLLC_COMMAND_COMPLETE pfCommandComplete,
    IN PFLLC_RECEIVE_INDICATION pfReceiveIndication,
    IN PFLLC_EVENT_INDICATION pfEventIndication,
    IN NDIS_MEDIUM NdisMedium,
    IN LLC_ETHERNET_TYPE EthernetType,
    IN UCHAR AdapterNumber,
    OUT PVOID *phBindingContext,
    OUT PUINT puiOpenStatus,
    OUT PUSHORT puiMaxFrameLength,
    OUT PNDIS_MEDIUM pActualNdisMedium
    );


#define LlcOpenSap(Context, Handle, Sap, Options, phSap) \
    LlcOpenStation(Context, Handle, (USHORT)(Sap), LLC_SAP_OBJECT, (USHORT)(Options), phSap)

#define LlcOpenDirectStation(Context, Handle, Sap, phSap) \
    LlcOpenStation(Context, Handle, (USHORT)(Sap), LLC_DIRECT_OBJECT, 0, phSap)

#define LlcOpenDixStation(Context, Handle, Sap, phSap) \
    LlcOpenStation(Context, Handle, (USHORT)(Sap), LLC_DIX_OBJECT, 0, phSap)


VOID
RemoveFromLinkList(
    OUT PVOID* ppBase,
    IN PVOID pElement
    );

VOID
LlcSleep(
    IN LONG lMicroSeconds
    );

VOID
LlcTerminate(
    VOID
    );

VOID
LlcDereferenceObject(
    IN PVOID pStation
    );

VOID
LlcReferenceObject(
    IN PVOID pStation
    );

DLC_STATUS
LlcTraceInitialize(
    IN PVOID UserTraceBuffer,
    IN ULONG UserTraceBufferSize,
    IN ULONG TraceFlags
    );

VOID
LlcTraceClose(
    VOID
    );

VOID
LlcTraceWrite(
    IN UINT Event,
    IN UCHAR AdapterNumber,
    IN UINT DataBufferSize,
    IN PVOID DataBuffer
    );

VOID
LlcTraceDump(
    IN UINT    LastEvents,
    IN UINT    AdapterNumber,
    IN PUCHAR  pRemoteNode
    );

VOID
LlcTraceDumpAndReset(
    IN UINT    LastEvents,
    IN UINT    AdapterNumber,
    IN PUCHAR  pRemoteNode
    );

#if DBG

typedef struct {
    USHORT Input;
    USHORT Time;
    PVOID pLink;
} LLC_SM_TRACE;

#define LLC_INPUT_TABLE_SIZE    500

extern ULONG AllocatedNonPagedPool;
extern ULONG LockedPageCount;
extern ULONG AllocatedMdlCount;
extern ULONG AllocatedPackets;
extern NDIS_SPIN_LOCK MemCheckLock;
extern ULONG cExAllocatePoolFailed;
extern ULONG FailedMemoryLockings;

VOID PrintMemStatus(VOID);

extern ULONG cFramesReceived;
extern ULONG cFramesIndicated;
extern ULONG cFramesReleased;

extern ULONG cLockedXmitBuffers;
extern ULONG cUnlockedXmitBuffers;

extern LLC_SM_TRACE aLast[];
extern UINT InputIndex;

#endif

//
// The inline memcpy and memset functions are faster,
// in x386 than RtlMoveMemory
//

#if defined(i386)

#define LlcMemCpy(Dest, Src, Len)   memcpy(Dest, Src, Len)
#define LlcZeroMem(Ptr, Len)        memset(Ptr, 0, Len)

#else

#define LlcMemCpy(Dest, Src, Len)   RtlMoveMemory(Dest, Src, Len)
#define LlcZeroMem(Ptr, Len)        RtlZeroMemory(Ptr, Len)

#endif

//
//
//  PVOID
//  PopEntryList(
//      IN PQUEUE_PACKET ListHead,
//      );
//

#define PopFromList(ListHead)               \
    (PVOID)(ListHead);                      \
    (ListHead) = (PVOID)(ListHead)->pNext;


//
//  VOID
//  PushToList(
//      IN PQUEUE_PACKET ListHead,
//      IN PQUEUE_PACKET Entry
//      );
//

#define PushToList(ListHead,Entry) {    \
    (Entry)->pNext = (PVOID)(ListHead); \
    (ListHead) = (Entry);               \
    }

//
//  About 30% of all bugs are related with the invalid operations with
//  packets.  A packet may be inserted to another list before it
//  has been removed from the previous one, etc.
//  The debug version of the list macroes reset the next pointer
//  every time it is removed from the list and check it when it is
//  inserted to a new list or released to a packet pool.  The packet
//  alloc will reset the next pointer automatically.
//  Problem: the packets are used for many other purposes as well =>
//  we must do quite a lot conditional code.
//

#if LLC_DBG

#if LLC_DBG_MP

#define DBG_INTERLOCKED_INCREMENT(Count)    \
    InterlockedIncrement(                   \
        (PLONG)&(Count)                     \
        )

#define DBG_INTERLOCKED_DECREMENT(Count)    \
    InterlockedDecrement(                   \
        (PLONG)&(Count)                     \
        )

#define DBG_INTERLOCKED_ADD(Added, Value)   \
    ExInterlockedAddUlong(                  \
        (PULONG)&(Added),                   \
        (ULONG)(Value),                     \
        &MemCheckLock.SpinLock              \
        )
#else

#define DBG_INTERLOCKED_INCREMENT(Count)    (Count)++
#define DBG_INTERLOCKED_DECREMENT(Count)    (Count)--
#define DBG_INTERLOCKED_ADD(Added, Value)   (Added) += (Value)

#endif // LLC_DBG_MP

#else

#define DBG_INTERLOCKED_INCREMENT(Count)
#define DBG_INTERLOCKED_DECREMENT(Count)
#define DBG_INTERLOCKED_ADD(Added, Value)

#endif // LLC_DBG


#if LLC_DBG

VOID LlcBreakListCorrupt( VOID );

#define LlcRemoveHeadList(ListHead)             \
    (PVOID)(ListHead)->Flink;                   \
    {                                           \
        PLIST_ENTRY FirstEntry;                 \
        FirstEntry = (ListHead)->Flink;         \
        FirstEntry->Flink->Blink = (ListHead);  \
        (ListHead)->Flink = FirstEntry->Flink;  \
        FirstEntry->Flink = NULL;               \
    }

#define LlcRemoveTailList(ListHead)             \
    (ListHead)->Blink;                          \
    {                                           \
        PLIST_ENTRY FirstEntry;                 \
        FirstEntry = (ListHead)->Blink;         \
        FirstEntry->Blink->Flink = (ListHead);  \
        (ListHead)->Blink = FirstEntry->Blink;  \
        FirstEntry->Flink = NULL;               \
    }

#define LlcRemoveEntryList(Entry)               \
    {                                           \
        RemoveEntryList((PLIST_ENTRY)Entry);    \
        ((PLIST_ENTRY)(Entry))->Flink = NULL;   \
    }

#define LlcInsertTailList(ListHead,Entry)       \
    if (((PLIST_ENTRY)(Entry))->Flink != NULL){ \
        LlcBreakListCorrupt();                  \
    }                                           \
    InsertTailList(ListHead, (PLIST_ENTRY)Entry)

#define LlcInsertHeadList(ListHead,Entry)                   \
            if (((PLIST_ENTRY)(Entry))->Flink != NULL) {    \
                LlcBreakListCorrupt();                      \
            }                                               \
            InsertHeadList(ListHead,(PLIST_ENTRY)Entry)

/*
#define DeallocatePacket( PoolHandle, pBlock ) {                        \
            if (((PLIST_ENTRY)pBlock)->Flink != NULL) {                 \
                LlcBreakListCorrupt();                                  \
            }                                                           \
            DBG_INTERLOCKED_DECREMENT( AllocatedPackets );              \
            ExFreeToZone(                                               \
                &(((PEXTENDED_ZONE_HEADER)PoolHandle)->Zone),           \
                pBlock);                                                \
        }
*/

#else


//
//  PVOID
//  LlcRemoveHeadList(
//      IN PLIST_ENTRY ListHead
//      );
//

#define LlcRemoveHeadList(ListHead) (PVOID)RemoveHeadList(ListHead)


//
//  PLIST_ENTRY
//  LlcRemoveTailList(
//      IN PLIST_ENTRY ListHead
//      );
//

#define LlcRemoveTailList(ListHead)             \
    (ListHead)->Blink; {                        \
        PLIST_ENTRY FirstEntry;                 \
        FirstEntry = (ListHead)->Blink;         \
        FirstEntry->Blink->Flink = (ListHead);  \
        (ListHead)->Blink = FirstEntry->Blink;  \
    }


//
//  VOID
//  LlcRemoveEntryList(
//      IN PVOID Entry
//      );
//

#define LlcRemoveEntryList(Entry) RemoveEntryList((PLIST_ENTRY)Entry)


//
//  VOID
//  LlcInsertTailList(
//      IN PLIST_ENTRY ListHead,
//      IN PVOID Entry
//      );
//

#define LlcInsertTailList(ListHead,Entry) \
            InsertTailList(ListHead, (PLIST_ENTRY)Entry)

//
//  VOID
//  LlcInsertHeadList(
//      IN PLIST_ENTRY ListHead,
//      IN PVOID Entry
//      );
//

#define LlcInsertHeadList(ListHead,Entry) \
            InsertHeadList(ListHead,(PLIST_ENTRY)Entry)

//
//  VOID
//  DeallocatePacket(
//      PEXTENDED_ZONE_HEADER PoolHandle,
//      PVOID pBlock
//      );
//

/*
#define DeallocatePacket( PoolHandle, pBlock ) {                        \
            ExFreeToZone( &(((PEXTENDED_ZONE_HEADER)PoolHandle)->Zone), \
                pBlock);                                                \
        }
*/

#endif // DBG

#if LLC_DBG == 2

VOID LlcMemCheck(VOID);

#define MEM_CHECK() LlcMemCheck()

#else

#define MEM_CHECK()

#endif


VOID LlcInvalidObjectType(VOID);

/*++

    Trace codes

    Big letters are reserved for actions, small letters are used to identify
    objects and packet types.  The trace macroes simply writes to DLC
    trace tree (remember: we have another trace for the state machine!!!)

    'a' = dix stations
    'b' = direct station
    'c' = link station
    'd' = sap station

    'e' = Connect command
    'f' = Close command
    'g' = Disconnect command
    'h' = Receive command
    'i' = transmit command

    'j' = Type1 packet
    'k' = type 2 packet
    'l' = data link packet

    'A' = CompleteSendAndLock
    'B' = LlcCommandCompletion
    'C' = LlcCloseStation
    'D' = LlcReceiveIndication
    'E' = LlcEventIndication
    'F' = DlcDeviceIoControl
    'G' = DlcDeviceIoControl sync exit
    'H' = DlcDeviceIoControl async exit
    'I' = LlcSendI
    'J' = DirCloseAdapter
    'K' = CompleteDirCloseAdapter
    'L' = LlcDereferenceObject
    'M' = LlcReferenceObject
    'N' = CompleteCloseStation (final)
    'O' = DereferenceLlcObject (in dlc)
    'P' = CompleteLlcObjectClose (final)
    'Q' = DlcReadCancel
    'R' =
    'S' =
    'T' = DlcTransmit
    'U' = LlcSendU
    'V' =
    'W' =
    'X' =
    'Y' =
    'Z' =

--*/

#if DBG & DLC_TRACE_ENABLED

#define LLC_TRACE_TABLE_SIZE    0x400   // this must be exponent of 2!

extern UINT LlcTraceIndex;
extern UCHAR LlcTraceTable[];

#define DLC_TRACE(a)  {\
    LlcTraceTable[LlcTraceIndex] = (a);\
    LlcTraceIndex = (LlcTraceIndex + 1) & (LLC_TRACE_TABLE_SIZE - 1);\
}

#else

#define DLC_TRACE(a)

#endif  // LLC_DBG

//
// the following functions can be macros if DLC and LLC live in the same driver
// and each knows about the other's structures
//

#if DLC_AND_LLC

//
//  UINT
//  LlcGetReceivedLanHeaderLength(
//      IN PVOID pBinding
//      );
//

#if 0

//
// this gives the wrong length for DIX lan header
//

#define LlcGetReceivedLanHeaderLength(pBinding) \
    ((((PBINDING_CONTEXT)(pBinding))->pAdapterContext->NdisMedium == NdisMedium802_3) \
        ? (((PBINDING_CONTEXT)(pBinding))->pAdapterContext->FrameType == LLC_DIRECT_ETHERNET_TYPE) \
            ? 12 \
            : 14 \
        : (((PBINDING_CONTEXT)(pBinding))->pAdapterContext->NdisMedium == NdisMediumFddi) \
            ? 14 \
            : ((PBINDING_CONTEXT)(pBinding))->pAdapterContext->RcvLanHeaderLength)

#else

//
// this always returns 14 as the DIX lan header length
//

#define LlcGetReceivedLanHeaderLength(pBinding) \
    ((((PBINDING_CONTEXT)(pBinding))->pAdapterContext->NdisMedium == NdisMedium802_3) \
        ? 14 \
        : (((PBINDING_CONTEXT)(pBinding))->pAdapterContext->NdisMedium == NdisMediumFddi) \
            ? 14 \
            : ((PBINDING_CONTEXT)(pBinding))->pAdapterContext->RcvLanHeaderLength)

#endif

//
//  USHORT
//  LlcGetEthernetType(
//      IN PVOID hContext
//      );
//

#define LlcGetEthernetType(hContext) \
    (((PBINDING_CONTEXT)(hContext))->pAdapterContext->EthernetType)

//
//  UINT
//  LlcGetCommittedSpace(
//      IN PVOID hLink
//      );
//

#define LlcGetCommittedSpace(hLink) \
    (((PDATA_LINK)(hLink))->BufferCommitment)

#else

//
// separation of church and state, or DLC and LLC even
//

UINT
LlcGetReceivedLanHeaderLength(
    IN PVOID pBinding
    );

USHORT
LlcGetEthernetType(
    IN PVOID hContext
    );

UINT
LlcGetCommittedSpace(
    IN PVOID hLink
    );

#endif // DLC_AND_LLC

#endif // _LLC_API_