/*++

Copyright (c) 1991  Microsoft Corporation
Copyright (c) 1991  Nokia Data Systems

Module Name:

    vrdlc.h

Abstract:

    This module is the only header file of Windows/Nt VDM DLC
    interface module.

    ALL STRUCTURES IN THIS FILE WHICH REFERENCE STRUCTURES IN DOS MEMORY
    ARE BYTE PACKED

Author:

    Antti Saarenheimo (o-anttis) 26-01-1992

Revision History:

--*/

//
// constants
//

#define DOS_DLC_MAX_SAPS                128
#define DOS_DLC_MAX_LINKS               255
#define DOS_DLC_MAX_EVENTS              64

#define LLC_DIR_MODIFY_OPEN_PARMS       0x01
#define LLC_DIR_RESTORE_OPEN_PARMS      0x02
#define LLC_DIR_SET_USER_APPENDAGE      0x2d
#define LLC_DOS_SPECIAL_COMMAND         ((ULONG)(-1))
#define LLC_BREAK                       0x20

#define DOS_DLC_STATUS_NO_INDICATION    0x81

#define LLC_SET_LOCAL_BUSY_BUFFER       0x20

//
// VRDLC_COMMAND_COMPLETION - this value is placed in the CCB_CMD_CMPL field
// of every CCB2 that we issue that is NOT for the VDM. This value is used to
// filter out command completions for commands that are generated by the DOS
// DLC Emulator. This stops us passing command completions through to the
// VDM that are not intended for it!
//

#define VRDLC_COMMAND_COMPLETION        ((ULONG)(-2))

//
// buffer pool sizes
//

#define DOS_DLC_BUFFER_POOL_SIZE   0x00010000   // 64K
#define DOS_DLC_MIN_FREE_THRESHOLD 0x00002000   //  8K

//
// flags for CopyFrame
//

#define CF_CONTIGUOUS               0x00000001  // frame is contiguous
#define CF_BREAK                    0x00000002  // options specified Break
#define CF_PARTIAL                  0x00000004  // receiving partial frame

//
// default values for DOS parameter tables (DD_ = DOS DEFAULT). These replace
// the various parameters which can be specified as 0. They may be different
// to the corresponding defaults applicable to NT DLC, so we fill them in
// specifically
//

//
// defaults for BUFFER.GET:
//

#define DD_BUFFER_GET           1

//
// defaults for DIR.INITIALIZE:
//

#define DD_SRAM_ADDRESS_0       0xd800
#define DD_SRAM_ADDRESS_1       0xd400

//
// defaults for DIR.OPEN.ADAPTER, ADAPTER_PARMS:
//

#define DD_NUMBER_RCV_BUFFERS   8
#define DD_RCV_BUFFER_LENGTH    112
#define DD_DHB_BUFFER_LENGTH    600
#define DD_DATA_HOLD_BUFFERS    1

//
// defaults for DIR.OPEN.ADAPTER, DIRECT_PARMS:
//

#define DD_DIR_BUF_SIZE         160
#define DD_DIR_POOL_BLOCKS      256

//
// defaults for DIR.OPEN.ADAPTER, DLC_PARMS:
//

#define DD_DLC_MAX_SAP          2
#define DD_DLC_MAX_STATIONS     6
#define DD_DLC_MAX_GSAP         0
#define DD_DLC_T1_TICK_ONE      5
#define DD_DLC_T2_TICK_ONE      1
#define DD_DLC_Ti_TICK_ONE      25
#define DD_DLC_T1_TICK_TWO      25
#define DD_DLC_T2_TICK_TWO      10
#define DD_DLC_Ti_TICK_TWO      125

//
// defaults for DLC.OPEN.SAP:
//

#define DD_MAXOUT               2
#define DD_MAXIN                1
#define DD_MAX_RETRY_COUNT      8
#define DD_MAX_I_FIELD          600
#define DD_DLC_BUF_SIZE         160
#define DD_DLC_POOL_LEN         256

//
// macros
//

//
// DOS_PTR_TO_FLAT - given a DOS 16:16 pointer stored implicitly as a DWORD
//

#define DOS_PTR_TO_FLAT(a)  (PVOID)GetVDMAddr(HIWORD(a), LOWORD(a))

//
// NEW_DOS_ADDRESS - generate a new DOS_ADDRESS, given a base DOS_ADDRESS and
// a new pointer which is some number of bytes plus the base DOS_ADDRESS
// converted to a flat pointer. For example, a DOS_ADDRESS of 1234:0000 becomes
// (on x86) a flat pointer of 0x12340. We generate a new pointer 0x12380 and
// want to convert this address back to a DOS_ADDRESS. So we use this macro.
// Offset-wrap and segment update is automatically handled
//

#define NEW_DOS_ADDRESS(b, p)   ((b) + ((DWORD)(p) - (DWORD)DOS_PTR_TO_FLAT(b)))

//
// POOL_INDEX_FROM_SAP - get the index in aBufferPools for a given SAP/adapter
// combination. There are a maximum 127 SAPs per adapter, and 2 adapters which
// are available to DOS
//

#define POOL_INDEX_FROM_SAP(Sap, Adapter)   ((Sap & 0xfe) | Adapter)

//
// POOL_INDEX_FROM_ID - given a station ID (high byte = SAP, low byte = link
// station), get the index to the SAP's buffer pool in aBufferPools
//

#define POOL_INDEX_FROM_ID(Id, Adapter)     POOL_INDEX_FROM_SAP(HIBYTE(Id), Adapter)

//
// GET_POOL_INDEX - the original pool index macro
//

#define GET_POOL_INDEX(Adapter, usStationId)    POOL_INDEX_FROM_ID(usStationId, Adapter)

//
// macros which initialize CCBs and call AcsLan
//

#define DlcFlowControl(Adapter, StationId, Options)\
            LlcCommand(Adapter, LLC_DLC_FLOW_CONTROL, ((DWORD)Options << 16) + StationId)

#define DosDlcFlowControl(Adapter, StationId, Options)\
            LlcCommand(Adapter, LLC_DOS_DLC_FLOW_CONTROL, ((DWORD)Options << 16) + StationId)

#define InitializeCcb(pCcb, AdapterNumber, Command, pParameter) \
            RtlZeroMemory((pCcb), sizeof(*(pCcb)));\
            RtlZeroMemory((pParameter), sizeof(*(pParameter)));\
            (pCcb)->uchAdapterNumber = (UCHAR)AdapterNumber;\
            (pCcb)->uchDlcCommand = (UCHAR)Command;\
            (pCcb)->u.pParameterTable = (PLLC_PARMS)(pParameter)

#define InitializeCcb2(pCcb, AdapterNumber, Command) \
            RtlZeroMemory((pCcb), sizeof(*(pCcb)));\
            (pCcb)->uchAdapterNumber = (UCHAR)AdapterNumber;\
            (pCcb)->uchDlcCommand = (UCHAR)Command;

#define ReceiveCancel(AdapterNumber, pCcb) \
            LlcCommand(AdapterNumber, LLC_RECEIVE_CANCEL, (DWORD)pCcb)

//
// DLC_ERROR_STATUS - after calling AcsLan, if an error was returned by AcsLan
// then return that, else get the return code out of the CCB and return that
//

#define DLC_ERROR_STATUS(AcslanStatus, uchDlcStatus) \
            (DWORD)((AcslanStatus == 0) ? (DWORD)uchDlcStatus : (DWORD)AcslanStatus)

//
// VRDLC_ALLOC - standard allocation strategy in VDM REDIR DLC functions
//

#define VRDLC_ALLOC(Bytes)  LocalAlloc(LMEM_FIXED, Bytes)

//
// VRDLC_FREE - companion to VRDLC_ALLOC - standard allocation free strategy
//

#define VRDLC_FREE(Pointer) LocalFree((HLOCAL)Pointer)

//
// SAP_ID - get the SAP from a station ID word. Used as array index 0..127
// (corresponding to SAP 0..254 step 2)
//

#define SAP_ID(stationId)   (HIBYTE(stationId) >> 1)

//
// LINK_ID - get the link station ID from a station ID word. Used as array index
// 0..254 (corresponding to link station 1..255)
//

#define LINK_ID(stationId)  (LOBYTE(stationId) - 1)
//
// types
//

union _LLC_DOS_PARMS;
typedef union _LLC_DOS_PARMS LLC_DOS_PARMS, *PLLC_DOS_PARMS;
typedef DWORD DOS_ADDRESS;
typedef DOS_ADDRESS DPLLC_DOS_BUFFER;

//
// LLC_DOS_BUFFER - this is a union of all the DOS DLC data buffers. There are
// basically 3 kinds: Buffer 1, the first buffer in a chain which contains net
// address info, this can be in contigous or non-contiguous form, and Buffer 2
// format which is the 2nd and subsequent buffers in a chain. DLC uses the
// buffers for received data. Transmit data (passed from the app to DLC) can
// use a buffer (or chain of buffers) from the pool or can source its own
// buffer. The latter is preferred since taking buffers which DLC would use
// for receiving data can leave DLC in the local busy state (ie no receive
// buffers)
//

#include <packon.h>

typedef union _LLC_DOS_BUFFER {

    //
    // pNext is just a pointer so we can follow the chain
    //

    union _LLC_DOS_BUFFER * pNext;

    //
    // NextDosBuffer is the Buffer 2 structure defined in the IBM Lan Tech.
    // Ref. pg 2-45
    //

    struct _NextDosBuffer {
        union _LLC_DOS_BUFFER * pNextBuffer;// next frame segment
        WORD        cbFrame;                // length of the whole rcvd frame
        WORD        cbBuffer;               // length of this segment
        WORD        offUserData;            // offset of data from descr header
        WORD        cbUserData;             // length of the data
    } Next;

    //
    // NotContiguous is the Not contiguous MAC/Data Buffer 1 structure defined
    // in IBM Lan Tech. Ref. pg 2-42
    //

    struct _DosDlcNotContiguousFirstBuffer {
        union _LLC_DOS_BUFFER * pNextBuffer;  // next frame segment
        WORD            cbFrame;        // length of entire frame
        WORD            cbBuffer;       // length of this buffer
        WORD            offUserData;    // user data in this struct
        WORD            cbUserData;     // length of user data
        WORD            usStationId;    // ssnn station id
        UCHAR           uchOptions;     // option byte from RECEIVE param tbl
        UCHAR           uchMsgType;     // the message type
        WORD            cBuffersLeft;   // number of basic buffer units left
        UCHAR           uchRcvFS;       // the received frame status
        UCHAR           uchAdapterNumber;  // current adapter number
        UCHAR           cbLanHeader;    // length of the LAN header
        UCHAR           cbDlcHeader;    // length of the DLC header
        UCHAR           auchLanHeader[32];// LAN header of the received frame
        UCHAR           auchDlcHeader[4]; // DLC header of the received frame
    } NotContiguous;

    //
    // Contiguous is the Contiguous MAC/Data Buffer 1 structure defined
    // in IBM Lan Tech. Ref. pg 2-43
    //

    struct _DosDlcContiguousFirstBuffer {
        union _LLC_DOS_BUFFER * pNextBuffer;  // next frame segment
        WORD            cbFrame;        // length of entire frame
        WORD            cbBuffer;       // length of this buffer
        WORD            offUserData;    // user data in this struct
        WORD            cbUserData;     // length of user data
        WORD            usStationId;    // ssnn station id
        UCHAR           uchOptions;     // option byte from RECEIVE param tbl
        UCHAR           uchMsgType;     // the message type
        WORD            cBuffersLeft;   // number of basic buffer units left
        UCHAR           uchRcvFS;       // the received frame status
        UCHAR           uchAdapterNumber;
    } Contiguous;
} LLC_DOS_BUFFER, *PLLC_DOS_BUFFER;

#include <packoff.h>

//
// DOS_DLC_BUFFER_POOL - there is one of these per each SAP per adapter (max.
// 127 SAPs per adapter * max. 2 adapters = 256), kept in an array. This
// structure maintains basic information about the DOS buffer pool - its
// starting address (dpBuffer) in DOS 16:16 format, the size of an individual
// buffer in the pool (BufferSize) and the number of buffers in the pool
// (BufferCount). A buffer must be an integral multiple of 16 bytes, a minimum
// length of 80 bytes and not exceeding 64K-16 (0xfff0 = 65520)
//

typedef struct _DOS_DLC_BUFFER_POOL {
    DOS_ADDRESS dpBuffer;
    WORD BufferSize;
    WORD BufferCount;
    WORD MaximumBufferCount;
} DOS_DLC_BUFFER_POOL, *PDOS_DLC_BUFFER_POOL;

//
// DOS DLC CCB aka CCB1 - see definition in IBM Lan Tech. Ref. pg 2-6
//

#include <packon.h>

typedef struct _LLC_DOS_CCB {
    UCHAR   uchAdapterNumber;       // Adapter 0 or 1
    UCHAR   uchDlcCommand;          // DLC command
    UCHAR   uchDlcStatus;           // DLC command completion code
    UCHAR   uchReserved1;           // reserved for DLC DLL
    struct _LLC_DOS_CCB *pNext;     // queued another CCB
    DWORD   ulCompletionFlag;       // used in command completion
    union {
        PLLC_DOS_PARMS pParms;      // pointer to the parameter table
        struct {
            WORD    usStationId;    // Station id
            WORD    usParameter;    // optional parameter
        } dlc;
        struct {
            WORD    usParameter0;   // first optional parameter
            WORD    usParameter1;   // second optional parameter
        } dir;
        UCHAR   auchBuffer[4];      // group/functional address
        DWORD   ulParameter;
    } u;
} LLC_DOS_CCB, *PLLC_DOS_CCB;

//
// additional parameter tables not defined in (sdk\inc\) DLCAPI.H (or where
// CCB1 parameter tables different from those defined in DLCAPI.H)
//

//
// LLC_DOS_DIR_INITIALIZE_PARMS - CCB1 DIR.INITIALIZE parameter table
//

typedef struct {
    WORD    BringUps;
    WORD    SharedRamAddress;
    WORD    Reserved;
    DWORD   AdapterCheckExit;
    DWORD   NetworkStatusExit;
    DWORD   PcErrorExit;
} LLC_DOS_DIR_INITIALIZE_PARMS, *PLLC_DOS_DIR_INITIALIZE_PARMS;

//
// ADAPTER_PARMS, DIRECT_PARMS, DLC_PARMS and NCB_PARMS - these are the
// parameter tables which are passed in to DIR.OPEN.ADAPTER
//

//
// ADAPTER_PARMS - parameters returned from the adapter support s/w
//

typedef struct _ADAPTER_PARMS {
    WORD    OpenErrorCode;          // error detected opening adapter
    WORD    OpenOptions;            // options for Token Ring only:

    //
    // OpenOptions Bit Meanings
    //
    //  This has been paraphrased from the IBM Lan Tech. Ref. p3-22. I don't
    //  understand most of it, but I think I made it easier to read than the
    //  IBM technicalese. Note: ONLY MEANINGFUL TO TOKEN RING ADAPTER
    //
    // Bit 15: Wrap Interface
    //          The adapter doesn't attach to the network; instead, all
    //          transmitted data is reflected back as received data
    //
    // Bit 14: Disable Hard Error
    //          Stops network status change involving "Hard Error" and
    //          "Transmit Beacon" bits from generating interrupt
    //
    // Bit 13: Disable Soft Errors
    //          Stops network status change involving "Soft Error" bit
    //          generating interrupt
    //
    // Bit 12: Pass Adapter MAC Frames
    //          Unsupported MAC frames are passed to the direct station.
    //          If OFF, these frames are ignored
    //
    // Bit 11: Pass Attention MAC Frames
    //          Passes attention MAC frames which are not the same as the last
    //          received Attention MAC Frame to the direct station. If OFF,
    //          these frames are not passed to the direct station (ie App)
    //
    // Bit 10: Reserved
    //          Should be zero, but not checked by adapter
    //
    // Bit  9: Pass Parameter Table
    //          If the adapter is already open, returns options specified
    //
    // Bit  8: Contender
    //          If ON, this adapter will participate in monitor contention
    //          (claim token), should the need arise. If OFF, and it is
    //          another adapter decides it is necessary to claim the token,
    //          this adapter will not participate
    //
    //          If this adapter decides it is necessary to determine a new
    //          active monitor, this adapter will initiate monitor contention
    //          processing IRRESPECTIVE OF THE VALUE OF THIS BIT
    //
    // Bit  7: Pass Beacon MAC Frames
    //          Pass to direct station first Beacon MAC frame and all subsequent
    //          Beacon MAC frames having a change in source address or beacon type
    //
    // Bit  6: Reserved
    //          Should be zero, but not checked by adapter
    //
    // Bit  5: Remote Program Load
    //          Only implemented on 16/4 adapters. Prevents adapter becoming
    //          a monitor during open process. If ON, will cause this adapter
    //          to fail the open if there are no other active adapters on the
    //          ring when it tries to insert itself
    //
    // Bit  4: Token Release
    //          Only implemented on 16/4 adapters and only available when
    //          operating at 16 Mbps. OFF: use early token release (default).
    //          ON: selects no early token release for adapter a 16 Mbps
    //
    // Bit  3: Reserved  \
    // Bit  2: Reserved   >  Should be 0, but are not checked by adapter
    // Bit  1: Reserved  /
    // Bit  0: Reserved /
    //

    BYTE    NodeAddress[6];         // this adapter's address
    DWORD   GroupAddress;           // group address to set
    DWORD   FunctionalAddress;      // functional address to set
    WORD    NumberReceiveBuffers;   // number of receive buffers
    WORD    ReceiveBufferLength;    // size of receive buffer
    WORD    DataHoldBufferLength;   // size of transmit data hold buffer
    BYTE    NumberDataHoldBuffers;  // returned: only by Token Ring
    BYTE    Reserved;
    WORD    OpenLock;               // Protection code to control closing adapter
                                    // This is NOT RETURNED when OpenOptions.9
                                    // is set (Pass parameter table)
    DWORD   ProductId;              // 18-byte product ID
                                    // This is NOT RETURNED when OpenOptions.9
                                    // is set (Pass parameter table)
    //
    // according to table 3-9 in IBM LAN Tech. Ref. (p3-25) the ProductId field
    // should point at an 18-byte buffer formatted like so:
    //
    //  Byte 0      0x01 indicates workstation
    //  Byte 1      0x10
    //  Byte 2-5    last 4 digits from workstation serial number in EBCDIC
    //  Byte 6-17   0x00
    //
} ADAPTER_PARMS, *PADAPTER_PARMS;

//
// DIRECT_PARMS - input parameters defining Direct Station for adapter
//

typedef struct _DIRECT_PARMS {

    //
    // the direct buffer size is min. 80 bytes, and must be integral multiple
    // of 16-bytes. If 0, default of 160 is used
    //

    WORD    DirectBufferSize;       // size of buffers in direct buffer pool


    //
    // direct pool blocks - number of 16-byte blocks in direct station buffer
    // pool. If 0, default of 256 is used (= 4096 byte buffer pool)
    //

    WORD    DirectPoolBlocks;       // size of buffer in 16-byte blocks


    //
    // direct buffer pool - segment address in workstation memory where direct
    // station buffer pool is created. Spec. doesn't say what happens if there
    // is a non-zero (or any, for that matter) offset. If 0, the application
    // must build the direct station buffer pool, in which case DirectBufferSize
    // must indicate the size of each buffer
    //

    DWORD   DirectBufferPool;       // start address of direct buffer pool


    //
    // adapter check exit - vectors to this address when the adapter detects
    // an internal error. If 0, the value specified in DIR.INITIALIZE is used
    //

    DWORD   AdapterCheckExit;       // I/O appendage exit: adapter check


    //
    // network status exit - vectors to this address when the network status
    // changes (whatever that means). If 0, the value specified by
    // DIR.INITIALIZE is used
    //

    DWORD   NetworkStatusExit;      // I/O appendage exit: network status change


    //
    // PC error exit - vectors to this address when the adapter s/w detects an
    // error in the workstation (!). If 0, the value specified by DIR.INITIALIZE
    // is used
    //

    DWORD   PcErrorExit;            // I/O appendage exit: error in workstation


    //
    // adapter work area - segment of area of w/s memory which is to be used
    // by the adapter. Ignored if  AdapterWorkAreaRequested is 0
    //

    DWORD   AdapterWorkArea;        // TR: adapter work are


    //
    // adapter work area length (requested) - the size of the workspace area,
    // the segment of which is specified in AdapterWorkArea. Size is calculated
    // thus: Number of SAPs x 36 + Number of stations (links) x 6 + 48
    //

    WORD    AdapterWorkAreaRequested; // TR: work area length requested


    //
    // adapter work area length (actual) - this value is returned by the
    // adapter. It is the amount of the work area used by the adapter (in bytes).
    // If this is greater than AdapterWorkAreaRequested then an error is returned
    // (0x12)

    WORD    AdapterWorkAreaActual;  // TR: actual work area length taken

} DIRECT_PARMS, *PDIRECT_PARMS;

//
// DLC_PARMS - returned values defining DLC limits
//

typedef struct _DLC_PARMS {

    //
    // maximum number of concurrently opened SAPs: limited by available
    // adapter memory and/or workspace memory. Maximum is 127 (126 if NetBIOS
    // is specified). If 0, the default 2 is used
    //

    BYTE    MaxSaps;                // maximum number of SAPs


    //
    // maximum number of concurrently opened link stations: limited by
    // available adapter and/or work area memory in workstation. Maximum is 255
    // for Token Ring, Ethernet or PC Network. If 0, the default of 6 is used
    //

    BYTE    MaxStations;            // maximum number of link stations


    //
    // maximum number of group SAPs concurrently opened. If 0, no group SAPs
    // can be opened. Maximum value is 126 for Token Ring, 125 for PC Network
    // and Ethernet
    //

    BYTE    MaxGroupSaps;           // maximum number of group SAPs


    //
    // maximum number of SAPs assigned to a group. Maximum is 127 for Token
    // Ring, 126 for PC Network and Ethernet
    //

    BYTE    MaxGroupMembers;        // maximum members per group SAP


    //
    // Timers. There are 3 timers: T1 is the Response Timer; T2 is the Inactivity
    // Timer; and Ti is the Receiver Acknowledgement Timer.
    //
    // Timers are set to a multiple of 40ms. They count down and interrupt the
    // adapter when they reach 0. Timer values can be between 1 and 10. If it
    // is between 1 and 5, the short timer tick (TICK_ONE) is used and is
    // referred to as group 1. If the number is between 6 and 10, the long timer
    // tick (TICK_TWO) is used and is referred to as group 2. The timer value is
    // the number (6 to 10) minus 5 multiplied by the long tick value.
    //

    //
    // Tick1 - number of 40 ms ticks for short DLC timer. Defaults (if 0):
    //  T1 5 (200ms-400ms)
    //  T2 1 (40ms-80ms)
    //  Ti 25 (1s-2s)
    //

    BYTE    T1Tick1;                // Timer 1 short timer
    BYTE    T2Tick1;                // Timer 2 short timer
    BYTE    TiTick1;                // Timer i short timer


    //
    // Tick2 - number of 40 ms ticks for long DLC timer. Default (if 0):
    //  T1 25 (1s-2s)
    //  T2 10 (400ms-800ms)
    //  Ti 125 (5s-10s)
    //

    BYTE    T1Tick2;                // Timer 1 long timer
    BYTE    T2Tick2;                // Timer 2 long timer
    BYTE    TiTick2;                // Timer i long timer
} DLC_PARMS, *PDLC_PARMS;

//
// NCB_PARMS - we are not interested in running DOS NETBIOS over DOS DLC (are we?)
//

typedef struct _NCB_PARMS {
    BYTE    Reserved1[4];       // adapter work area
    BYTE    TimerT1;
    BYTE    TimerT2;
    BYTE    TimerTi;
    BYTE    MaxOut;
    BYTE    MaxIn;
    BYTE    MaxOutIncr;
    BYTE    MaxRetry;
    BYTE    Reserved2[4];
    BYTE    NcbAccessPri;
    BYTE    MaxStations;
    BYTE    Reserved3[19];
    BYTE    MaxNames;
    BYTE    MaxNcbs;
    BYTE    MaxSessions;
    BYTE    Reserved4[2];
    BYTE    Options;
    WORD    PoolLength;
    DWORD   PoolAddress;
    BYTE    TxTimeout;
    BYTE    TxCount;
} NCB_PARMS, *PNCB_PARMS;

//
// LLC_DOS_DIR_OPEN_ADAPTER_PARMS is the CCB1 DIR.OPEN.ADAPTER parameter table
//

typedef struct _LLC_DOS_DIR_OPEN_ADAPTER_PARMS {
    PADAPTER_PARMS  pAdapterParms;
    PDIRECT_PARMS   pDirectParms;
    PDLC_PARMS      pDlcParms;
    PNCB_PARMS      pNcbParms;
} LLC_DOS_DIR_OPEN_ADAPTER_PARMS, *PLLC_DOS_DIR_OPEN_ADAPTER_PARMS;

//
// LLC_DOS_RECEIVE_PARMS is the CCB1 RECEIVE parameter table
//

typedef struct _LLC_DOS_RECEIVE_PARMS {
    WORD        usStationId;    // SAP, link station or direct id
    WORD        usUserLength;   // length of user data in buffer header
    DWORD       ulReceiveExit;  // the received data handler
    PLLC_BUFFER pFirstBuffer;   // first buffer in the pool
    UCHAR       uchOptions;     // defines how the frame is received
} LLC_DOS_RECEIVE_PARMS,  *PLLC_DOS_RECEIVE_PARMS;

//
// LLC_DOS_RECEIVE_PARMS_EX is an extended version of the LLC_DOS_RECEIVE_PARMS
// parameter table. We keep extra information - the DOS address of the original
// CCB and the original RECEIVE_DATA completion exit routine
//

typedef struct _LLC_DOS_RECEIVE_PARMS_EX {
    WORD        usStationId;            // SAP, link station or direct id
    WORD        usUserLength;           // length of user data in buffer header
    DWORD       ulReceiveExit;          // the received data handler
    PLLC_BUFFER pFirstBuffer;           // first buffer in the pool
    UCHAR       uchOptions;             // defines how the frame is received
    UCHAR       auchReserved1[3];       //
    UCHAR       uchRcvReadOption;       // defines if rcv frames are chained
    UCHAR       auchReserved2[3];       // align dpOriginalCcbAddress on DWORD
    DOS_ADDRESS dpOriginalCcbAddress;   // dos address of orginal ccb
    DOS_ADDRESS dpCompletionFlag;       // orginal completion flag
} LLC_DOS_RECEIVE_PARMS_EX,  *PLLC_DOS_RECEIVE_PARMS_EX;

//
// LLC_DOS_RECEIVE_MODIFY_PARMS is the parameter table for RECEIVE.MODIFY which
// we don't seem to support in NT native DLC
//

typedef struct {
    WORD    StationId;                  // SAP & link station Id
    WORD    UserLength;                 // length of user area in buffer
    DWORD   ReceivedDataExit;           // address of routine to call with data
    DWORD   FirstBuffer;                // pointer to first buffer from pool
    DWORD   Subroutine;                 // address of routine to call to get app buffer
} LLC_DOS_RECEIVE_MODIFY_PARMS, *PLLC_DOS_RECEIVE_MODIFY_PARMS;

//
// LLC_DOS_TRANSMIT_PARMS this structure is identical to LLC_TRANSMIT_PARMS
// except that there is no XMIT_READ_OPTION byte on the end, and the types of
// the fields are different, although the sizes are the same: eg. DOS_ADDRESS
// instead of PVOID or PLLC_XMIT_BUFFER
//

typedef struct _LLC_DOS_TRANSMIT_PARMS {
    WORD        usStationId;            // SAP and link station ID
    BYTE        uchTransmitFs;          // returned: Frame Status
    BYTE        uchRemoteSap;           // remote SAP we're talking to
    DOS_ADDRESS pXmitQueue1;            // address of 1st buffer queue. Not pooled
    DOS_ADDRESS pXmitQueue2;            // address of 2nd buffer queue. Pooled
    WORD        cbBuffer1;              // length of data in pBuffer1
    WORD        cbBuffer2;              // length of data in pBuffer2
    DOS_ADDRESS pBuffer1;               // address of 1st data buffer
    DOS_ADDRESS pBuffer2;               // address of 2nd data buffer
} LLC_DOS_TRANSMIT_PARMS, *PLLC_DOS_TRANSMIT_PARMS;

typedef struct _LLC_MODIFY_OPEN_PARMS {
    WORD        usBufferSize;           // block size of dlc buffers (>=80)
    WORD        cPoolBlocks;            // number of 16 bytes blocks in buffer
    DOS_ADDRESS dpPoolAddress;
    DOS_ADDRESS dpAdapterCheckExit;
    DOS_ADDRESS dpNetworkStatusExit;
    DOS_ADDRESS dpPcErrorExit;
    WORD        usOpenOption;
} LLC_MODIFY_OPEN_PARMS, *PLLC_MODIFY_OPEN_PARMS;

typedef struct _DOS_DLC_DIRECT_PARMS {
    WORD        usBufferSize;           // block size of dlc buffers (>=80)
    WORD        cPoolBlocks;            // number of 16 bytes blocks in buffer
    DOS_ADDRESS dpPoolAddress;          //
    DOS_ADDRESS dpAdapterCheckExit;
    DOS_ADDRESS dpNetworkStatusExit;
    DOS_ADDRESS dpPcErrorExit;
    DWORD       ulReserved1;
    WORD        usReserved2;
    WORD        usReserved3;
} DOS_DLC_DIRECT_PARMS, *PDOS_DLC_DIRECT_PARMS;

typedef struct _DOS_DLC_OPEN_SAP_PARMS {
    WORD        usStationId;            // SAP or link station id
    WORD        usUserStatValue;        // reserved for user
    UCHAR       uchT1;                  // response timer
    UCHAR       uchT2;                  // aknowledgment timer
    UCHAR       uchTi;                  // inactivity timer
    UCHAR       uchMaxOut;              // max tramists without ack
    UCHAR       uchMaxIn;               // max receives without ack
    UCHAR       uchMaxOutIncr;          // dynamic window increment value
    UCHAR       uchMaxRetryCnt;         // N2 value (retries)
    UCHAR       uchMaxMembers;          // maximum members for group SAP
    WORD        usMaxI_Field;           // maximum length of the Info field
    UCHAR       uchSapValue;            // SAP value to be assigned
    UCHAR       uchOptionsPriority;     // SAP options and access priority
    UCHAR       uchcStationCount;       // maximum number of link stations in sap
    UCHAR       uchReserved2[2];        //
    UCHAR       cGroupCount;            // number of group SAPs of this SAP
    PUCHAR      pGroupList;             // offset to the group list
    DWORD       DlcStatusFlags;         // User notify flag for DLC status changes
    WORD        usBufferSize;           // size of individual buffer in bytes
    WORD        cPoolBlocks;            // number of 16-byte blocks in pool
    DOS_ADDRESS dpPoolAddress;          // address of Buffer Pool (may be 0)
} DOS_DLC_OPEN_SAP_PARMS,  *PDOS_DLC_OPEN_SAP_PARMS;

typedef struct _DOS_DIR_STATUS_PARMS {
    UCHAR       auchPermanentAddress[6];// permanent encoded address
    UCHAR       auchNodeAddress[6];     // adapter's network address
    UCHAR       auchGroupAddress[4];    // adapter's group address
    UCHAR       auchFunctAddr[4];       // adapter's functional address
    UCHAR       uchMaxSap;              // maximum allowable SAP
    UCHAR       uchOpenSaps;            // number of currently open saps
    UCHAR       uchMaxStations;         // max number of stations (always 253)
    UCHAR       uchOpenStation;         // number of open stations (only up to 253)
    UCHAR       uchAvailStations;       // number of available stations (always 253)
    UCHAR       uchAdapterConfig;       // adapter configuration flags
    UCHAR       auchMicroCodeLevel[10]; // microcode level
    DOS_ADDRESS dpAdapterParmsAddr;     // shared RAM address of adapter parms
    DOS_ADDRESS dpAdapterMacAddr;       // shared RAM address of adapter MAC buffer
    DOS_ADDRESS dpTimerTick;            // address of DLC timer tick counter
    USHORT      usLastNetworkStatus;    // most recent network status issued
    DOS_ADDRESS dpExtendedParms;        // address of extended status table
} DOS_DIR_STATUS_PARMS, *PDOS_DIR_STATUS_PARMS;

typedef struct {
    DOS_ADDRESS dpAdapterCheckExit;     // adapter check appendage
    DOS_ADDRESS dpNetworkStatusExit;    // network status change appendage
    DOS_ADDRESS dpPcErrorExit;          // workstation error appendage
} LLC_DIR_SET_USER_APPENDAGE_PARMS, *PLLC_DIR_SET_USER_APPENDAGE_PARMS;

#include <packoff.h>

union _LLC_DOS_PARMS {
    LLC_BUFFER_FREE_PARMS       BufferFree;
    LLC_BUFFER_GET_PARMS        BufferGet;
    LLC_DLC_CONNECT_PARMS       DlcConnectStation;
    LLC_DLC_MODIFY_PARMS        DlcModify;
    LLC_DLC_OPEN_SAP_PARMS      DlcOpenSap;
    LLC_DLC_OPEN_STATION_PARMS  DlcOpenStation;
    LLC_DLC_REALLOCATE_PARMS    DlcReallocate;
    LLC_DLC_SET_THRESHOLD_PARMS DlcSetThreshold;
    LLC_DLC_STATISTICS_PARMS    DlcStatistics;
    LLC_DIR_INITIALIZE_PARMS    DirInitialize;
    LLC_MODIFY_OPEN_PARMS       DirModifyOpenParms;
    LLC_DIR_OPEN_ADAPTER_PARMS  DirOpenAdapter;
    LLC_DIR_OPEN_DIRECT_PARMS   DirOpenDirect;
    LLC_DIR_READ_LOG_PARMS      DirReadLog;
    LLC_DIR_SET_EFLAG_PARMS     DirSetExceptionFlags;
    LLC_DIR_SET_USER_APPENDAGE_PARMS    DirSetUserAppendage;
    LLC_DIR_STATUS_PARMS        DirStatus;
    DOS_DIR_STATUS_PARMS        DosDirStatus;
    LLC_READ_PARMS              Read;
    LLC_DOS_RECEIVE_PARMS_EX    Receive;
    LLC_DOS_RECEIVE_PARMS       DosReceive;
    LLC_TRANSMIT_PARMS          Transmit;
    LLC_TRANSMIT2_COMMAND       Transmit2;
    LLC_TRACE_INITIALIZE_PARMS  TraceInitialize;
    DOS_DLC_OPEN_SAP_PARMS      DosDlcOpenSap;
};

//
// ADAPTER_TYPE - what type of network adapter we have - Token Ring, Ethernet
// or (less likely) PC network
//

typedef enum {
    TokenRing,
    Ethernet,
    PcNetwork,
    UnknownAdapter
} ADAPTER_TYPE;

//
// LOCAL_BUSY_STATE - a link station can be in 1 of 3 emulated local-busy
// (buffer) states:
//
//  NOT_BUSY
//      the station doesn't have any backed-up I-frames pending
//
//  BUSY
//      the station is in emulated local-busy(buffer) state and a
//      DLC.FLOW.CONTROL(local-busy(buffer), set) has been sent to
//      the DLC device driver
//
//  BUSY_BUFFER
//      to get out of BUSY state into CLEARING, we need at least one buffer and
//      a DLC.FLOW.CONTROL from the app. Because apps can issue DLC.FLOW.CONTROL
//      and BUFFER.FREE in the wrong order, we need an AND of these 2 commands
//      to get going again. So we have this intermediate state where we are
//      awaiting either command to restart I-Frame reception
//
//  BUSY_FLOW
//      Together with BUSY_BUFFER, used to create a hysteresis whereby we can't
//      reach CLEARING from BUSY without getting both a DLC.FLOW.CONTROL and
//      BUFFER.FREE
//
//  CLEARING
//      the VDM app has cleared the emulated local-busy state, but
//      the DLC device driver is still in local-busy (buffer) state.
//      When the last deferred I-Frame is indicated to the VDM app,
//      the NT device driver local-busy(buffer) state will be reset
//      and normal service will resume
//
//
// State transitions:
//
//  NOT_BUSY -> BUSY
//      occurs when we discover there aren't enough DOS buffers to
//      receive an I-Frame. This transition is distinguished by the
//      following actions:
//
//          1. DLC.FLOW.CONTROL(local-busy(buffer), set) is indicated to
//             the DLC device driver
//          2. the received I-Frame is dequeued from the event queue for
//             this adapter and queued on the LocalBusyInfo.Queue
//          3. a local-busy(buffer) DLC status change event is indicated to
//             the DOS DLC app
//
//  BUSY -> BUSY_FLOW/BUSY_BUFFER
//      occurs when either a DLC.FLOW.CONTROL or BUFFER.FREE (resp) is issued
//
//  BUSY_FLOW/BUSY_BUFFER -> CLEARING
//      occurs when the DOS DLC app indicates we can continue receiving.
//      This is done when the other (DLC.FLOW.CONTROL or BUFFER.FREE) required
//      command is issued
//      This transition is distinguished by the following actions:
//
//          1. DOS DLC app issues DLC.FLOW.CONTROL(local-busy(buffer), reset)
//          2. DOS DLC app *may* issue BUFFER.FREE to return receive
//             buffers to the SAP pool
//
//  CLEARING -> NOT_BUSY
//      occurs when we indicate the last deferred receive to the DOS DLC
//      app. At this point we can do the following:
//
//          1. issue DLC.FLOW.CONTROL(local-busy(buffer), reset) to the
//             device driver
//
//  CLEARING -> BUSY
//      occurs when, during indicating deferred received I-Frames to the DOS
//      DLC app, we once again run out of buffers. Again, we indicate a DLC
//      status change of local-busy(buffer) to the DOS DLC app, but WE DO NOT
//      indicate local-busy(buffer) to the DLC device driver (it is still in
//      local-busy(buffer) state)
//

typedef enum {
    NOT_BUSY = 0,
    CLEARING,
    BUSY,
    BUSY_BUFFER,
    BUSY_FLOW
} LOCAL_BUSY_STATE;

//
// LOCAL_BUSY_INFO - this structure maintains a local-busy(buffer) state
// indicator and a pointer to a queue of deferred received I-Frames per link
// station per adapter
//

typedef struct {

    //
    // State maintains the tri-state of the link station w.r.t. received I-Frames
    //
    //  NOT_BUSY
    //      nothing queued on Queue, get next completed event from event Q
    //
    //  BUSY
    //      local-busy(buffer) state has been set in DLC driver,
    //      awaiting buffers & flow control command from DOS DLC app
    //
    //  CLEARING
    //      DOS DLC app has submitted DLC.FLOW.CONTROL(local_busy(buffer), reset)
    //      command, we are now trying to indicate deferred received I-Frames to
    //      DOS DLC app, pending enough DOS receive buffers
    //

    LOCAL_BUSY_STATE State;

    //
    // Queue - when in BUSY and CLEARING states, maintains a linked list of
    // completed NT READ CCBs containing received I-Frames
    //

    PLLC_CCB Queue;

#if DBG

    //
    // track queue depth for each link station in debug version
    //

    DWORD Depth;
#endif

} LOCAL_BUSY_INFO;

//
// MAX_I_FRAME_DEPTH - we don't expect the queue of deferred I-Frames to grow
// beyond this small number. The deferred I-Frame queue is a buffer between
// running out of receive buffers & restarting I-Frame reception
//

#define MAX_I_FRAME_DEPTH   64  // !

//
// DOS_ADAPTER - there is one of these for each DOS adapter (i.e. 2 max.). The
// structure contains information about the virtual state of each DOS adapter.
// We record such information as the parameters used to open the adapter, the
// exit addresses and the direct station information
//

typedef struct {

    //
    // AdapterType - tells us what type (class?) of adapter we are using. We
    // mainly use this to differentiate the types of values we return based
    // on whether this is a Token Ring adapter. We get the information from
    // the NT DIR.STATUS command
    //

    ADAPTER_TYPE AdapterType;

    //
    // IsOpen will be TRUE when this adapter has been successfully opened
    //

    BOOLEAN IsOpen;

    //
    // DirectStationOpen is TRUE when the direct station has been initialized
    // for this adapter. This is required because the direct station is opened
    // separately from the adapter in NT, but DOS expects both to be opened
    // simultaneously. Hence, we only issue a DIR.OPEN.DIRECT when the DOS app
    // issues a request for the direct station
    //

    BOOLEAN DirectStationOpen;

    //
    // if DirectReceive is TRUE then there is a receive outstanding on the
    // direct station for this adapter
    //

    BOOLEAN DirectReceive;

    //
    // if WaitingRestore is TRUE then we must get a DIR.RESTORE.OPEN.PARMS
    // before we can accept the next DIR.MODIFY.OPEN.PARMS
    //

    BOOLEAN WaitingRestore;

    //
    // BufferFree is TRUE when a BUFFER_FREE has been successfully issued for
    // any station ID belonging to this adapter
    //

    BOOLEAN BufferFree;

    //
    // BufferPool is the buffer pool for this adapter's direct station
    //

    PVOID BufferPool;

    //
    // CurrentExceptionHandlers and PreviousExceptionHandlers are the addresses
    // of exception 'exit' routines in DOS memory which are called asynchronously
    // if one of the special exceptions occurs. These are mapped to exception
    // flags in NT DLC and are presented as such in the READ CCB (ulCompletionFlag)
    //
    // exception handlers are always presented in the following order:
    //
    //  Adapter Check Exit
    //  Network Status Exit
    //  PC Error Exit
    //

    DWORD CurrentExceptionHandlers[3];
    DWORD PreviousExceptionHandlers[3];

    //
    // DlcStatusChangeAppendage - this appendage pointer is supplied in DLC.OPEN.SAP
    // - one for each SAP. We need to keep them here because the emulator can
    // generate its own DLC status change call-backs (local-busy(buffer))
    //

    DWORD DlcStatusChangeAppendage[DOS_DLC_MAX_SAPS];

    //
    // LastNetworkStatusChange is the last network status change we recorded.
    // This is reported in DIR.STATUS
    //

    WORD LastNetworkStatusChange;

    //
    // UserStatusValue - we have to record the USER_STAT_VALUE from each
    // successful DLC.OPEN.SAP. This is returned to the DLC status change
    // appendage. We need this info for when we generate our own status change
    // event (ie when we detect emulated local busy (buffer) state)
    //

    WORD UserStatusValue[DOS_DLC_MAX_SAPS];

    //
    // AdapterParms are the actual adapter parameters that this adapter was
    // opened with - either specified in the DIR.OPEN.ADAPTER from the DOS
    // app, or those which we use internally when we automatically open the
    // adapter
    //

    ADAPTER_PARMS AdapterParms;

    //
    // DlcSpecified will be TRUE if the DLC_PARMS table was given when the
    // adapter was opened (either by DIR.OPEN.ADAPTER from DOS app, or by
    // DIR.OPEN.ADAPTER from automatic open)
    //

    BOOLEAN DlcSpecified;

    //
    // DlcParms - the DLC parameters specified in the open
    //

    DLC_PARMS DlcParms;

    //
    // AdapterCloseCcb - used in asynchronous adapter close when close is
    // initiated by emulator
    //

    LLC_CCB AdapterCloseCcb;

    //
    // DirectCloseCcb - used in asynchronous direct station close when close is
    // initiated by emulator
    //

    LLC_CCB DirectCloseCcb;

    //
    // ReadCcb - pointer to current READ CCB for this adapter
    //

    PLLC_CCB ReadCcb;

    //
    // EventQueueCritSec - must hold this while accessing EventQueue
    //

    CRITICAL_SECTION EventQueueCritSec;

    //
    // EventQueue - linked list of pending completed READ CCBs. These are linked
    // by pNext field which is not normally used by READ CCB. The event queue is
    // a serialized list of asynchronous events which have occurred for this
    // adapter. Events are command completions, transmit completions, received
    // data frames and status changes
    //

    PLLC_CCB EventQueueHead;    // pointer to READ CCB at head of queue
    PLLC_CCB EventQueueTail;    //    "     "   "   "  "  end  "    "
    DWORD QueueElements;        // count of elements currently on EventQueue

    //
    // LocalBusyCritSec - must hold this while accessing DeferredReceives or
    // LocalBusyInfo array
    //

    CRITICAL_SECTION LocalBusyCritSec;

    //
    // DeferredReceives - reference count of number of link stations for this
    // adapter which are in local busy (buffer) state. Accessed while holding
    // LocalBusyCritSec. Serves as a boolean: check for !0 to discover if there
    // are deferred receives before checking all of LocalBusyInfo
    //

    DWORD DeferredReceives;

    //
    // FirstIndex and LastIndex - the start & stop points for searches through
    // LocalBusyInfo. These are used in an attempt to improve searching, since
    // for the vast majority of time, very few of the 255 possible slots in
    // LocalBusyInfo will be used
    //
    // NOTE: these are array indicies, NOT link station ids (index = id - 1)
    //

    DWORD FirstIndex;
    DWORD LastIndex;

    //
    // LocalBusyInfo - when a link station is in emulated local busy (buffer)
    // state, we dequeue any completed received I-Frames from the event queue
    // and link them onto the LocalBusyInfo list. For each adapter, there are
    // 255 lists - one per link station (there are 255 possible link stations
    // per adapter). The deferred receives are a list of completed NT READ CCBs
    // linked by CCB.pNext field. The lists serve as a buffer between realizing
    // we are out of buffers and the DLC device driver receiving the
    // DLC.FLOW.CONTROL(set, buffer) command. The lists are not expected to
    // grow very long
    //
    // This array combines the state of each link station for this adapter w.r.t.
    // local-busy(buffer) and maintains the list of deferred I-Frames.
    //
    // The array is accessed both by the main VDM thread and the EventHandlerThread
    // and so must only be accessed when holding LocalBusyCritSec
    //
    // Since there are 255 possible link stations per adapter and since the
    // Direct Station doesn't support link stations, link station 01 uses slot
    // 0, etc.
    //

    LOCAL_BUSY_INFO LocalBusyInfo[DOS_DLC_MAX_LINKS];

} DOS_ADAPTER, *PDOS_ADAPTER;

#define NO_LINKS_BUSY   ((DWORD)0x7fffffff)

//
// DOS DLC prototypes and externals
//

extern PLLC_BUFFER aOverflowedData[];
extern DWORD OpenedAdapters;
extern DOS_ADDRESS dpVdmWindow;

//
// vrdlc5c.c
//

VOID
VrDlc5cHandler(
    VOID
    );

VOID
CompleteCcbProcessing(
    IN LLC_STATUS Status,
    IN LLC_DOS_CCB UNALIGNED * pCcb,
    IN PLLC_PARMS pNtParms
    );

LLC_STATUS
LlcCommand(
    IN  UCHAR   AdapterNumber,
    IN  UCHAR   Command,
    IN  DWORD   Parameter
    );

LLC_STATUS
BufferFree(
    IN  UCHAR   AdapterNumber,
    IN  PVOID   pFirstBuffer,
    OUT LPWORD  pusBuffersLeft
    );

VOID
VrVdmWindowInit(
    VOID
    );

VOID
TerminateDlcEmulation(
    VOID
    );

//
// vrdlcbuf.c
//

VOID
InitializeBufferPools(
    VOID
    );

LLC_STATUS
CreateBufferPool(
    IN  DWORD   PoolIndex,
    IN  DOS_ADDRESS dpBuffer,
    IN  WORD    BufferCount,
    IN  WORD    BufferSize
    );

VOID
DeleteBufferPool(
    IN  DWORD   PoolIndex
    );

LLC_STATUS
GetBuffers(
    IN  DWORD   PoolIndex,
    IN  WORD    BuffersToGet,
    IN  DPLLC_DOS_BUFFER *pdpBuffer,
    OUT LPWORD  pusBuffersLeft,
    IN  BOOLEAN PartialList,
    OUT PWORD   BuffersGot OPTIONAL
    );

LLC_STATUS
FreeBuffers(
    IN  DWORD   PoolIndex,
    IN  DPLLC_DOS_BUFFER dpBuffer,
    OUT LPWORD  pusBuffersLeft
    );

WORD
CalculateBufferRequirement(
    IN UCHAR Adapter,
    IN WORD StationId,
    IN PLLC_BUFFER pFrame,
    IN LLC_DOS_PARMS UNALIGNED * pDosParms,
    OUT PWORD BufferSize
    );

LLC_STATUS
CopyFrame(
    IN PLLC_BUFFER pFrame,
    IN DPLLC_DOS_BUFFER DosBuffers,
    IN WORD UserLength,
    IN WORD BufferSize,
    IN DWORD Flags
    );

BOOLEAN
AllBuffersInPool(
    IN DWORD PoolIndex
    );

//
// vrdlcpst.c
//

VOID
VrDlcInitialize(
    VOID
    );

BOOLEAN
VrDlcHwInterrupt(
    VOID
    );

BOOLEAN
ResetEmulatedLocalBusyState(
    IN BYTE AdapterNumber,
    IN WORD StationId,
    IN BYTE DlcCommand
    );

BOOLEAN
InitializeEventHandler(
    VOID
    );

PLLC_CCB
InitiateRead(
    IN DWORD AdapterNumber,
    OUT LLC_STATUS* ErrorStatus
    );
