summaryrefslogtreecommitdiffstats
path: root/private/mvdm/vdmredir/vrdlc5c.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/mvdm/vdmredir/vrdlc5c.c')
-rw-r--r--private/mvdm/vdmredir/vrdlc5c.c3893
1 files changed, 3893 insertions, 0 deletions
diff --git a/private/mvdm/vdmredir/vrdlc5c.c b/private/mvdm/vdmredir/vrdlc5c.c
new file mode 100644
index 000000000..d45fec96c
--- /dev/null
+++ b/private/mvdm/vdmredir/vrdlc5c.c
@@ -0,0 +1,3893 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+Copyright (c) 1991 Nokia Data Systems
+
+Module Name:
+
+ vrdlc5c.c
+
+Abstract:
+
+ This module handles DLC INT 5Ch calls from a VDM
+
+ Contents:
+ VrDlc5cHandler
+ (ValidateDosAddress)
+ (AutoOpenAdapter)
+ (ProcessImmediateCommand)
+ (MapDosCommandsToNt)
+ CompleteCcbProcessing
+ (InitializeAdapterSupport)
+ (SaveExceptions)
+ (RestoreExceptions)
+ (CopyDosBuffersToDescriptorArray)
+ (BufferCreate)
+ (SetExceptionFlags)
+ LlcCommand
+ (OpenAdapter)
+ (CloseAdapter)
+ (OpenDirectStation)
+ (CloseDirectStation)
+ BufferFree
+ (VrDlcInit)
+ VrVdmWindowInit
+ (GetAdapterType)
+ (LoadDlcDll)
+ TerminateDlcEmulation
+ InitializeDlcWorkerThread
+ VrDlcWorkerThread
+ DlcCallWorker
+
+Author:
+
+ Antti Saarenheimo (o-anttis) 26-DEC-1991
+
+Revision History:
+
+ 16-Jul-1992 Richard L Firth (rfirth)
+ Rewrote large parts - separated functions into categories (complete
+ in DLL, complete in driver, complete asynchronously); allocate NT
+ CCBs for commands which complete asynchronously; fixed asynchronous
+ processing; added extra debugging; condensed various per-adapter data
+ structures into Adapters data structure; made processing closer to IBM
+ LAN Tech. Ref. specification
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h> // ASSERT, DbgPrint
+#include <nturtl.h>
+#include <windows.h>
+#include <softpc.h> // x86 virtual machine definitions
+#include <vrdlctab.h>
+#include <vdmredir.h>
+#include <smbgtpt.h>
+#include <dlcapi.h> // Official DLC API definition
+#include <ntdddlc.h> // IOCTL commands
+#include <dlcio.h> // Internal IOCTL API interface structures
+#include <vrdefld.h> // VDM_LOAD_INFO
+#include "vrdlc.h"
+#include "vrdebug.h"
+#include "vrdlcdbg.h"
+
+//
+// defines
+//
+
+//
+// for each DLC command, a flags byte in DlcFunctionalCharacteristics uses these
+// bits to indicate properties of the command processing
+//
+
+#define POINTERS_IN_TABLE 0x01 // pointers in parameter table
+#define OUTPUT_PARMS 0x02 // parameters returned from DLC
+#define SECONDARY_TABLE 0x04 // parameter table has pointers to secondary table(s)
+#define IMMEDIATE_COMMAND 0x20 // command executes without call to DLC DLL
+#define SYNCHRONOUS_COMMAND 0x40 // command executes in workstation
+#define UNSUPPORTED_COMMAND 0x80 // command is not supported in DOS DLC
+
+//
+// macros
+//
+
+//
+// IS_IMMEDIATE_COMMAND - the following commands are those which complete
+// 'immediately' - i.e. without having to submit the CCB to AcsLan or NtAcsLan.
+// Immediate commands may read and write the parameter table though
+//
+
+#define IS_IMMEDIATE_COMMAND(c) (((c) == LLC_BUFFER_FREE) || \
+ ((c) == LLC_BUFFER_GET) || \
+ ((c) == LLC_DIR_INTERRUPT) || \
+ ((c) == LLC_DIR_MODIFY_OPEN_PARMS) || \
+ ((c) == LLC_DIR_RESTORE_OPEN_PARMS) || \
+ ((c) == LLC_DIR_SET_USER_APPENDAGE) \
+ )
+
+//
+// private prototypes
+//
+
+LLC_STATUS
+ValidateDosAddress(
+ IN DOS_ADDRESS Address,
+ IN WORD Size,
+ IN LLC_STATUS ErrorCode
+ );
+
+LLC_STATUS
+AutoOpenAdapter(
+ IN UCHAR AdapterNumber
+ );
+
+LLC_STATUS
+ProcessImmediateCommand(
+ IN UCHAR AdapterNumber,
+ IN UCHAR Command,
+ IN LLC_DOS_PARMS UNALIGNED * pParms
+ );
+
+LLC_STATUS
+MapDosCommandsToNt(
+ IN PLLC_CCB pDosCcb,
+ IN DOS_ADDRESS dpOriginalCcbAddress,
+ OUT LLC_DOS_CCB UNALIGNED * pOutputCcb
+ );
+
+LLC_STATUS
+InitializeAdapterSupport(
+ IN UCHAR AdapterNumber,
+ IN DOS_DLC_DIRECT_PARMS UNALIGNED * pDirectParms OPTIONAL
+ );
+
+VOID
+SaveExceptions(
+ IN UCHAR AdapterNumber,
+ IN LPDWORD pulExceptionFlags
+ );
+
+LPDWORD
+RestoreExceptions(
+ IN UCHAR AdapterNumber
+ );
+
+LLC_STATUS
+CopyDosBuffersToDescriptorArray(
+ IN OUT PLLC_TRANSMIT_DESCRIPTOR pDescriptors,
+ IN PLLC_XMIT_BUFFER pDlcBufferQueue,
+ IN OUT LPDWORD pIndex
+ );
+
+LLC_STATUS
+BufferCreate(
+ IN UCHAR AdapterNumber,
+ IN PVOID pVirtualMemoryBuffer,
+ IN DWORD ulVirtualMemorySize,
+ IN DWORD ulMinFreeSizeThreshold,
+ OUT HANDLE* phBufferPoolHandle
+ );
+
+LLC_STATUS
+SetExceptionFlags(
+ IN UCHAR AdapterNumber,
+ IN DWORD ulAdapterCheckFlag,
+ IN DWORD ulNetworkStatusFlag,
+ IN DWORD ulPcErrorFlag,
+ IN DWORD ulSystemActionFlag
+ );
+
+LLC_STATUS
+OpenAdapter(
+ IN UCHAR AdapterNumber
+ );
+
+VOID
+CloseAdapter(
+ IN UCHAR AdapterNumber
+ );
+
+LLC_STATUS
+OpenDirectStation(
+ IN UCHAR AdapterNumber
+ );
+
+VOID
+CloseDirectStation(
+ IN UCHAR AdapterNumber
+ );
+
+LLC_STATUS
+VrDlcInit(
+ VOID
+ );
+
+ADAPTER_TYPE
+GetAdapterType(
+ IN UCHAR AdapterNumber
+ );
+
+BOOLEAN
+LoadDlcDll(
+ VOID
+ );
+
+BOOLEAN
+InitializeDlcWorkerThread(
+ VOID
+ );
+
+VOID
+VrDlcWorkerThread(
+ IN LPVOID Parameters
+ );
+
+LLC_STATUS
+DlcCallWorker(
+ PLLC_CCB pInputCcb,
+ PLLC_CCB pOriginalCcb,
+ PLLC_CCB pOutputCcb
+ );
+
+//
+// public data
+//
+
+//
+// lpVdmWindow is the flat 32-bit address of the VDM_REDIR_DOS_WINDOW structure
+// in DOS memory (in the redir TSR)
+//
+
+LPVDM_REDIR_DOS_WINDOW lpVdmWindow = 0;
+
+//
+// dpVdmWindow is the DOS address (ssssoooo, s=segment, o=offset) of the
+// VDM_REDIR_DOS_WINDOW structure in DOS memory (in the redir TSR)
+//
+
+DOS_ADDRESS dpVdmWindow = 0;
+
+DWORD OpenedAdapters = 0;
+
+//
+// Adapters - for each adapter supported by DOS emulation (maximum 2 adapters -
+// primary & secondary) there is a structure which maintains adapter specific
+// information - like whether the adapter has been opened, etc.
+//
+
+DOS_ADAPTER Adapters[DOS_DLC_MAX_ADAPTERS];
+
+//
+// all functions in DLCAPI.DLL are now called indirected through function pointer
+// create these stupid typedefs to avoid compiler warnings
+//
+
+typedef ACSLAN_STATUS (*ACSLAN_FUNC_PTR)(IN OUT PLLC_CCB, OUT PLLC_CCB*);
+ACSLAN_FUNC_PTR lpAcsLan;
+
+typedef LLC_STATUS (*DLC_CALL_DRIVER_FUNC_PTR)(IN UINT, IN UINT, IN PVOID, IN UINT, OUT PVOID, IN UINT);
+DLC_CALL_DRIVER_FUNC_PTR lpDlcCallDriver;
+
+typedef LLC_STATUS (*NTACSLAN_FUNC_PTR)(IN PLLC_CCB, IN PVOID, OUT PLLC_CCB, IN HANDLE OPTIONAL);
+NTACSLAN_FUNC_PTR lpNtAcsLan;
+
+//
+// private data
+//
+
+static LLC_EXTENDED_ADAPTER_PARMS DefaultExtendedParms = {
+ NULL, // hBufferPool
+ NULL, // pSecurityDescriptor
+ LLC_ETHERNET_TYPE_DEFAULT // LlcEthernetType
+};
+
+//
+// DlcFunctionCharacteristics - for each DOS DLC command, tells us the size of
+// the parameter table to copy and whether there are pointers in the parameter
+// table. Segmented 16-bit pointers in the parameter table must be converted to
+// flat 32-bit pointers. The Flags byte tells us - at a glance - the following:
+//
+// - if this command is supported a) in DOS DLC, b) in our implementation
+// - if this command has parameters
+// - if there are (DOS) pointers in the parameter table
+// - if this command receives output parameters (ie written to parameter table)
+// - if the parameter table has secondary parameter tables (DIR.OPEN.ADAPTER)
+// - if this command is synchronous (ie does not return 0xFF)
+//
+
+struct {
+ BYTE ParamSize; // no parameter tables >255 bytes long
+ BYTE Flags;
+} DlcFunctionCharacteristics[] = {
+ {0, IMMEDIATE_COMMAND}, // 0x00, DIR.INTERRUPT
+ {
+ sizeof(LLC_DIR_MODIFY_OPEN_PARMS),
+ IMMEDIATE_COMMAND
+ }, // 0x01, DIR.MODIFY.OPEN.PARMS
+ {0, IMMEDIATE_COMMAND}, // 0x02, DIR.RESTORE.OPEN.PARMS
+ {
+ sizeof(LLC_DIR_OPEN_ADAPTER_PARMS),
+ SYNCHRONOUS_COMMAND
+ | SECONDARY_TABLE
+ | OUTPUT_PARMS
+ | POINTERS_IN_TABLE
+ }, // 0x03, DIR.OPEN.ADAPTER
+ {0, 0x00}, // 0x04, DIR.CLOSE.ADAPTER
+ {0, UNSUPPORTED_COMMAND}, // 0x05, ?
+ {0, SYNCHRONOUS_COMMAND}, // 0x06, DIR.SET.GROUP.ADDRESS
+ {0, SYNCHRONOUS_COMMAND}, // 0x07, DIR.SET.FUNCTIONAL.ADDRESS
+ {0, SYNCHRONOUS_COMMAND}, // 0x08, READ.LOG
+ {0, UNSUPPORTED_COMMAND}, // 0x09, NT: TRANSMIT.FRAME
+ {
+ sizeof(LLC_TRANSMIT_PARMS),
+ POINTERS_IN_TABLE
+ }, // 0x0a, TRANSMIT.DIR.FRAME
+ {
+ sizeof(LLC_TRANSMIT_PARMS),
+ POINTERS_IN_TABLE
+ }, // 0x0b, TRANSMIT.I.FRAME
+ {0, UNSUPPORTED_COMMAND}, // 0x0c, ?
+ {sizeof(LLC_TRANSMIT_PARMS), POINTERS_IN_TABLE},// 0x0d, TRANSMIT.UI.FRAME
+ {sizeof(LLC_TRANSMIT_PARMS), POINTERS_IN_TABLE},// 0x0e, TRANSMIT.XID.CMD
+ {sizeof(LLC_TRANSMIT_PARMS), POINTERS_IN_TABLE},// 0x0f, TRANSMIT.XID.RESP.FINAL
+ {sizeof(LLC_TRANSMIT_PARMS), POINTERS_IN_TABLE},// 0x10, TRANSMIT.XID.RESP.NOT.FINAL
+ {sizeof(LLC_TRANSMIT_PARMS), POINTERS_IN_TABLE},// 0x11, TRANSMIT.TEST.CMD
+ {0, UNSUPPORTED_COMMAND}, // 0x12, ?
+ {0, UNSUPPORTED_COMMAND}, // 0x13, ?
+ {0, 0x00}, // 0x14, DLC.RESET
+ {
+ sizeof(LLC_DLC_OPEN_SAP_PARMS),
+ SYNCHRONOUS_COMMAND
+ | OUTPUT_PARMS
+ | POINTERS_IN_TABLE
+ }, // 0x15, DLC.OPEN.SAP
+ {0, 0x00}, // 0x16, DLC.CLOSE.SAP
+ {0, SYNCHRONOUS_COMMAND}, // 0x17, DLC_REALLOCATE
+ {0, UNSUPPORTED_COMMAND}, // 0x18, ?
+ {
+ sizeof(LLC_DLC_OPEN_STATION_PARMS),
+ SYNCHRONOUS_COMMAND
+ | OUTPUT_PARMS
+ | POINTERS_IN_TABLE
+ }, // 0x19, DLC.OPEN.STATION
+ {0, 0x00}, // 0x1a, DLC.CLOSE.STATION
+ {
+ sizeof(LLC_DLC_CONNECT_PARMS),
+ POINTERS_IN_TABLE
+ }, // 0x1b, DLC.CONNECT.STATION
+ {
+ sizeof(LLC_DLC_MODIFY_PARMS),
+ SYNCHRONOUS_COMMAND
+ | POINTERS_IN_TABLE
+ }, // 0x1c, DLC.MODIFY
+ {0, SYNCHRONOUS_COMMAND}, // 0x1d, DLC.FLOW.CONTROL
+ {
+ sizeof(LLC_DLC_STATISTICS_PARMS),
+ SYNCHRONOUS_COMMAND
+ | OUTPUT_PARMS
+ | POINTERS_IN_TABLE
+ }, // 0x1e, DLC.STATISTICS
+ {0, UNSUPPORTED_COMMAND}, // 0x1f, ?
+ {
+ sizeof(LLC_DOS_DIR_INITIALIZE_PARMS),
+ SYNCHRONOUS_COMMAND
+ | OUTPUT_PARMS
+ }, // 0x20, DIR.INITIALIZE
+ {
+ sizeof(DOS_DIR_STATUS_PARMS) - 2,
+ SYNCHRONOUS_COMMAND
+ | OUTPUT_PARMS
+ | POINTERS_IN_TABLE
+ }, // 0x21, DIR.STATUS
+ {0, 0x00}, // 0x22, DIR.TIMER.SET
+ {0, SYNCHRONOUS_COMMAND}, // 0x23, DIR.TIMER.CANCEL
+ {0, UNSUPPORTED_COMMAND}, // 0x24, PDT.TRACE.ON / DLC_TRACE_INITIALIZE
+ {0, UNSUPPORTED_COMMAND}, // 0x25, PDT.TRACE.OFF
+ {
+ sizeof(LLC_BUFFER_GET_PARMS),
+ IMMEDIATE_COMMAND
+ | OUTPUT_PARMS
+ }, // 0x26, BUFFER.GET
+ {
+ sizeof(LLC_BUFFER_FREE_PARMS),
+ IMMEDIATE_COMMAND
+ | POINTERS_IN_TABLE
+ }, // 0x27, BUFFER.FREE
+ {sizeof(LLC_DOS_RECEIVE_PARMS), OUTPUT_PARMS}, // 0x28, RECEIVE
+ {0, SYNCHRONOUS_COMMAND}, // 0x29, RECEIVE.CANCEL
+ {
+ sizeof(LLC_DOS_RECEIVE_MODIFY_PARMS),
+ SYNCHRONOUS_COMMAND
+ | OUTPUT_PARMS
+ }, // 0x2a, RECEIVE.MODIFY
+ {0, UNSUPPORTED_COMMAND}, // 0x2b, DIR.DEFINE.MIF.ENVIRONMENT
+ {0, SYNCHRONOUS_COMMAND}, // 0x2c, DLC.TIMER.CANCEL.GROUP
+ {
+ sizeof(LLC_DIR_SET_EFLAG_PARMS),
+ IMMEDIATE_COMMAND
+ } // 0x2d, DIR.SET.USER.APPENDAGE
+};
+
+//
+// routines
+//
+
+
+VOID
+VrDlc5cHandler(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Receives control from the INT 5Ch BOP provided by the DOS redir TSR. The
+ DLC calls can be subdivided into the following categories:
+
+ * complete within this translation layer
+ * complete synchronously in a call to AcsLan
+ * complete asynchronously after calling AcsLan
+
+ The latter type complete when a READ (which we submit when the adapter is
+ opened) completes. Control transfers to an ISR in the DOS redir TSR via
+ the EventHandlerThread (in vrdlcpst.c)
+
+ The calls can be further subdivided:
+
+ * calls which return parameters in the parameter table
+ * calls which do not return parameters in the parameter table
+
+ For the former type of call, we have to copy the parameter table from
+ DOS memory and copy the returned parameters back to DOS memory
+
+ With the exception of a few DLC commands, we assume that the parameter
+ tables are exactly the same size between DOS and NT, even if the don't
+ contain exactly the same information
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None, LLC_STATUS is return in AL register.
+
+--*/
+
+{
+ LLC_CCB ccb; // should be NT CCB for NtAcsLan
+ LLC_PARMS parms;
+ LLC_DOS_CCB UNALIGNED * pOutputCcb;
+ LLC_DOS_PARMS UNALIGNED * pDosParms;
+ DOS_ADDRESS dpOriginalCcbAddress;
+ BOOLEAN parmsCopied;
+ WORD paramSize;
+ LLC_STATUS status;
+ UCHAR command;
+ UCHAR adapter;
+ BYTE functionFlags;
+
+ static BOOLEAN IsDlcDllLoaded = FALSE;
+
+ IF_DEBUG(DLC) {
+ DPUT("VrDlc5cHandler entered\n");
+ }
+
+ //
+ // DLCAPI.DLL is now dynamically loaded
+ //
+
+ if (!IsDlcDllLoaded) {
+ if (!LoadDlcDll()) {
+ setAL(LLC_STATUS_COMMAND_CANCELLED_FAILURE);
+ return;
+ } else {
+ IsDlcDllLoaded = TRUE;
+ }
+ }
+
+ //
+ // dpOriginalCcbAddress is the segmented 16-bit address stored as a DWORD
+ // eg. a CCB1 address of 1234:abcd gets stored as 0x1234abcd. This will
+ // be used in asynchronous command completion to get back the address of
+ // the original DOS CCB
+ //
+
+ dpOriginalCcbAddress = (DOS_ADDRESS)MAKE_DWORD(getES(), getBX());
+
+ //
+ // pOutputCcb is the flat 32-bit address of the DOS CCB. We can use this
+ // to read and write byte fields only (unaligned)
+ //
+
+ pOutputCcb = POINTER_FROM_WORDS(getES(), getBX());
+ pOutputCcb->uchDlcStatus = (UCHAR)LLC_STATUS_PENDING;
+
+ //
+ // zero the CCB_POINTER (pNext) field. CCB1 cannot have chained CCBs on
+ // input: this is just for returning (cancelled) pending CCBs. If we don't
+ // zero it & the app leaves garbage there, then NtAcsLan can think it is
+ // a pointer to a chain of CCBs (CCB2 can be chained), which is bogus
+ //
+
+ WRITE_DWORD(&pOutputCcb->pNext, 0);
+
+ IF_DEBUG(CRITICAL) {
+ CRITDUMP(("INPUT CCB @%04x:%04x command=%02x\n", getES(), getBX(), pOutputCcb->uchDlcCommand));
+ }
+
+ IF_DEBUG(DOS_CCB_IN) {
+
+ //
+ // dump the input CCB1 - gives us an opportunity to check out what
+ // the DOS app is sending us, even if its garbage
+ //
+
+ DUMPCCB(pOutputCcb,
+ TRUE, // DumpAll
+ TRUE, // CcbIsInput
+ TRUE, // IsDos
+ HIWORD(dpOriginalCcbAddress), // segment
+ LOWORD(dpOriginalCcbAddress) // offset
+ );
+ }
+
+ //
+ // first check that the adapter is 0 or 1 - DOS only supports 2 adapters -
+ // and check that the request code in the CCB is not off the end of our
+ // table. Unsupported requests will be filtered out in the BIG switch
+ // statement below
+ //
+
+ adapter = pOutputCcb->uchAdapterNumber;
+ command = pOutputCcb->uchDlcCommand;
+
+ if (adapter >= DOS_DLC_MAX_ADAPTERS) {
+
+ //
+ // adapter is not 0 or 1 - return 0x1D
+ //
+
+ status = LLC_STATUS_INVALID_ADAPTER;
+ pOutputCcb->uchDlcStatus = (UCHAR)status;
+ } else if (command > LAST_ELEMENT(DlcFunctionCharacteristics)) {
+
+ //
+ // command is off end of supported list - return 0x01
+ //
+
+ status = LLC_STATUS_INVALID_COMMAND;
+ pOutputCcb->uchDlcStatus = (UCHAR)status;
+ } else {
+
+ //
+ // the command is in range. Get the parameter table size and flags from
+ // the function characteristics array
+ //
+
+ functionFlags = DlcFunctionCharacteristics[command].Flags;
+ paramSize = DlcFunctionCharacteristics[command].ParamSize;
+
+ //
+ // if we don't support this command, return an error
+ //
+
+ status = LLC_STATUS_SUCCESS;
+ if (functionFlags & UNSUPPORTED_COMMAND) {
+ status = LLC_STATUS_INVALID_COMMAND;
+ pOutputCcb->uchDlcStatus = LLC_STATUS_INVALID_COMMAND;
+ } else {
+
+ //
+ // command is supported. If it has a parameter table, check that
+ // the address is in range for 0x1B error check
+ //
+
+ if (paramSize) {
+ status = ValidateDosAddress((DOS_ADDRESS)(pOutputCcb->u.pParms),
+ paramSize,
+ LLC_STATUS_INVALID_PARAMETER_TABLE
+ );
+ }
+
+ //
+ // we allow the adapter to be opened as a consequence of another
+ // request since DOS apps could assume that the adapter has already
+ // been opened (by NetBIOS). If the command is DIR.OPEN.ADAPTER or
+ // DIR.CLOSE.ADAPTER then let it go through
+ //
+
+ if (status == LLC_STATUS_SUCCESS
+ && !Adapters[adapter].IsOpen
+ && !(command == LLC_DIR_OPEN_ADAPTER || command == LLC_DIR_CLOSE_ADAPTER)) {
+ status = AutoOpenAdapter(adapter);
+ } else {
+ status = LLC_STATUS_SUCCESS;
+ }
+ }
+
+ //
+ // if we have a valid command, an ok-looking parameter table pointer
+ // and an open adapter (or a command which will open or close it) then
+ // process the command
+ //
+
+ if (status == LLC_STATUS_SUCCESS) {
+
+ //
+ // get a 32-bit pointer to the DOS parameter table. This may be
+ // an unaligned address!
+ //
+
+ pDosParms = READ_FAR_POINTER(&pOutputCcb->u.pParms);
+
+ //
+ // the CCB commands are subdivided into those which do not need the
+ // CCB to be mapped from DOS memory to NT memory and which complete
+ // 'immediately' in this DLL, and those which must be mapped from
+ // DOS to NT and which may complete synchronously or asynchronously
+ //
+
+ if (functionFlags & IMMEDIATE_COMMAND) {
+
+ IF_DEBUG(DLC) {
+ DPUT("VrDlc5cHandler: request is IMMEDIATE command\n");
+ }
+
+ status = ProcessImmediateCommand(adapter, command, pDosParms);
+
+ //
+ // the following is safe - it is a single byte write
+ //
+
+ pOutputCcb->uchDlcStatus = status;
+
+ //
+ // the 'immediate' case is now complete, and control can be
+ // returned to the VDM
+ //
+
+ } else {
+
+ //
+ // the CCB is not one which can be completed immediately. We
+ // have to copy (and align) the DOS CCB (and the parameter
+ // table, if there is one) into 32-bit address space.
+ // Note that since we are going to call AcsLan or NtAcsLan
+ // with this CCB then we supply the correct CCB format - 2,
+ // not 1 as it was previously. However, handing in a CCB1
+ // didn't *seem* to cause any problems (yet)
+ //
+
+ RtlCopyMemory(&ccb, pOutputCcb, sizeof(*pOutputCcb));
+
+ //
+ // zero the unused fields
+ //
+
+ ccb.hCompletionEvent = 0;
+ ccb.uchReserved2 = 0;
+ ccb.uchReadFlag = 0;
+ ccb.usReserved3 = 0;
+
+ parmsCopied = FALSE;
+ if (paramSize) {
+
+ //
+ // if the parameter table contains (segmented) pointers
+ // (which we need to convert to flat-32 bit pointers) OR
+ // the parameter table is not DWORD aligned, copy the whole
+ // parameter table to 32-bit memory . If we need to modify
+ // pointers, do it in the specific case in the switch
+ // statement in MapDosCommandsToNt
+ //
+ // Note: DIR.OPEN.ADAPTER is a special case because its
+ // parameter table just points to 4 other parameter tables.
+ // We take care of this in MapDosCommandsToNt and
+ // CompleteCcbProcessing
+ //
+
+ if ((functionFlags & POINTERS_IN_TABLE)) {
+ RtlCopyMemory(&parms, pDosParms, paramSize);
+ ccb.u.pParameterTable = &parms;
+ parmsCopied = TRUE;
+ } else {
+
+ //
+ // didn't need to copy parameter table - leave it in
+ // DOS memory. It is safe to read & write this table
+ //
+
+ ccb.u.pParameterTable = (PLLC_PARMS)pDosParms;
+ }
+ }
+
+ //
+ // submit the synchronous/asynchronous CCB for processing
+ //
+
+ status = MapDosCommandsToNt(&ccb, dpOriginalCcbAddress, pOutputCcb);
+ if (status == STATUS_PENDING) {
+ status = LLC_STATUS_PENDING;
+ }
+
+ IF_DEBUG(CRITICAL) {
+ CRITDUMP(("CCB submitted: returns %02x\n", status));
+ }
+
+ IF_DEBUG(DLC) {
+ DPUT2("VrDlc5cHandler: MapDosCommandsToNt returns %#x (%d)\n", status, status);
+ }
+
+ //
+ // if status is not LLC_STATUS_PENDING then the CCB completed
+ // synchronously. We can complete the processing here
+ //
+
+ if (status != LLC_STATUS_PENDING) {
+ if ((functionFlags & OUTPUT_PARMS) && parmsCopied) {
+
+ //
+ // if there are no pointers in the parameter table then
+ // we can simply copy the 32-bit parameters to 16-bit
+ // memory. If there are pointers, then they will be in
+ // an incorrect format for DOS. We must update these
+ // parameter tables individually
+ //
+
+ if (!(functionFlags & POINTERS_IN_TABLE)) {
+ RtlCopyMemory(pDosParms, &parms, paramSize);
+ } else {
+ CompleteCcbProcessing(status, pOutputCcb, &parms);
+ }
+ }
+
+ //
+ // set the CCB status. It will be marked as PENDING if
+ // LLC_STATUS_PENDING returned from MapDosCommandsToNt
+ //
+
+ pOutputCcb->uchDlcStatus = (UCHAR)status;
+ }
+ }
+ } else {
+ pOutputCcb->uchDlcStatus = (UCHAR)status;
+ }
+ }
+
+ //
+ // return the DLC status in AL
+ //
+
+ setAL((UCHAR)status);
+
+#if DBG
+ IF_DEBUG(DOS_CCB_OUT) {
+
+ DPUT2("VrDlc5cHandler: returning AL=%02x CCB.RETCODE=%02x\n",
+ status,
+ pOutputCcb->uchDlcStatus
+ );
+
+ //
+ // dump the CCB being returned to the DOS app
+ //
+
+ DumpCcb(pOutputCcb,
+ TRUE, // DumpAll
+ FALSE, // CcbIsInput
+ TRUE, // IsDos
+ HIWORD(dpOriginalCcbAddress), // segment
+ LOWORD(dpOriginalCcbAddress) // offset
+ );
+ }
+
+ //
+ // make sure (in debug version) that the error code being returned is valid
+ // for this particular command. Doesn't necessarily mean the return code is
+ // semantically correct, just that it belongs to the set of allowed DLC
+ // return codes for the DLC command being processed
+ //
+
+ IF_DEBUG(DLC) {
+ if (!IsCcbErrorCodeAllowable(pOutputCcb->uchDlcCommand, pOutputCcb->uchDlcStatus)) {
+ DPUT2("Returning bad error code: Command=%02x, Retcode=%02x\n",
+ pOutputCcb->uchDlcCommand,
+ pOutputCcb->uchDlcStatus
+ );
+ DEBUG_BREAK();
+ }
+ }
+#endif
+}
+
+
+LLC_STATUS
+ValidateDosAddress(
+ IN DOS_ADDRESS Address,
+ IN WORD Size,
+ IN LLC_STATUS ErrorCode
+ )
+
+/*++
+
+Routine Description:
+
+ IBM DLC performs some checking of pointers - if the address points into
+ the IVT or is close enough to the end of a segment that the address would
+ wrap then we return an error
+
+ This is a moronic test, but we do it for compatibility (just in case an
+ app tests for the specific error code). There are a million other addresses
+ in DOS memory that need to be checked. The tests in this routine will only
+ protect against scribbling over the interrupt vectors, but would allow e.g.
+ scribbling over DOS's code segment
+
+Arguments:
+
+ Address - DOS address to check (ssssoooo, s=segment, o=offset)
+ Size - word size of structure at Address
+ ErrorCode - which error code to return. This function called to validate
+ A) the parameter table pointer, in which case the error code
+ to return is LLC_STATUS_INVALID_PARAMETER_TABLE (0x1B) or
+ B) pointers within the parameter table, in which case the
+ error to return is LLC_STATUS_INVALID_POINTER_IN_CCB (0x1C)
+
+Return Value:
+
+ LLC_STATUS
+
+--*/
+
+{
+ //
+ // convert segment:offset into 20-bit real-mode linear address
+ //
+
+ DWORD linearAddress = HIWORD(Address) * 16 + LOWORD(Address);
+
+ //
+ // the Interrupt Vector Table (IVT) in real-mode occupies addresses 0
+ // through 400h
+ //
+
+ if ((linearAddress < 0x400L) || (((DWORD)LOWORD(Address) + Size) < (DWORD)LOWORD(Address))) {
+ return ErrorCode;
+ }
+ return LLC_STATUS_SUCCESS;
+}
+
+
+LLC_STATUS
+AutoOpenAdapter(
+ IN UCHAR AdapterNumber
+ )
+
+/*++
+
+Routine Description:
+
+ Opens the adapter as a consequence of a request other than DIR.OPEN.ADAPTER
+
+Arguments:
+
+ AdapterNumber - which adapter to open
+
+Return Value:
+
+ LLC_STATUS
+ Success - LLC_STATUS_SUCCESS
+ Failure -
+
+--*/
+
+{
+ LLC_STATUS status;
+
+ //
+ // Any DLC command except DIR.OPEN.ADAPTER or DIR.INITIALIZE automatically
+ // opens the adapter. There are three reasons to do this:
+ //
+ // 1. DIR.STATUS command can be issued before DIR.OPEN.ADAPTER in DOS.
+ // In Windows/Nt this is not possible. Therefore, DIR.STATUS should
+ // open the adapter
+ //
+ // 2. An application may assume that the adapter is always opened
+ // by NetBIOS and that it can't open the adapter itself if it
+ // has already been opened
+ //
+ // 3. A DOS DLC application may initialize (= hw reset) a closed
+ // adapter before the open and that takes 5 - 10 seconds.
+ //
+
+ IF_DEBUG(DLC) {
+ DPUT1("AutoOpenAdapter: automatically opening adapter %d\n", AdapterNumber);
+ }
+
+ status = OpenAdapter(AdapterNumber);
+ if (status == LLC_STATUS_SUCCESS) {
+
+ //
+ // initialize the buffer pool for the direct station on this
+ // adapter. If this succeeds, open the direct station. If that
+ // succeeds, preset the ADAPTER_PARMS and DLC_PARMS structures
+ // in the DOS_ADAPTER with default values
+ //
+
+ status = InitializeAdapterSupport(AdapterNumber, NULL);
+ if (status == LLC_STATUS_SUCCESS) {
+ status = OpenDirectStation(AdapterNumber);
+ if (status == LLC_STATUS_SUCCESS) {
+
+ }
+ }
+
+ if (status != LLC_STATUS_SUCCESS) {
+
+ IF_DEBUG(DLC) {
+ DPUT("AutoOpenAdapter: InitializeAdapterSupport failed\n");
+ }
+
+ }
+ } else {
+
+ IF_DEBUG(DLC) {
+ DPUT("AutoOpenAdapter: auto open adapter failed\n");
+ }
+
+ }
+
+ return status;
+}
+
+
+LLC_STATUS
+ProcessImmediateCommand(
+ IN UCHAR AdapterNumber,
+ IN UCHAR Command,
+ IN LLC_DOS_PARMS UNALIGNED * pParms
+ )
+
+/*++
+
+Routine Description:
+
+ Processes CCB requests which complete 'immediately'. An immediate completion
+ is one where the CCB does not have to be submitted to the DLC driver. There
+ may be other calls to the driver as a consequence of the immediate command,
+ but the CCB itself is not submitted. Immediate command completion requires
+ the parameter table only. We may return parameters into the DOS parameter
+ table
+
+Arguments:
+
+ AdapterNumber - which adapter to process command for
+ Command - command to process
+ pParms - pointer to parameter table (in DOS memory)
+
+Return Value:
+
+ LLC_STATUS
+ Completion status of the 'immediate' command
+
+--*/
+
+{
+ LLC_STATUS status;
+ WORD cBuffersLeft;
+ WORD stationId;
+ DPLLC_DOS_BUFFER buffer;
+
+ switch (Command) {
+ case LLC_BUFFER_FREE:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_BUFFER_FREE\n");
+ }
+
+ //
+ // if the FIRST_BUFFER field is 0:0 then this request returns success
+ //
+
+ buffer = (DPLLC_DOS_BUFFER)READ_DWORD(&pParms->BufferFree.pFirstBuffer);
+ if (!buffer) {
+ status = LLC_STATUS_SUCCESS;
+ break;
+ }
+
+ //
+ // Windows/Nt doesn't need station id for buffer pool operation =>
+ // thus the field is reserved
+ //
+
+ stationId = READ_WORD(&pParms->BufferFree.usReserved1);
+ status = FreeBuffers(GET_POOL_INDEX(AdapterNumber, stationId),
+ buffer,
+ &cBuffersLeft
+ );
+
+ IF_DEBUG(CRITICAL) {
+ CRITDUMP(("LLC_BUFFER_FREE: %d\n", status));
+ }
+
+ if (status == LLC_STATUS_SUCCESS) {
+
+ //
+ // write the number of buffers left to the parameter table using
+ // WRITE_WORD macro, since the table may not be aligned on a WORD
+ // boundary
+ //
+
+ WRITE_WORD(&pParms->BufferFree.cBuffersLeft, cBuffersLeft);
+
+ //
+ // p3-4 of the IBM LAN Tech. Ref. states that the FIRST_BUFFER
+ // field will be set to zero when the request is completed
+ //
+
+ WRITE_DWORD(&pParms->BufferFree.pFirstBuffer, 0);
+
+ //
+ // note that a successful BUFFER.FREE has been executed for this
+ // adapter
+ //
+
+ Adapters[AdapterNumber].BufferFree = TRUE;
+
+ //
+ // perform half of the local-busy reset processing. This only has
+ // an effect if the link is in emulated local-busy(buffer) state.
+ // This is required because we need 2 events to get us out of local
+ // busy buffer state - a BUFFER.FREE and a DLC.FLOW.CONTROL command
+ // Apps don't always issue these in the correct sequence
+ //
+
+ ResetEmulatedLocalBusyState(AdapterNumber, stationId, LLC_BUFFER_FREE);
+
+ //
+ // this here because Extra! for Windows gets its state machine in a
+ // knot if we go buffer busy too quickly after a flow control
+ //
+
+ if (AllBuffersInPool(GET_POOL_INDEX(AdapterNumber, stationId))) {
+ ResetEmulatedLocalBusyState(AdapterNumber, stationId, LLC_DLC_FLOW_CONTROL);
+ }
+ }
+ break;
+
+ case LLC_BUFFER_GET:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_BUFFER_GET\n");
+ }
+
+ status = GetBuffers(
+ GET_POOL_INDEX(AdapterNumber, READ_WORD(&pParms->BufferGet.usReserved1)),
+ READ_WORD(&pParms->BufferGet.cBuffersToGet),
+ &buffer,
+ &cBuffersLeft,
+ FALSE,
+ NULL
+ );
+
+ //
+ // if GetBuffers fails, buffer is returned as 0
+ //
+
+ WRITE_WORD(&pParms->BufferGet.cBuffersLeft, cBuffersLeft);
+ WRITE_DWORD(&pParms->BufferGet.pFirstBuffer, buffer);
+ break;
+
+ case LLC_DIR_INTERRUPT:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DIR_INTERRUPT\n");
+ }
+
+ //
+ // We may consider, that the adapter is always initialized!
+ // I hope, that the apps doesn't use an appendage routine
+ // in DIR_INTERRUPT, because it would be very difficult to
+ // call from here.
+ //
+
+ status = LLC_STATUS_SUCCESS;
+ break;
+
+ case LLC_DIR_MODIFY_OPEN_PARMS:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DIR_MODIFY_OPEN_PARMS\n");
+ }
+
+ //
+ // this command is rejected if a BUFFER.FREE has been issued for this
+ // adapter or there is a RECEIVE active at the direct interface
+ //
+
+ if (Adapters[AdapterNumber].BufferFree || Adapters[AdapterNumber].DirectReceive) {
+
+ //
+ // BUGBUG - this can't be correct error code. Check what is
+ // returned by IBM DOS DLC stack
+ //
+
+ status = LLC_STATUS_INVALID_COMMAND;
+ } else if (Adapters[AdapterNumber].WaitingRestore) {
+
+ //
+ // BUGBUG - this can't be correct error code. Check what is
+ // returned by IBM DOS DLC stack
+ //
+
+ status = LLC_STATUS_INVALID_COMMAND;
+ } else {
+
+ //
+ // Create a buffer pool, if there are no buffers in
+ // the current one, set the exception flags (or dos appendage
+ // routines), if the operation was success
+ //
+
+ status = CreateBufferPool(
+ (DWORD)GET_POOL_INDEX(AdapterNumber, 0),
+ (DOS_ADDRESS)READ_DWORD(&pParms->DirModifyOpenParms.dpPoolAddress),
+ READ_WORD(&pParms->DirModifyOpenParms.cPoolBlocks),
+ READ_WORD(&pParms->DirModifyOpenParms.usBufferSize)
+ );
+
+ if (status == LLC_STATUS_SUCCESS) {
+
+ //
+ // SaveExceptions uses RtlCopyMemory, so its okay to pass in a possibly
+ // unaligned pointer to the exception handlers
+ //
+
+ SaveExceptions(AdapterNumber,
+ (LPDWORD)&pParms->DirModifyOpenParms.dpAdapterCheckExit
+ );
+
+ //
+ // set the exception handlers as the exception flags in the
+ // DLC driver
+ //
+
+ status = SetExceptionFlags(
+ AdapterNumber,
+ READ_DWORD(&pParms->DirModifyOpenParms.dpAdapterCheckExit),
+ READ_DWORD(&pParms->DirModifyOpenParms.dpNetworkStatusExit),
+ READ_DWORD(&pParms->DirModifyOpenParms.dpPcErrorExit),
+ 0
+ );
+ }
+
+ //
+ // mark this adapter as waiting for a DIR.RESTORE.OPEN.PARMS before
+ // we can process the next DIR.MODIFY.OPEN.PARMS
+ //
+
+ if (status == LLC_STATUS_SUCCESS) {
+ Adapters[AdapterNumber].WaitingRestore = TRUE;
+ }
+ }
+ break;
+
+ case LLC_DIR_RESTORE_OPEN_PARMS:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DIR_RESTORE_OPEN_PARMS\n");
+ }
+
+ //
+ // if a DIR.MODIFY.OPEN.PARMS has not been successfully processed for
+ // this adapter then return an error
+ //
+
+ if (!Adapters[AdapterNumber].WaitingRestore) {
+ status = LLC_STATUS_INVALID_OPTION;
+ } else {
+
+ //
+ // Delete the current buffer pool and restore the previous exception
+ // handlers
+ //
+
+ DeleteBufferPool(GET_POOL_INDEX(AdapterNumber, 0));
+ pParms = (PLLC_DOS_PARMS)RestoreExceptions(AdapterNumber);
+ status = SetExceptionFlags(
+ AdapterNumber,
+ READ_DWORD(&pParms->DirSetExceptionFlags.ulAdapterCheckFlag),
+ READ_DWORD(&pParms->DirSetExceptionFlags.ulNetworkStatusFlag),
+ READ_DWORD(&pParms->DirSetExceptionFlags.ulPcErrorFlag),
+ 0
+ );
+
+ //
+ // if the restore succeeded, mark this adapter as able to accept
+ // the next DIR.MODIFY.OPEN.PARMS request
+ //
+
+ if (status == LLC_STATUS_SUCCESS) {
+ Adapters[AdapterNumber].WaitingRestore = FALSE;
+ }
+ }
+ break;
+
+ case LLC_DIR_SET_USER_APPENDAGE:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DIR_SET_USER_APPENDAGE\n");
+ }
+
+ if (pParms == NULL) {
+ pParms = (PLLC_DOS_PARMS)RestoreExceptions(AdapterNumber);
+ } else {
+ SaveExceptions(AdapterNumber, (LPDWORD)&pParms->DirSetUserAppendage);
+ }
+ status = SetExceptionFlags(
+ AdapterNumber,
+ READ_DWORD(&pParms->DirSetUserAppendage.dpAdapterCheckExit),
+ READ_DWORD(&pParms->DirSetUserAppendage.dpNetworkStatusExit),
+ READ_DWORD(&pParms->DirSetUserAppendage.dpPcErrorExit),
+ 0
+ );
+ break;
+
+#if DBG
+ default:
+ DPUT1("ProcessImmediateCommand: Error: Command is NOT immediate (%x)\n", Command);
+ DbgBreakPoint();
+#endif
+
+ }
+
+ return status;
+}
+
+
+LLC_STATUS
+MapDosCommandsToNt(
+ IN PLLC_CCB pDosCcb,
+ IN DOS_ADDRESS dpOriginalCcbAddress,
+ OUT LLC_DOS_CCB UNALIGNED * pOutputCcb
+ )
+
+/*++
+
+Routine Description:
+
+ This function handles DOS CCBs which must be submitted to the DLC driver.
+ The CCBs may complete synchronously - i.e. the DLC driver returns a
+ success or failure indication, or they complete asyncronously - i.e. the
+ DLC driver returns a pending status.
+
+ This function processes CCBs which may have parameter tables. The parameter
+ tables will have been mapped to 32-bit memory unless they are already aligned
+ on a DWORD boundary
+
+Architecture
+------------
+
+ There is a major difference in the adapter initialization between DOS and
+ NT (or OS/2) DLC applications. A DOS DLC application could assume that
+ the adapter is always open (opened by NetBIOS). It might directly check
+ the type of adapter with DIR.STATUS command, open SAP stations and setup
+ a link session to a host. Usually a DOS app uses DIR.INTERRUPT to check if
+ the adapter is already initialized. There are also commands to redefine
+ new parameters for the direct interface and restore the old ones when the
+ application completes. Only one application may be simultaneously using
+ the direct station or a SAP.
+
+ In Windows/NT each DLC application is a process having its own separate
+ virtual image of the DLC interface. They must first make a logical open for
+ the adapter to access DLC services and close the adapter when they are
+ terminating. Process exit automatically closes any open DLC objects.
+
+ A Windows/NT MVDM process does not allocate any DLC resources until it
+ issues the first DLC command, which opens the adapter and initializes its
+ buffer pool.
+
+
+DLC Commands Different In Windows/NT And DOS
+--------------------------------------------
+
+ BUFFER.FREE, BUFFER.GET
+ - separate buffer pools ...
+
+ DIR.DEFINE.MIF.ENVIRONMENT
+ - not supported, a special api for
+ IBM Netbios running on DLC.
+
+ DIR.INITIALIZE
+ - We will always return OK status from DIR.INITIALIZE: the app should not
+ call this very often. We don't need to care about the exception handlers
+ set in DIR.INITIALIZE, because they are never used. DOS DLC and OS/2 DLC
+ states can be mapped thus:
+
+ DOS DLC OS/2 DLC
+ ----------------------------
+ uninitialized Closed
+ Initialized Closed
+ Open Open
+
+ DIR.OPEN.ADAPTER defines new exception handlers, thus the values
+ set by DIR.INITIALIZE are valid only when the adapter is closed.
+ Therefore, nothing untoward can happen if we just ignore them
+
+ DIR.INTERRUPT
+ - This command opens the adapter, if it has not yet been opened
+ and returns the successful status.
+
+ DIR.MODIFY.OPEN.PARMS
+ - Defines buffers pool for the direct station, if it was not defined
+ in DIR.OPEN.ADAPTER, and defines DLC exception handlers.
+
+ DIR.OPEN.ADAPTER
+ - Can be executed only immediately after DIR.CLOSE.ADAPTER
+ and DIR.INITIALIZE. We must support the special DOS Direct Open Parms.
+
+ DIR.OPEN.DIRECT, DIR.CLOSE.DIRECT
+ - Not supported for DOS
+
+ DIR.SET.USER.APPENDAGE == DIR.SET.EXCEPTION.FLAGS (- system action flag)
+ - This is just one of those many functions to set the exception handlers
+ for DOS DLC (you may set them when adapter is opened, you may set
+ them when adapter is closed, you may restore the old values and
+ set the new values if the buffer pool was uninitialized or if they
+ were restored ... (I become crazy)
+
+ DIR.STATUS
+ - (We must fill MicroCodeLevel with a sensible string and set
+ AdapterConfigAddress to point a some constant code in DOS
+ DLC handler) Not yet implemented!!!
+ - DOS DLC stub code should hook the timer tick interrupt and
+ update the timer counter.
+ - We must also set the correct adapter data rate in AdapterConfig
+ (this should be done by the DLC driver!).
+ - We must convert the Nt (and OS/2) adapter type to a DOS type
+ (ethernet have a different value in IBM DOS and OS/2 DLC
+ implementations)
+
+ PDT.TRACE.ON, PDT.TRACE.OFF
+ - Not Supported
+
+ RECEIVE.MODIFY
+ - This function is not supported in the first implementation,
+ Richard may have to do this later, if a DOS DLC application
+ uses the function.
+
+ RECEIVE
+ - DOS uses shorter RECEIVE parameter table, than NT (or OS/2).
+ Thus we cannot directly use DOS CCBs. We also need the pointer
+ of the original receive CCB and the DOS receive appendage is called.
+ On the other hand, the only original CCB can be linked to the
+ other CCBs (using the original DOS pointer).
+ Solution:
+ The Input CCB and its parameter table are always allocated from the
+ stack. Output CCB is the original DOS CCB mapped to 32-bit address space.
+ The receive flag in the input CCB parameter table is the output CCB
+ pointer. The actual receive data appendage can be read from
+ the output CCB. DOS DLC driver completes and links the receive CCB
+ correctly, because we use the original receive CCB as an output buffer
+ and DOS CCB address.
+ This method would not work if we had to receive any parameters
+ to the parameter table (actually we could get it to work by
+ calling directly DLC driver with a correct parameter table address
+ in the CCB structure of the input parameter block. The actual
+ input parameters would be modified (receive data appendage)).
+
+
+Adapter Exception Handlers
+--------------------------
+
+ The exception handler setting and resetting is very confusing in DOS DLC,
+ but the main idea seems to be this: a temporary DOS DLC application must
+ restore the exception handlers set by a TSR, because otherwise the next
+ network exception would call the random memory. On the other hand, a newer
+ TSR may always overwrite the previous exception handlers (because it never
+ restores the old values). We may assume, that any DOS DLC application
+ process resets its exception handlers and restores the original addresses.
+ Solution: we have two tables, both initially reset: any set operation
+ copies first table 1 to table 2 and saves the new values back to table 1.
+ A restore operation copies table 2 back to table 1 and sets its values to DLC.
+ We don't make any difference between set/reset by DIR.SET.USER.APPENDAGE or
+ doing the same operation with DIR.MODIFY.OPEN.PARMS and DIR.RESTORE.OPEN.PARMS.
+ We don't try to save the buffer pool definitions, because it is unnecessary.
+
+
+DLC Status
+----------
+
+ A DOS DLC status indication returns a pointer to a link specific DLC
+ status table. DOS DLC application may keep this pointer and use it
+ later for example to find the remote node address and SAP (not very likely).
+ On the other hand, the link may expect the status table to be stable
+ until another DOS task (eg. from timer tick) has responded to it.
+ If we used only one status table for all links, a new overwrite the old
+ status, because it has been handled by DOS.
+ For example, losing a local buffer busy indication would hang up the link
+ station permamently. Thus we cannot use just one link status table.
+ Allocating 20 bytes for each 2 * 255 link station would take 10 kB
+ DOS memory. Limiting the number of available link stations would
+ be also too painful to implement and manage by installation program.
+ Thus we will implement a compromise:
+ 1. We allocate 5 permanent DLC status tables for the first link stations
+ on both adapters.
+ 2. All other link stations (on both adapters) share the last
+ 5 status tables
+
+ => We need to allocate only 300 bytes DOS memory for the DLC status tables.
+ This will not work, if a DOS application assumes having permanent pointers
+ to status tables and uses more than 5 DLC sessions for an adapter or
+ if an application has again over 5 link stations on an adapter and
+ it gets very rapidily more than 5 DLC status indications (it may lose
+ the first DLC indications).
+
+ Actually this should work quite well, because DLC applications should
+ save (by copying) the DLC status in the DLC status appendage routine,
+ because a new DLC status indication for the same adapter could overwrite
+ the previous status.
+
+Arguments:
+
+ pDosCcb - aligned DOS DLC Command control block (NT CCB)
+ dpOriginalCcbAddress- the original
+ pOutputCcb - the original DLC Command control block
+
+Return Value:
+
+ LLC_STATUS
+
+--*/
+
+{
+ UCHAR adapter = pDosCcb->uchAdapterNumber;
+ UCHAR command = pDosCcb->uchDlcCommand;
+ DWORD InputBufferSize;
+ UCHAR FrameType;
+ DWORD cElement;
+ DWORD i;
+ PLLC_CCB pNewCcb;
+ LLC_STATUS Status;
+ NT_DLC_PARMS NtDlcParms;
+ LLC_DOS_PARMS UNALIGNED * pParms = (PLLC_DOS_PARMS)pDosCcb->u.pParameterTable;
+ PDOS_DLC_DIRECT_PARMS pDirectParms;
+ PLLC_PARMS pNtParms;
+
+ //
+ // adapterOpenParms and dlcParms are used to take the place of the DOS
+ // ADAPTER_PARMS and DLC_PARMS structures in DIR.OPEN.ADAPTER
+ //
+
+ LLC_ADAPTER_OPEN_PARMS adapterParms;
+ LLC_DLC_PARMS dlcParms;
+ DWORD groupAddress;
+ DWORD functionalAddress;
+
+ IF_DEBUG(DLC) {
+ DPUT("MapDosCommandsToNt\n");
+ }
+
+ //
+ // check that the command code in the CCB is a valid CCB1 command - this
+ // will error if its one of the disallowed OS/2 (CCB2) commands or an
+ // unsupported DOS (CCB1) command (eg PDT.TRACE.ON)
+ //
+
+ CHECK_CCB_COMMAND(pDosCcb);
+
+ //
+ // preset the CCB to PENDING
+ //
+
+ pOutputCcb->uchDlcStatus = (UCHAR)LLC_STATUS_PENDING;
+
+ //
+ // This large switch statement will convert individual DOS format parameter
+ // tables to NT format. Functions that can be handled entirely in VdmRedir
+ // return early, else we need to make a call into DlcApi (AcsLan or NtAcsLan)
+ //
+ // We must convert all 16:16 DOS pointers to flat 32-bit pointers.
+ // We must copy all changed data structures (that includes pointers)
+ // to stack, because we don't want to change them back, when
+ // the command completes. All transmit commands are changed to
+ // the new generic transmit.
+ //
+
+ switch (command) {
+ default:
+
+ IF_DEBUG(DLC) {
+ DPUT("*** Shouldn't be here - this command should be caught already ***\n");
+ }
+
+ return LLC_STATUS_INVALID_COMMAND;
+
+ //
+ // *** everything below here has been sanctioned ***
+ //
+
+ case LLC_DIR_CLOSE_ADAPTER:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DIR_CLOSE_ADAPTER\n");
+ }
+
+ //
+ // no parameter table
+ //
+
+ break;
+
+ case LLC_DIR_INITIALIZE:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DIR_INITIALIZE\n");
+ }
+
+ break;
+
+ case LLC_DIR_OPEN_ADAPTER:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DIR_OPEN_ADAPTER\n");
+ }
+
+ //
+ // copy the adapter parms and DLC parms to 32-bit memory. If there is no
+ // adapter parms or direct parms pointer then return an error
+ //
+
+ if (!(pParms->DirOpenAdapter.pAdapterParms && pParms->DirOpenAdapter.pExtendedParms)) {
+ return LLC_STATUS_PARAMETER_MISSING;
+ }
+
+ //
+ // copy the DOS ADAPTER_PARMS table to an NT ADAPTER_PARMS table. The
+ // NT table is larger, so zero the uncopied part
+ //
+
+ RtlCopyMemory(&adapterParms,
+ DOS_PTR_TO_FLAT(pParms->DirOpenAdapter.pAdapterParms),
+ sizeof(ADAPTER_PARMS)
+ );
+ RtlZeroMemory(&adapterParms.usReserved3[0],
+ sizeof(LLC_ADAPTER_OPEN_PARMS) - (DWORD)&((PLLC_ADAPTER_OPEN_PARMS)0)->usReserved3
+ );
+ pParms->DirOpenAdapter.pAdapterParms = &adapterParms;
+
+ //
+ // make a note of the group and functional addresses. We have to set
+ // these later if they're not 0x00000000. We have to save these, because
+ // the addresses in the table will get blasted when DIR.OPEN.ADAPTER
+ // (in DLCAPI.DLL/DLC.SYS) writes the current (0) values to the table
+ //
+
+ groupAddress = *(UNALIGNED DWORD *)adapterParms.auchGroupAddress;
+ functionalAddress = *(UNALIGNED DWORD *)adapterParms.auchFunctionalAddress;
+
+ //
+ // the DLC_PARMS table doesn't HAVE to be supplied - if we just want to
+ // use the direct station, we don't need these parameters. However, if
+ // they were supplied, copy them to 32-bit memory. The tables are the
+ // same size in DOS and NT
+ //
+
+ if (pParms->DirOpenAdapter.pDlcParms) {
+ RtlCopyMemory(&dlcParms,
+ DOS_PTR_TO_FLAT(pParms->DirOpenAdapter.pDlcParms),
+ sizeof(dlcParms)
+ );
+ pParms->DirOpenAdapter.pDlcParms = &dlcParms;
+ }
+
+ //
+ // set the default values for ADAPTER_PARMS. Not sure about these
+ //
+ // usReserved1 == NUMBER_RCV_BUFFERS
+ // usReserved2 == RCV_BUFFER_LEN
+ // usMaxFrameSize == DHB_BUFFER_LENGTH
+ // usReserved3[0] == DATA_HOLD_BUFFERS
+ //
+
+ if (pParms->DirOpenAdapter.pAdapterParms->usReserved1 < 2) {
+ pParms->DirOpenAdapter.pAdapterParms->usReserved1 = DD_NUMBER_RCV_BUFFERS;
+ }
+ if (pParms->DirOpenAdapter.pAdapterParms->usReserved2 == 0) {
+ pParms->DirOpenAdapter.pAdapterParms->usReserved2 = DD_RCV_BUFFER_LENGTH;
+ }
+ if (pParms->DirOpenAdapter.pAdapterParms->usMaxFrameSize == 0) {
+ pParms->DirOpenAdapter.pAdapterParms->usReserved1 = DD_DHB_BUFFER_LENGTH;
+ }
+ if (*(PBYTE)&pParms->DirOpenAdapter.pAdapterParms->usReserved3 == 0) {
+ pParms->DirOpenAdapter.pAdapterParms->usReserved1 = DD_DATA_HOLD_BUFFERS;
+ }
+
+ //
+ // if we have DLC_PARMS then set the defaults, else reset the flag
+ //
+
+ if (pParms->DirOpenAdapter.pDlcParms) {
+ if (pParms->DirOpenAdapter.pDlcParms->uchDlcMaxSaps == 0) {
+ pParms->DirOpenAdapter.pDlcParms->uchDlcMaxSaps = DD_DLC_MAX_SAP;
+ }
+ if (pParms->DirOpenAdapter.pDlcParms->uchDlcMaxStations == 0) {
+ pParms->DirOpenAdapter.pDlcParms->uchDlcMaxStations = DD_DLC_MAX_STATIONS;
+ }
+ if (pParms->DirOpenAdapter.pDlcParms->uchDlcMaxGroupSaps == 0) {
+ pParms->DirOpenAdapter.pDlcParms->uchDlcMaxGroupSaps = DD_DLC_MAX_GSAP;
+ }
+ if (pParms->DirOpenAdapter.pDlcParms->uchT1_TickOne == 0) {
+ pParms->DirOpenAdapter.pDlcParms->uchT1_TickOne = DD_DLC_T1_TICK_ONE;
+ }
+ if (pParms->DirOpenAdapter.pDlcParms->uchT2_TickOne == 0) {
+ pParms->DirOpenAdapter.pDlcParms->uchT2_TickOne = DD_DLC_T2_TICK_ONE;
+ }
+ if (pParms->DirOpenAdapter.pDlcParms->uchTi_TickOne == 0) {
+ pParms->DirOpenAdapter.pDlcParms->uchTi_TickOne = DD_DLC_Ti_TICK_ONE;
+ }
+ if (pParms->DirOpenAdapter.pDlcParms->uchT1_TickTwo == 0) {
+ pParms->DirOpenAdapter.pDlcParms->uchT1_TickTwo = DD_DLC_T1_TICK_TWO;
+ }
+ if (pParms->DirOpenAdapter.pDlcParms->uchT2_TickTwo == 0) {
+ pParms->DirOpenAdapter.pDlcParms->uchT2_TickTwo = DD_DLC_T2_TICK_TWO;
+ }
+ if (pParms->DirOpenAdapter.pDlcParms->uchTi_TickTwo == 0) {
+ pParms->DirOpenAdapter.pDlcParms->uchTi_TickTwo = DD_DLC_Ti_TICK_TWO;
+ }
+ Adapters[adapter].DlcSpecified = TRUE;
+ } else {
+ Adapters[adapter].DlcSpecified = FALSE;
+ }
+
+ //
+ // replace DIRECT_PARMS with the EXTENDED_ADAPTER_PARMS
+ //
+
+ pDirectParms = (PDOS_DLC_DIRECT_PARMS)
+ READ_FAR_POINTER(&pParms->DirOpenAdapter.pExtendedParms);
+ pParms->DirOpenAdapter.pExtendedParms = &DefaultExtendedParms;
+ break;
+
+ case LLC_DIR_READ_LOG:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DIR_READ_LOG\n");
+ }
+
+ pParms->DirReadLog.pLogBuffer = DOS_PTR_TO_FLAT(pParms->DirReadLog.pLogBuffer);
+ break;
+
+ case LLC_DIR_SET_FUNCTIONAL_ADDRESS:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DIR_SET_FUNCTIONAL_ADDRESS\n");
+ }
+
+ //
+ // no parameter table
+ //
+
+ break;
+
+ case LLC_DIR_SET_GROUP_ADDRESS:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DIR_SET_GROUP_ADDRESS\n");
+ }
+
+ //
+ // no parameter table
+ //
+
+ break;
+
+ case LLC_DIR_STATUS:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DIR_STATUS\n");
+ }
+
+ break;
+
+ case LLC_DIR_TIMER_CANCEL:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DIR_TIMER_CANCEL\n");
+ }
+
+ //
+ // no parameter table
+ //
+
+ break;
+
+ case LLC_DIR_TIMER_CANCEL_GROUP:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DIR_TIMER_CANCEL_GROUP\n");
+ }
+
+ //
+ // no parameter table
+ //
+
+ break;
+
+ case LLC_DIR_TIMER_SET:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DIR_TIMER_SET\n");
+ }
+
+ //
+ // no parameter table
+ //
+
+ //
+ // Debug code is too slow - commands keep timing out. Multiply the
+ // timer value by 2 to give us a chance!
+ //
+
+ IF_DEBUG(DOUBLE_TICKS) {
+ pDosCcb->u.dir.usParameter0 *= 2;
+ }
+
+ break;
+
+ case LLC_DLC_CLOSE_SAP:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DLC_CLOSE_SAP\n");
+ }
+
+ //
+ // no parameter table
+ //
+
+ break;
+
+ case LLC_DLC_CLOSE_STATION:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DLC_CLOSE_STATION\n");
+ }
+
+ //
+ // no parameter table
+ //
+
+ break;
+
+ case LLC_DLC_CONNECT_STATION:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DLC_CONNECT_STATION\n");
+ }
+
+ pParms->DlcConnectStation.pRoutingInfo = DOS_PTR_TO_FLAT(pParms->DlcConnectStation.pRoutingInfo);
+ break;
+
+ case LLC_DLC_FLOW_CONTROL:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DLC_FLOW_CONTROL\n");
+ }
+
+ //
+ // if we are resetting the local-busy(buffer) state then we clear the
+ // emulated state. When all deferred I-Frames are indicated to the app
+ // the real local-busy(buffer) state will be reset in the driver
+ //
+ // If we don't think the indicated link station is in emulated local
+ // busy(buffer) state then let the driver handle the request. If we
+ // reset the emulated state then return success
+ //
+
+ if ((LOBYTE(pDosCcb->u.dlc.usParameter) & LLC_RESET_LOCAL_BUSY_BUFFER) == LLC_RESET_LOCAL_BUSY_BUFFER) {
+ if (ResetEmulatedLocalBusyState(adapter, pDosCcb->u.dlc.usStationId, LLC_DLC_FLOW_CONTROL)) {
+ return LLC_STATUS_SUCCESS;
+ } else {
+
+ IF_DEBUG(DLC) {
+ DPUT2("MapDosCommandsToNt: ERROR: Adapter %d StationId %04x not in local-busy(buffer) state\n",
+ adapter,
+ pDosCcb->u.dlc.usStationId
+ );
+ }
+
+ return LLC_STATUS_SUCCESS;
+ }
+ }
+
+ //
+ // let AcsLan/driver handle any other type of flow control request
+ //
+
+ break;
+
+ case LLC_DLC_MODIFY:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DLC_MODIFY\n");
+ }
+
+ pParms->DlcModify.pGroupList = DOS_PTR_TO_FLAT(pParms->DlcModify.pGroupList);
+ break;
+
+ case LLC_DLC_OPEN_SAP:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DLC_OPEN_SAP\n");
+ }
+
+ //
+ // convert segmented group list pointer to flat-32
+ //
+
+ pParms->DlcOpenSap.pGroupList = DOS_PTR_TO_FLAT(pParms->DlcOpenSap.pGroupList);
+
+ //
+ // Initialize the DOS DLC buffer pool for the SAP station. If this fails
+ // return error immediately else call the NT driver to create the SAP
+ // proper. If that fails, then the buffer pool created here will be
+ // destroyed
+ //
+
+ Status = CreateBufferPool(
+ POOL_INDEX_FROM_SAP(pParms->DosDlcOpenSap.uchSapValue, adapter),
+ pParms->DosDlcOpenSap.dpPoolAddress,
+ pParms->DosDlcOpenSap.cPoolBlocks,
+ pParms->DosDlcOpenSap.usBufferSize
+ );
+ if (Status != LLC_STATUS_SUCCESS) {
+
+ IF_DEBUG(DLC) {
+ DPUT1("MapDosCommandsToNt: Couldn't create buffer pool for DLC.OPEN.SAP (%x)\n", Status);
+ }
+
+ return Status;
+ }
+
+ //
+ // trim the timer parameters to the range expected by the DLC driver
+ //
+
+ if (pParms->DlcOpenSap.uchT1 > 10) {
+ pParms->DlcOpenSap.uchT1 = 10;
+ }
+ if (pParms->DlcOpenSap.uchT2 > 10) {
+ pParms->DlcOpenSap.uchT2 = 10;
+ }
+ if (pParms->DlcOpenSap.uchTi > 10) {
+ pParms->DlcOpenSap.uchTi = 10;
+ }
+ break;
+
+ case LLC_DLC_OPEN_STATION:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DLC_OPEN_STATION\n");
+ }
+
+ pParms->DlcOpenStation.pRemoteNodeAddress = DOS_PTR_TO_FLAT(pParms->DlcOpenStation.pRemoteNodeAddress);
+ break;
+
+ case LLC_DLC_REALLOCATE_STATIONS:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DLC_REALLOCATE_STATIONS\n");
+ }
+
+ break;
+
+ case LLC_DLC_RESET:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DLC_RESET\n");
+ }
+
+ //
+ // no parameter table
+ //
+
+ break;
+
+ case LLC_DLC_STATISTICS:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_DLC_STATISTICS\n");
+ }
+
+ pParms->DlcStatistics.pLogBuf = DOS_PTR_TO_FLAT(pParms->DlcStatistics.pLogBuf);
+ break;
+
+
+ //
+ // RECEIVE processing
+ //
+
+ case LLC_RECEIVE:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_RECEIVE\n");
+ }
+
+ //
+ // we have to replace the DOS RECEIVE with an NT RECEIVE for 2 reasons:
+ // (i) NT assumes an NT RECEIVE parameter table which is longer than
+ // the DOS RECEIVE parameter table: if we send the DOS pointers through
+ // NT may write RECEIVE parameter information where we don't want it;
+ // (ii) NT will complete the RECEIVE with NT buffers which we need to
+ // convert to DOS buffers anyway in the event completion processing
+ //
+ // NOTE: we no longer chain receive frames on the SAP since this doesn't
+ // really improve performance because we generate the same number of VDM
+ // interrupts if we don't chain frames, and it just serves to complicate
+ // completion event processing
+ //
+
+ pNewCcb = (PLLC_CCB)LocalAlloc(LMEM_FIXED,
+ sizeof(LLC_CCB)
+ + sizeof(LLC_DOS_RECEIVE_PARMS_EX)
+ );
+ if (pNewCcb == NULL) {
+ return LLC_STATUS_NO_MEMORY;
+ } else {
+
+ IF_DEBUG(DLC) {
+ DPUT1("VrDlc5cHandler: allocated Extended RECEIVE+parms @ %08x\n", pNewCcb);
+ }
+
+ }
+ RtlCopyMemory(pNewCcb, pDosCcb, sizeof(LLC_DOS_CCB));
+ RtlCopyMemory((PVOID)(pNewCcb + 1), (PVOID)pParms, sizeof(LLC_DOS_RECEIVE_PARMS));
+ pNewCcb->hCompletionEvent = NULL;
+ pNewCcb->uchReserved2 = 0;
+ pNewCcb->uchReadFlag = 0;
+ pNewCcb->usReserved3 = 0;
+ pDosCcb = pNewCcb;
+ pNtParms = (PLLC_PARMS)(pNewCcb + 1);
+ pDosCcb->u.pParameterTable = pNtParms;
+ ((PLLC_DOS_RECEIVE_PARMS_EX)pNtParms)->dpOriginalCcbAddress = dpOriginalCcbAddress;
+ ((PLLC_DOS_RECEIVE_PARMS_EX)pNtParms)->dpCompletionFlag = 0;
+ dpOriginalCcbAddress = (DOS_ADDRESS)pOutputCcb = (DOS_ADDRESS)pDosCcb;
+
+ //
+ // point the notification flag at the extended RECEIVE CCB. This is how
+ // we get back to the extended RECEIVE CCB when a READ completes with a
+ // receive event. From this CCB pointer, we can find our way to the
+ // extended parameter table and from there the original DOS CCB address
+ // and from there the original DOS RECEIVE parameter table
+ //
+
+ pNtParms->Receive.ulReceiveFlag = (DWORD)dpOriginalCcbAddress;
+
+ //
+ // uchRcvReadOption of 0x00 means DO NOT chain received frames. DOS DLC
+ // cannot handle more than 1 frame at a time
+ //
+
+ pNtParms->Receive.uchRcvReadOption = 0;
+
+ //
+ // indicate, using LLC_DOS_SPECIAL_COMMAND as the completion flags, that
+ // this RECEIVE CCB & parameter table were allocated on behalf of the
+ // VDM, in this emulator, and should be freed when the command completes.
+ // This also indicates that the parameter table is the extended receive
+ // parameter table and as such contains the address of the original DOS
+ // CCB which we must complete with the same information which completes
+ // the NT RECEIVE we are about to submit
+ //
+
+ pNewCcb->ulCompletionFlag = LLC_DOS_SPECIAL_COMMAND;
+
+#if DBG
+
+ //
+ // clear out the first-buffer field to stop the debug code displaying
+ // a ton of garbage if the field is left uninitialized
+ //
+
+ WRITE_DWORD(&pOutputCcb->u.pParms->DosReceive.pFirstBuffer, 0);
+ pNtParms->Receive.pFirstBuffer = NULL;
+#endif
+
+ break;
+
+ case LLC_RECEIVE_CANCEL:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_RECEIVE_CANCEL\n");
+ }
+
+ break;
+
+ case LLC_RECEIVE_MODIFY:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_RECEIVE_MODIFY\n");
+ }
+
+ break;
+
+
+ //
+ // TRANSMIT processing. All transmit commands (7 flavours) are collapsed
+ // into the new TRANSMIT command
+ //
+
+ case LLC_TRANSMIT_DIR_FRAME:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_TRANSMIT_DIR_FRAME\n");
+ }
+
+ FrameType = LLC_DIRECT_TRANSMIT;
+ goto TransmitHandling;
+
+ case LLC_TRANSMIT_I_FRAME:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_TRANSMIT_I_FRAME\n");
+ }
+
+ FrameType = LLC_I_FRAME;
+ goto TransmitHandling;
+
+ case LLC_TRANSMIT_TEST_CMD:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_TRANSMIT_TEST_CMD\n");
+ }
+
+ FrameType = LLC_TEST_COMMAND_POLL;
+ goto TransmitHandling;
+
+ case LLC_TRANSMIT_UI_FRAME:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_TRANSMIT_UI_FRAME\n");
+ }
+
+ FrameType = LLC_UI_FRAME;
+ goto TransmitHandling;
+
+ case LLC_TRANSMIT_XID_CMD:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_TRANSMIT_XID_CMD\n");
+ }
+
+ FrameType = LLC_XID_COMMAND_POLL;
+ goto TransmitHandling;
+
+ case LLC_TRANSMIT_XID_RESP_FINAL:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_TRANSMIT_XID_RESP_FINAL\n");
+ }
+
+ FrameType = LLC_XID_RESPONSE_FINAL;
+ goto TransmitHandling;
+
+ case LLC_TRANSMIT_XID_RESP_NOT_FINAL:
+
+ IF_DEBUG(DLC) {
+ DPUT("LLC_TRANSMIT_XID_RESP_NOT_FINAL\n");
+ }
+
+ FrameType = LLC_XID_RESPONSE_NOT_FINAL;
+
+TransmitHandling:
+
+ //
+ // Copy the DOS CCB to the input buffer, save the original DOS address
+ // of the CCB and fix the parameter table address (to a flat address)
+ // Copy the link list headers to the descriptor array and build NT CCB
+ //
+
+ WRITE_DWORD(&pOutputCcb->pNext, dpOriginalCcbAddress);
+ RtlCopyMemory((PCHAR)&NtDlcParms.Async.Ccb, (PCHAR)pOutputCcb, sizeof(NT_DLC_CCB));
+ NtDlcParms.Async.Ccb.u.pParameterTable = DOS_PTR_TO_FLAT(NtDlcParms.Async.Ccb.u.pParameterTable);
+ NtDlcParms.Async.Parms.Transmit.StationId = pParms->Transmit.usStationId;
+ NtDlcParms.Async.Parms.Transmit.RemoteSap = pParms->Transmit.uchRemoteSap;
+ NtDlcParms.Async.Parms.Transmit.XmitReadOption = LLC_CHAIN_XMIT_COMMANDS_ON_SAP;
+ NtDlcParms.Async.Parms.Transmit.FrameType = FrameType;
+
+ cElement = 0;
+
+ if (pParms->Transmit.pXmitQueue1) {
+ Status = CopyDosBuffersToDescriptorArray(
+ NtDlcParms.Async.Parms.Transmit.XmitBuffer,
+ (PLLC_XMIT_BUFFER)pParms->Transmit.pXmitQueue1,
+ &cElement
+ );
+ if (Status != LLC_STATUS_SUCCESS) {
+ return Status;
+ }
+ }
+
+ if (pParms->Transmit.pXmitQueue2) {
+ Status = CopyDosBuffersToDescriptorArray(
+ NtDlcParms.Async.Parms.Transmit.XmitBuffer,
+ (PLLC_XMIT_BUFFER)pParms->Transmit.pXmitQueue2,
+ &cElement
+ );
+ if (Status != LLC_STATUS_SUCCESS) {
+ return Status;
+ }
+ }
+
+ if (pParms->Transmit.cbBuffer1) {
+ if (cElement == MAX_TRANSMIT_SEGMENTS) {
+ return LLC_STATUS_TRANSMIT_ERROR;
+ }
+
+ NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].pBuffer = DOS_PTR_TO_FLAT(pParms->Transmit.pBuffer1);
+ NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].cbBuffer = pParms->Transmit.cbBuffer1;
+ NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].boolFreeBuffer = FALSE;
+ NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].eSegmentType = LLC_NEXT_DATA_SEGMENT;
+ cElement++;
+ }
+
+ if (pParms->Transmit.cbBuffer2) {
+ if (cElement == MAX_TRANSMIT_SEGMENTS) {
+ return LLC_STATUS_TRANSMIT_ERROR;
+ }
+
+ NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].cbBuffer = pParms->Transmit.cbBuffer2;
+ NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].pBuffer = DOS_PTR_TO_FLAT(pParms->Transmit.pBuffer2);
+ NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].boolFreeBuffer = FALSE;
+ NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].eSegmentType = LLC_NEXT_DATA_SEGMENT;
+ cElement++;
+ }
+
+ NtDlcParms.Async.Parms.Transmit.XmitBuffer[0].eSegmentType = LLC_FIRST_DATA_SEGMENT;
+ NtDlcParms.Async.Parms.Transmit.XmitBufferCount = cElement;
+
+ InputBufferSize = sizeof(LLC_TRANSMIT_DESCRIPTOR) * cElement
+ + sizeof(NT_DLC_TRANSMIT_PARMS)
+ + sizeof(NT_DLC_CCB)
+ - sizeof(LLC_TRANSMIT_DESCRIPTOR);
+
+ //
+ // We don't need return FrameCopied status, when sending
+ // I-frames. We save an extra memory locking, when we use
+ // TRANSMIT2 with the I- frames.
+ //
+
+ return lpDlcCallDriver((DWORD)adapter,
+ //(FrameType == LLC_I_FRAME)
+ // ? IOCTL_DLC_TRANSMIT2
+ // : IOCTL_DLC_TRANSMIT,
+ IOCTL_DLC_TRANSMIT,
+ &NtDlcParms,
+ InputBufferSize,
+ pOutputCcb,
+ sizeof(NT_DLC_CCB_OUTPUT)
+ );
+ }
+
+ //
+ // Call the common DLC API entry point for DOS and Windows/Nt
+ //
+
+ Status = DlcCallWorker((PLLC_CCB)pDosCcb, // aligned input CCB pointer
+ (PLLC_CCB)dpOriginalCcbAddress,
+ (PLLC_CCB)pOutputCcb // possibly unaligned output CCB pointer
+ );
+
+ IF_DEBUG(DLC) {
+ DPUT2("MapDosCommandsToNt: NtAcsLan returns %#x (%d)\n", Status, Status);
+ }
+
+ switch (pDosCcb->uchDlcCommand) {
+ case LLC_DIR_CLOSE_ADAPTER:
+ case LLC_DIR_INITIALIZE:
+ OpenedAdapters--;
+
+ //
+ // NtAcsLan converts DIR.INITIALIZE to DIR.CLOSE.ADAPTER. The former
+ // completes "in the workstation", whereas the latter completes
+ // asynchronously. Interpret LLC_STATUS_PENDING as LLC_STATUS_SUCCESS
+ // in this case, otherwise we may not fully uninitialize the adapter
+ //
+
+ if (Status == LLC_STATUS_SUCCESS || Status == LLC_STATUS_PENDING) {
+
+ //
+ // We may free all virtual memory in NT buffer pool
+ //
+
+ Adapters[adapter].IsOpen = FALSE;
+ LocalFree(Adapters[adapter].BufferPool);
+
+ IF_DEBUG(DLC_ALLOC) {
+ DPUT1("FREE: freed block @ %x\n", Adapters[adapter].BufferPool);
+ }
+
+ //
+ // Delete all DOS buffer pools
+ //
+
+ for (i = 0; i <= 0xfe00; i += 0x0200) {
+ DeleteBufferPool(GET_POOL_INDEX(adapter, i));
+ }
+
+ //
+ // closing the adapter also closed the direct station
+ //
+
+ Adapters[adapter].DirectStationOpen = FALSE;
+
+ //
+ // clear the stored ADAPTER_PARMS and DLC_PARMS
+ //
+
+ RtlZeroMemory(&Adapters[adapter].AdapterParms, sizeof(ADAPTER_PARMS));
+ RtlZeroMemory(&Adapters[adapter].DlcParms, sizeof(DLC_PARMS));
+
+ if (pDosCcb->uchDlcCommand == LLC_DIR_INITIALIZE) {
+ Status = LLC_STATUS_SUCCESS;
+ }
+ }
+ break;
+
+ case LLC_DIR_OPEN_ADAPTER:
+ if (Status != LLC_STATUS_SUCCESS) {
+ break;
+ }
+
+ //
+ // Initialize the adapter support software
+ //
+
+ Status = InitializeAdapterSupport(adapter, pDirectParms);
+
+ //
+ // if we allocated the direct station buffer ok then perform the
+ // rest of the open request - open the direct station, add any
+ // group or functional addresses specified and set the ADAPTER_PARMS
+ // and DLC_PARMS default values in the DOS_ADAPTER structure
+ //
+
+ if (Status == LLC_STATUS_SUCCESS) {
+
+ //
+ // open the direct station
+ //
+
+ Status = OpenDirectStation(adapter);
+ if (Status == LLC_STATUS_SUCCESS) {
+
+ //
+ // add the group address
+ //
+
+ if (groupAddress) {
+ Status = LlcCommand(adapter,
+ LLC_DIR_SET_GROUP_ADDRESS,
+ groupAddress
+ );
+ } else IF_DEBUG(DLC) {
+ DPUT1("Error: couldn't set group address: %02x\n", Status);
+ }
+
+ if (Status == LLC_STATUS_SUCCESS) {
+
+ //
+ // add the functional address
+ //
+
+ if (functionalAddress) {
+ Status = LlcCommand(adapter,
+ LLC_DIR_SET_FUNCTIONAL_ADDRESS,
+ functionalAddress
+ );
+ }
+ } else IF_DEBUG(DLC) {
+ DPUT1("Error: couldn't set functional address: %02x\n", Status);
+ }
+
+ } else IF_DEBUG(DLC) {
+ DPUT1("Error: could open Direct Station: %02x\n", Status);
+ }
+ }
+
+ //
+ // copy the returned default information to the adapter structure if
+ // we successfully managed to open the direct station and add the
+ // group and functional addresses (if specified)
+ //
+
+ if (Status == LLC_STATUS_SUCCESS) {
+ RtlCopyMemory(&Adapters[adapter].AdapterParms,
+ pParms->DirOpenAdapter.pAdapterParms,
+ sizeof(ADAPTER_PARMS)
+ );
+ if (pParms->DirOpenAdapter.pDlcParms) {
+ RtlCopyMemory(&Adapters[adapter].DlcParms,
+ pParms->DirOpenAdapter.pDlcParms,
+ sizeof(DLC_PARMS)
+ );
+ }
+ } else {
+
+ //
+ // yoiks! something failed - close the direct station (if its
+ // open, close the adapter and fail the request)
+ //
+
+ if (Adapters[adapter].DirectStationOpen) {
+ CloseDirectStation(adapter);
+ }
+ CloseAdapter(adapter);
+ }
+ break;
+
+ case LLC_DLC_CLOSE_SAP:
+ case LLC_DLC_CLOSE_STATION:
+
+ //
+ // Delete the buffer pools of the closed or closing station.
+ // It does not matter, if the close operation is still pending,
+ // because the pending operations should always succeed
+ //
+
+ if (Status == LLC_STATUS_SUCCESS || Status == LLC_STATUS_PENDING) {
+
+ DeleteBufferPool(GET_POOL_INDEX(adapter, pDosCcb->u.dlc.usStationId));
+
+ //
+ // DLC.SYS returns a pointer to the NT RECEIVE CCB for this SAP.
+ // Change the pointer to the DOS RECEIVE CCB
+ //
+
+ if (Status == LLC_STATUS_SUCCESS || !pDosCcb->ulCompletionFlag) {
+
+ PLLC_CCB pNtReceive;
+ PLLC_DOS_RECEIVE_PARMS_EX pNtReceiveParms;
+
+ pNtReceive = (PLLC_CCB)READ_DWORD(&pOutputCcb->pNext);
+ if (pNtReceive) {
+ pNtReceiveParms = (PLLC_DOS_RECEIVE_PARMS_EX)(pNtReceive->u.pParameterTable);
+ WRITE_FAR_POINTER(&pOutputCcb->pNext, pNtReceiveParms->dpOriginalCcbAddress);
+
+ //
+ // free the NT RECEIVE CCB we allocated (see LLC_RECEIVE above)
+ //
+
+ ASSERT(pNtReceive->ulCompletionFlag == LLC_DOS_SPECIAL_COMMAND);
+ LocalFree((HLOCAL)pNtReceive);
+
+ IF_DEBUG(DLC) {
+ DPUT1("VrDlc5cHandler: freed Extended RECEIVE+parms @ %08x\n", pNtReceive);
+ }
+
+ }
+ }
+ }
+ break;
+
+ case LLC_DLC_OPEN_SAP:
+
+ //
+ // delete the buffer pool, if the open SAP command failed
+ //
+
+ if (Status != LLC_STATUS_SUCCESS) {
+ DeleteBufferPool(GET_POOL_INDEX(adapter, pParms->DlcOpenSap.usStationId));
+ } else {
+
+ //
+ // record the DLC status change appendage for this SAP
+ //
+
+ Adapters[ adapter ].DlcStatusChangeAppendage
+
+ [ SAP_ID(pParms->DlcOpenSap.usStationId) ]
+
+ = pParms->DlcOpenSap.DlcStatusFlags;
+
+ //
+ // and user value
+ //
+
+ Adapters[ adapter ].UserStatusValue
+
+ [ SAP_ID(pParms->DlcOpenSap.usStationId) ]
+
+ = pParms->DlcOpenSap.usUserStatValue;
+ }
+ break;
+
+ case LLC_DLC_RESET:
+
+ //
+ // Delete the reset sap buffer pool,
+ // or all sap buffer pools. We don't need to care about
+ // the possible error codes, because this can fail only
+ // if the given sap station does not exist any more =>
+ // it does not matter, if we reset it again.
+ //
+
+ if (pDosCcb->u.dlc.usStationId != 0) {
+ DeleteBufferPool(GET_POOL_INDEX(adapter, pDosCcb->u.dlc.usStationId));
+ } else {
+
+ int sapNumber;
+
+ //
+ // Close all SAP stations (0x0200 - 0xfe00). SAP number goes up in
+ // increments of 2 since bit 0 is ignored (ie SAP 2 == SAP 3 etc)
+ //
+
+ for (sapNumber = 2; sapNumber <= 0xfe; sapNumber += 2) {
+ DeleteBufferPool(POOL_INDEX_FROM_SAP(sapNumber, adapter));
+ }
+ }
+ break;
+ }
+ return Status;
+}
+
+
+VOID
+CompleteCcbProcessing(
+ IN LLC_STATUS Status,
+ IN LLC_DOS_CCB UNALIGNED * pCcb,
+ IN PLLC_PARMS pNtParms
+ )
+
+/*++
+
+Routine Description:
+
+ Performs any CCB completion processing. Processing can be called either
+ when the CCB completes synchronously, or asynchronously. Processing is
+ typically to fill in parts of the DOS CCB or parameter table
+
+Arguments:
+
+ Status - of the request
+ pCcb - pointer to DOS CCB to complete (flat 32-bit pointer to DOS memory)
+ pNtParms- pointer to NT parameter table
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LLC_DOS_PARMS UNALIGNED * pDosParms = READ_FAR_POINTER(&pCcb->u.pParms);
+ BYTE adapter = pCcb->uchAdapterNumber;
+
+ IF_DEBUG(DLC) {
+ DPUT("CompleteCcbProcessing\n");
+ }
+
+ switch (pCcb->uchDlcCommand) {
+ case LLC_DIR_OPEN_ADAPTER:
+
+ //
+ // this command is unique in that it has a parameter table which points
+ // to up to 4 other parameter tables. The following values are output:
+ //
+ // ADAPTER_PARMS
+ // OPEN_ERROR_CODE
+ // NODE_ADDRESS
+ //
+ // DIRECT_PARMS
+ // WORK_LEN_ACT
+ //
+ // DLC_PARMS
+ // None for CCB1
+ //
+ // NCB_PARMS
+ // Not accessed
+ //
+
+ //
+ // we only copy the info if the command succeeded (we may have garbage
+ // table pointers otherwise). It is also OK to copy the information if
+ // the adapter is already open and the caller requested that we pass
+ // the default information back
+ //
+
+ if (Status == LLC_STATUS_SUCCESS || Status == LLC_STATUS_ADAPTER_OPEN) {
+
+ PLLC_DOS_DIR_OPEN_ADAPTER_PARMS pOpenAdapterParms = (PLLC_DOS_DIR_OPEN_ADAPTER_PARMS)pDosParms;
+ PADAPTER_PARMS pAdapterParms = READ_FAR_POINTER(&pOpenAdapterParms->pAdapterParms);
+ PDIRECT_PARMS pDirectParms = READ_FAR_POINTER(&pOpenAdapterParms->pDirectParms);
+ PDLC_PARMS pDlcParms = READ_FAR_POINTER(&pOpenAdapterParms->pDlcParms);
+
+ //
+ // if we got an error and the caller didn't request the original
+ // open parameters, then skip out
+ //
+
+ if (Status == LLC_STATUS_ADAPTER_OPEN && !(pAdapterParms->OpenOptions & 0x200)) {
+ break;
+ }
+
+ WRITE_WORD(&pAdapterParms->OpenErrorCode, pNtParms->DirOpenAdapter.pAdapterParms->usOpenErrorCode);
+ RtlCopyMemory(&pAdapterParms->NodeAddress,
+ pNtParms->DirOpenAdapter.pAdapterParms->auchNodeAddress,
+ sizeof(pAdapterParms->NodeAddress)
+ );
+
+ //
+ // direct parms are not returned from NT DLC, so we just copy the
+ // requested work area size to the actual
+ //
+
+ WRITE_WORD(&pDirectParms->AdapterWorkAreaActual,
+ READ_WORD(&pDirectParms->AdapterWorkAreaRequested)
+ );
+
+ //
+ // copy the entire DLC_PARMS structure from the DOS_ADAPTER structure
+ //
+
+ if (pDlcParms) {
+ RtlCopyMemory(pDlcParms, &Adapters[adapter].DlcParms, sizeof(*pDlcParms));
+ }
+ }
+ break;
+
+ case LLC_DIR_STATUS:
+
+ //
+ // copy the common areas from the 32-bit parameter table to 16-bit table
+ // This copies up to the adapter parameters address
+ //
+
+ RtlCopyMemory(pDosParms, pNtParms, (DWORD)&((PDOS_DIR_STATUS_PARMS)0)->dpAdapterParmsAddr);
+
+ //
+ // fill in the other fields as best we can
+ //
+
+ RtlZeroMemory(pDosParms->DosDirStatus.auchMicroCodeLevel,
+ sizeof(pDosParms->DosDirStatus.auchMicroCodeLevel)
+ );
+ WRITE_DWORD(&pDosParms->DosDirStatus.dpAdapterMacAddr, 0);
+ WRITE_DWORD(&pDosParms->DosDirStatus.dpTimerTick, 0);
+ WRITE_WORD(&pDosParms->DosDirStatus.usLastNetworkStatus,
+ Adapters[adapter].LastNetworkStatusChange
+ );
+
+ //
+ // If the app has requested we return the extended parameter table, then
+ // fill it in with reasonable values if we can. There is one table per
+ // adapter, statically allocated in the real-mode redir TSR
+ //
+
+ if (pDosParms->DosDirStatus.uchAdapterConfig & 0x20) {
+
+ //
+ // Ethernet type uses different bit in DOS and Nt (or OS/2)
+ //
+
+ lpVdmWindow->aExtendedStatus[adapter].cbSize = sizeof(EXTENDED_STATUS_PARMS);
+
+ //
+ // if adapter type as reported by NtAcsLan is Ethernet (0x100), set
+ // adapter type in extended status table to Ethernet (0x10), else
+ // record whatever NtAcsLan gave us
+ //
+
+ if (pNtParms->DirStatus.usAdapterType & 0x100) {
+ WRITE_WORD(&lpVdmWindow->aExtendedStatus[adapter].wAdapterType,
+ 0x0010
+ );
+ lpVdmWindow->aExtendedStatus[adapter].cbPageFrameSize = 0;
+ WRITE_WORD(&lpVdmWindow->aExtendedStatus[adapter].wCurrentFrameSize,
+ 0
+ );
+ WRITE_WORD(&lpVdmWindow->aExtendedStatus[adapter].wMaxFrameSize,
+ 0
+ );
+ } else {
+ WRITE_WORD(&lpVdmWindow->aExtendedStatus[adapter].wAdapterType,
+ pNtParms->DirStatus.usAdapterType
+ );
+
+ //
+ // set the TR page frame size (KBytes)
+ //
+
+ lpVdmWindow->aExtendedStatus[adapter].cbPageFrameSize = 16;
+
+ //
+ // set the current and maximum DHB sizes for TR cards
+ //
+
+ WRITE_WORD(&lpVdmWindow->aExtendedStatus[adapter].wCurrentFrameSize,
+ (WORD)pNtParms->DirStatus.ulMaxFrameLength
+ );
+ WRITE_WORD(&lpVdmWindow->aExtendedStatus[adapter].wMaxFrameSize,
+ (WORD)pNtParms->DirStatus.ulMaxFrameLength
+ );
+ }
+
+ //
+ // record the address of the extended parameter table in the
+ // DIR.STATUS parameter table
+ //
+
+ WRITE_DWORD(&pDosParms->DosDirStatus.dpExtendedParms,
+ NEW_DOS_ADDRESS(dpVdmWindow, &lpVdmWindow->aExtendedStatus[adapter])
+ );
+ } else {
+
+ //
+ // no extended parameters requested
+ //
+
+ WRITE_DWORD(&pDosParms->DosDirStatus.dpExtendedParms, 0);
+ }
+
+ //
+ // return the tick counter. We don't currently update the tick counter
+ //
+
+ WRITE_DWORD(&pDosParms->DosDirStatus.dpTimerTick,
+ NEW_DOS_ADDRESS(dpVdmWindow, &lpVdmWindow->dwDlcTimerTick)
+ );
+
+ //
+ // always return a pointer to the extended adapter parameter table we
+ // now keep in DOS memory. We currently always zero this table. It
+ // would normally be maintained by the adapter (MAC) software
+ //
+
+ WRITE_DWORD(&pDosParms->DosDirStatus.dpAdapterParmsAddr,
+ NEW_DOS_ADDRESS(dpVdmWindow, &lpVdmWindow->AdapterStatusParms[adapter])
+ );
+ RtlZeroMemory(&lpVdmWindow->AdapterStatusParms[adapter],
+ sizeof(lpVdmWindow->AdapterStatusParms[adapter])
+ );
+ break;
+
+ case LLC_DLC_OPEN_SAP:
+
+ //
+ // STATION_ID is only output value
+ //
+
+ WRITE_WORD(&pDosParms->DlcOpenSap.usStationId, pNtParms->DlcOpenSap.usStationId);
+ break;
+
+ case LLC_DLC_OPEN_STATION:
+
+ //
+ // LINK_STATION_ID is only output value
+ //
+
+ WRITE_WORD(&pDosParms->DlcOpenStation.usLinkStationId, pNtParms->DlcOpenStation.usLinkStationId);
+ break;
+
+ case LLC_DLC_STATISTICS:
+ break;
+ }
+}
+
+
+LLC_STATUS
+InitializeAdapterSupport(
+ IN UCHAR AdapterNumber,
+ IN DOS_DLC_DIRECT_PARMS UNALIGNED * pDirectParms OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ The function initializes the buffer pool for the new adapter opened by
+ DOS DLC
+
+Arguments:
+
+ AdapterNumber - which adapter to initialize the buffer pool for
+ pDirectParms - Direct station parameter table, not used in NT, but
+ optional in DOS
+
+Return Value:
+
+ LLC_STATUS
+ LLC_NO_RESOURCES
+
+--*/
+
+{
+ LLC_STATUS Status;
+ HANDLE hBufferPool;
+
+ IF_DEBUG(DLC) {
+ DPUT("InitializeAdapterSupport\n");
+ }
+
+ //
+ // Check if the global DLL initialization has already been done. This is not
+ // done in global DLL init because there is no reason to start an extra
+ // thread if DLC is not used. If this succeeds then the asynchronous event
+ // handler thread will be waiting on a list of 2 events - one for each
+ // adapter. We need to submit a READ CCB for this adapter
+ //
+
+ Status = VrDlcInit();
+ if (Status != LLC_STATUS_SUCCESS) {
+ return Status;
+ } else if (InitiateRead(AdapterNumber, &Status) == NULL) {
+ return Status;
+ }
+
+ OpenedAdapters++;
+
+ //
+ // mark the adapter as being opened and get the media type/class
+ //
+
+ Adapters[AdapterNumber].IsOpen = TRUE;
+ Adapters[AdapterNumber].AdapterType = GetAdapterType(AdapterNumber);
+
+ //
+ // Create the DLC buffer pool for the new adapter. DLC driver will
+ // deallocate the buffer pool in the DIR.CLOSE.ADAPTER or when
+ // the MVDM process makes a process exit
+ //
+
+ Adapters[AdapterNumber].BufferPool = (PVOID)LocalAlloc(LMEM_FIXED, DOS_DLC_BUFFER_POOL_SIZE);
+ if (Adapters[AdapterNumber].BufferPool == NULL) {
+ Status = LLC_STATUS_NO_MEMORY;
+ goto ErrorHandler;
+ }
+
+ Status = BufferCreate(AdapterNumber,
+ Adapters[AdapterNumber].BufferPool,
+ DOS_DLC_BUFFER_POOL_SIZE,
+ DOS_DLC_MIN_FREE_THRESHOLD,
+ &hBufferPool
+ );
+ if (Status != LLC_STATUS_SUCCESS) {
+ goto ErrorHandler;
+ }
+
+ if (ARGUMENT_PRESENT(pDirectParms)) {
+
+ //
+ // create a buffer pool for the direct station (SAP 0). This allows
+ // us to receive MAC and non-MAC frames sent to the direct station
+ // without having to purposefully allocate a buffer
+ //
+
+ Status = CreateBufferPool(GET_POOL_INDEX(AdapterNumber, 0),
+ pDirectParms->dpPoolAddress,
+ pDirectParms->cPoolBlocks,
+ pDirectParms->usBufferSize
+ );
+ if (Status != LLC_STATUS_SUCCESS) {
+ goto ErrorHandler;
+ }
+
+ SaveExceptions(AdapterNumber,
+ (LPDWORD)&pDirectParms->dpAdapterCheckExit
+ );
+ Status = SetExceptionFlags(AdapterNumber,
+ (DWORD)pDirectParms->dpAdapterCheckExit,
+ (DWORD)pDirectParms->dpNetworkStatusExit,
+ (DWORD)pDirectParms->dpPcErrorExit,
+ 0
+ );
+ if (Status != LLC_STATUS_SUCCESS) {
+ goto ErrorHandler;
+ }
+ }
+
+ IF_DEBUG(DLC) {
+ DPUT("InitializeAdapterSupport: returning success\n");
+ }
+
+ return LLC_STATUS_SUCCESS;
+
+ErrorHandler:
+
+ //
+ // The open failed. We must close the adapter, but we don't care about the
+ // result. This must succeed
+ //
+
+ if (Adapters[AdapterNumber].BufferPool != NULL) {
+ LocalFree(Adapters[AdapterNumber].BufferPool);
+
+ IF_DEBUG(DLC_ALLOC) {
+ DPUT1("FREE: freed block @ %x\n", Adapters[AdapterNumber].BufferPool);
+ }
+
+ }
+
+ CloseAdapter(AdapterNumber);
+ Adapters[AdapterNumber].IsOpen = FALSE;
+
+ //
+ // this is probably not the right error code to return under these
+ // circumstances, but we'll keep it until something better comes along
+ //
+
+ IF_DEBUG(DLC) {
+ DPUT("InitializeAdapterSupport: returning FAILURE\n");
+ }
+
+ return LLC_STATUS_ADAPTER_NOT_INITIALIZED;
+}
+
+
+VOID
+SaveExceptions(
+ IN UCHAR AdapterNumber,
+ IN LPDWORD pulExceptionFlags
+ )
+
+/*++
+
+Routine Description:
+
+ Procedure saves the current exception handlers
+ and copies new current values on the old ones.
+
+Arguments:
+
+ IN UCHAR AdapterNumber - current adapter
+ IN LPDWORD pulExceptionFlags - 3 dos exception handlers in the arrays
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ IF_DEBUG(DLC) {
+ DPUT("SaveExceptions\n");
+ }
+
+ RtlCopyMemory(Adapters[AdapterNumber].PreviousExceptionHandlers,
+ Adapters[AdapterNumber].CurrentExceptionHandlers,
+ sizeof(Adapters[AdapterNumber].PreviousExceptionHandlers)
+ );
+ RtlCopyMemory(Adapters[AdapterNumber].CurrentExceptionHandlers,
+ pulExceptionFlags,
+ sizeof(Adapters[AdapterNumber].CurrentExceptionHandlers)
+ );
+}
+
+
+LPDWORD
+RestoreExceptions(
+ IN UCHAR AdapterNumber
+ )
+
+/*++
+
+Routine Description:
+
+ Procedure restores the previous exception handlers
+ and returns the their address.
+
+Arguments:
+
+ IN UCHAR AdapterNumber - current adapter
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ IF_DEBUG(DLC) {
+ DPUT("RestoreExceptions\n");
+ }
+
+ RtlCopyMemory(Adapters[AdapterNumber].CurrentExceptionHandlers,
+ Adapters[AdapterNumber].PreviousExceptionHandlers,
+ sizeof(Adapters[AdapterNumber].CurrentExceptionHandlers)
+ );
+ return Adapters[AdapterNumber].CurrentExceptionHandlers;
+}
+
+
+LLC_STATUS
+CopyDosBuffersToDescriptorArray(
+ IN OUT PLLC_TRANSMIT_DESCRIPTOR pDescriptors,
+ IN PLLC_XMIT_BUFFER pDlcBufferQueue,
+ IN OUT LPDWORD pIndex
+ )
+
+/*++
+
+Routine Description:
+
+ The routine copies DOS transmit buffers to a Nt Transmit
+ descriptor array. All DOS pointers must be mapped to the flat
+ 32-bit address space. Any data in the parameter table may be
+ unaligned.
+
+Arguments:
+
+ pDescriptors - current descriptor array
+ pDlcBufferQueue - DOS transmit buffer queue
+ pIndex - current index in the descriptor array
+
+Return Value:
+
+ LLC_STATUS
+
+--*/
+
+{
+ PLLC_XMIT_BUFFER pBuffer;
+ DWORD Index = *pIndex;
+ DWORD i = 0;
+ DWORD DlcStatus = LLC_STATUS_SUCCESS;
+ WORD cbBuffer;
+
+ IF_DEBUG(DLC) {
+ DPUT("CopyDosBuffersToDescriptorArray\n");
+ }
+
+ while (pDlcBufferQueue) {
+ pBuffer = (PLLC_XMIT_BUFFER)DOS_PTR_TO_FLAT(pDlcBufferQueue);
+
+ //
+ // Check the overflow of the internal xmit buffer in stack and
+ // the loop counter, that prevents the forever loop of zero length
+ // transmit buffer (the buffer chain might be circular)
+ //
+
+ if (Index >= MAX_TRANSMIT_SEGMENTS || i > 60000) {
+ DlcStatus = LLC_STATUS_TRANSMIT_ERROR;
+ break;
+ }
+
+ if ((cbBuffer = READ_WORD(&pBuffer->cbBuffer)) != 0) {
+ pDescriptors[Index].pBuffer = (PUCHAR)(pBuffer->auchData)
+ + READ_WORD(&pBuffer->cbUserData);
+ pDescriptors[Index].cbBuffer = cbBuffer;
+ pDescriptors[Index].eSegmentType = LLC_NEXT_DATA_SEGMENT;
+ pDescriptors[Index].boolFreeBuffer = FALSE;
+
+ Index++;
+ }
+ i++;
+ pDlcBufferQueue = (PLLC_XMIT_BUFFER)READ_DWORD(&pBuffer->pNext);
+ }
+ *pIndex = Index;
+ return DlcStatus;
+}
+
+
+LLC_STATUS
+BufferCreate(
+ IN UCHAR AdapterNumber,
+ IN PVOID pVirtualMemoryBuffer,
+ IN DWORD ulVirtualMemorySize,
+ IN DWORD ulMinFreeSizeThreshold,
+ OUT HANDLE* phBufferPoolHandle
+ )
+
+/*++
+
+Routine Description:
+
+ Function creates a Windows/Nt DLC buffer pool.
+
+ THIS COMMAND COMPLETES SYNCHRONOUSLY
+
+Arguments:
+
+ AdapterNumber -
+ pVirtualMemoryBuffer - pointer to a virtual memory
+ ulVirtualMemorySize - size of all available buffer pool space
+ ulMinFreeSizeThreshold - locks more pages when this is exceeded
+ phBufferPoolHandle -
+
+Return Value:
+
+ LLC_STATUS
+
+--*/
+
+{
+ LLC_CCB ccb;
+ LLC_BUFFER_CREATE_PARMS BufferCreate;
+ LLC_STATUS status;
+
+ IF_DEBUG(DLC) {
+ DPUT("BufferCreate\n");
+ }
+
+ InitializeCcb(&ccb, AdapterNumber, LLC_BUFFER_CREATE, &BufferCreate);
+
+ BufferCreate.pBuffer = pVirtualMemoryBuffer;
+ BufferCreate.cbBufferSize = ulVirtualMemorySize;
+ BufferCreate.cbMinimumSizeThreshold = ulMinFreeSizeThreshold;
+
+ status = lpAcsLan(&ccb, NULL);
+ *phBufferPoolHandle = BufferCreate.hBufferPool;
+
+ IF_DEBUG(DLC) {
+ DPUT2("BufferCreate: returning %#x (%d)\n", status, status);
+ }
+
+ return DLC_ERROR_STATUS(status, ccb.uchDlcStatus);
+}
+
+
+LLC_STATUS
+SetExceptionFlags(
+ IN UCHAR AdapterNumber,
+ IN DWORD ulAdapterCheckFlag,
+ IN DWORD ulNetworkStatusFlag,
+ IN DWORD ulPcErrorFlag,
+ IN DWORD ulSystemActionFlag
+ )
+
+/*++
+
+Routine Description:
+
+ Sets the new appendage addresses
+
+ THIS COMMAND COMPLETES SYNCHRONOUSLY
+
+Arguments:
+
+ AdapterNumber -
+ ulAdapterCheckFlag -
+ ulNetworkStatusFlag -
+ ulPcErrorFlag -
+ ulSystemActionFlag -
+
+Return Value:
+
+ LLC_STATUS
+
+--*/
+
+{
+ LLC_CCB ccb;
+ LLC_STATUS status;
+ LLC_DIR_SET_EFLAG_PARMS DirSetFlags;
+
+ IF_DEBUG(DLC) {
+ DPUT("SetExceptionFlags\n");
+ }
+
+ InitializeCcb(&ccb, AdapterNumber, LLC_DIR_SET_EXCEPTION_FLAGS, &DirSetFlags);
+
+ DirSetFlags.ulAdapterCheckFlag = ulAdapterCheckFlag;
+ DirSetFlags.ulNetworkStatusFlag = ulNetworkStatusFlag;
+ DirSetFlags.ulPcErrorFlag = ulPcErrorFlag;
+ DirSetFlags.ulSystemActionFlag = ulSystemActionFlag;
+
+ status = lpAcsLan(&ccb, NULL);
+ return DLC_ERROR_STATUS(status, ccb.uchDlcStatus);
+}
+
+
+LLC_STATUS
+LlcCommand(
+ IN UCHAR AdapterNumber,
+ IN UCHAR Command,
+ IN DWORD Parameter
+ )
+
+/*++
+
+Routine Description:
+
+ Calls the ACSLAN DLL to perform a DLC request which takes no parameter
+ table, but which takes parameters in byte, word or dword form in the CCB
+
+ COMMANDS USING THIS ROUTINE MUST COMPLETE SYNCHRONOUSLY
+
+Arguments:
+
+ AdapterNumber - which adapter to perform command for
+ Command - which DLC command to perform. Currently, commands are:
+ DIR.SET.GROUP.ADDRESS
+ DIR.SET.FUNCTIONAL.ADDRESS
+ DLC.FLOW.CONTROL
+ RECEIVE.CANCEL
+ Parameter - the associated command
+
+Return Value:
+
+ DWORD
+
+--*/
+
+{
+ LLC_CCB ccb;
+ LLC_STATUS status;
+
+ IF_DEBUG(DLC) {
+ DPUT3("LlcCommand(%d, %02x, %08x)\n", AdapterNumber, Command, Parameter);
+ }
+
+ InitializeCcb2(&ccb, AdapterNumber, Command);
+ ccb.u.ulParameter = Parameter;
+
+ status = lpAcsLan(&ccb, NULL);
+ return DLC_ERROR_STATUS(status, ccb.uchDlcStatus);
+}
+
+
+LLC_STATUS
+OpenAdapter(
+ IN UCHAR AdapterNumber
+ )
+
+/*++
+
+Routine Description:
+
+ Opens a DLC adapter context for a Windows/Nt VDM
+
+ THIS COMMAND COMPLETES SYNCHRONOUSLY
+
+Arguments:
+
+ AdapterNumber - which adapter to open
+
+Return Value:
+
+ LLC_STATUS
+
+--*/
+
+{
+ LLC_CCB Ccb;
+ LLC_DIR_OPEN_ADAPTER_PARMS DirOpenAdapter;
+ LLC_ADAPTER_OPEN_PARMS AdapterParms;
+ LLC_EXTENDED_ADAPTER_PARMS ExtendedParms;
+ LLC_DLC_PARMS DlcParms;
+ LLC_STATUS status;
+
+ IF_DEBUG(DLC) {
+ DPUT1("OpenAdapter(AdapterNumber=%d)\n", AdapterNumber);
+ }
+
+ InitializeCcb(&Ccb, AdapterNumber, LLC_DIR_OPEN_ADAPTER, &DirOpenAdapter);
+
+ DirOpenAdapter.pAdapterParms = &AdapterParms;
+ DirOpenAdapter.pExtendedParms = &ExtendedParms;
+ DirOpenAdapter.pDlcParms = &DlcParms;
+
+ ExtendedParms.hBufferPool = NULL;
+ ExtendedParms.pSecurityDescriptor = NULL;
+ ExtendedParms.LlcEthernetType = LLC_ETHERNET_TYPE_DEFAULT;
+
+ RtlZeroMemory(&AdapterParms, sizeof(AdapterParms));
+ RtlZeroMemory(&DlcParms, sizeof(DlcParms));
+
+ status = lpAcsLan(&Ccb, NULL);
+
+ if (status == LLC_STATUS_SUCCESS) {
+
+ //
+ // get the adapter media type/class
+ //
+
+ Adapters[AdapterNumber].AdapterType = GetAdapterType(AdapterNumber);
+
+ //
+ // mark the adapter structure as open
+ //
+
+ Adapters[AdapterNumber].IsOpen = TRUE;
+
+ //
+ // fill in the DOS ADAPTER_PARMS and DLC_PARMS structures with any
+ // returned values
+ //
+
+ RtlCopyMemory(&Adapters[AdapterNumber].AdapterParms,
+ &AdapterParms,
+ sizeof(ADAPTER_PARMS)
+ );
+ RtlCopyMemory(&Adapters[AdapterNumber].DlcParms,
+ &DlcParms,
+ sizeof(DLC_PARMS)
+ );
+ Adapters[AdapterNumber].DlcSpecified = TRUE;
+ }
+
+ IF_DEBUG(DLC) {
+ DPUT2("OpenAdapter: returning %d (%x)\n", status, status);
+ }
+
+ return DLC_ERROR_STATUS(status, Ccb.uchDlcStatus);
+}
+
+
+VOID
+CloseAdapter(
+ IN UCHAR AdapterNumber
+ )
+
+/*++
+
+Routine Description:
+
+ Closes this adapter. Uses a CCB in the DOS_ADAPTER structure specifically
+ for this purpose
+
+ THIS COMMAND COMPLETES ** ASYNCHRONOUSLY **
+
+Arguments:
+
+ AdapterNumber - adapter to close
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ InitializeCcb2(&Adapters[AdapterNumber].AdapterCloseCcb, AdapterNumber, LLC_DIR_CLOSE_ADAPTER);
+ Adapters[AdapterNumber].AdapterCloseCcb.ulCompletionFlag = VRDLC_COMMAND_COMPLETION;
+
+#if DBG
+
+ ASSERT(lpAcsLan(&Adapters[AdapterNumber].AdapterCloseCcb, NULL) == LLC_STATUS_SUCCESS);
+
+#else
+
+ lpAcsLan(&Adapters[AdapterNumber].AdapterCloseCcb, NULL);
+
+#endif
+
+ //
+ // mark the adapter structure as being closed
+ //
+
+ Adapters[AdapterNumber].IsOpen = FALSE;
+}
+
+
+LLC_STATUS
+OpenDirectStation(
+ IN UCHAR AdapterNumber
+ )
+
+/*++
+
+Routine Description:
+
+ Opens the direct station for this adapter
+
+ THIS COMMAND COMPLETES SYNCHRONOUSLY
+
+Arguments:
+
+ AdapterNumber - which adapter to open direct station for
+
+Return Value:
+
+ LLC_STATUS
+
+--*/
+
+{
+ LLC_CCB ccb;
+ LLC_DIR_OPEN_DIRECT_PARMS DirOpenDirect;
+ LLC_STATUS status;
+
+ IF_DEBUG(DLC) {
+ DPUT1("OpenDirectStation(%d)\n", AdapterNumber);
+ }
+
+ InitializeCcb(&ccb, AdapterNumber, LLC_DIR_OPEN_DIRECT, &DirOpenDirect);
+
+ DirOpenDirect.usOpenOptions = 0;
+ DirOpenDirect.usEthernetType = 0;
+
+ status = lpAcsLan(&ccb, NULL);
+ if (status == LLC_STATUS_SUCCESS) {
+
+ //
+ // mark this DOS_ADAPTER as having the direct station open
+ //
+
+ Adapters[AdapterNumber].DirectStationOpen = TRUE;
+ }
+
+ status = DLC_ERROR_STATUS(status, ccb.uchDlcStatus);
+
+ IF_DEBUG(DLC) {
+ DPUT2("OpenDirectStation: returning %d (%x)\n", status, status);
+ }
+
+ return status;
+}
+
+
+VOID
+CloseDirectStation(
+ IN UCHAR AdapterNumber
+ )
+
+/*++
+
+Routine Description:
+
+ Closes the direct station for this adapter. Uses a CCB in the DOS_ADAPTER
+ structure specifically for this purpose
+
+ THIS COMMAND COMPLETES ** ASYNCHRONOUSLY **
+
+Arguments:
+
+ AdapterNumber - adapter to close the direct station for
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ InitializeCcb2(&Adapters[AdapterNumber].DirectCloseCcb, AdapterNumber, LLC_DIR_CLOSE_DIRECT);
+ Adapters[AdapterNumber].DirectCloseCcb.ulCompletionFlag = VRDLC_COMMAND_COMPLETION;
+
+#if DBG
+
+ ASSERT(lpAcsLan(&Adapters[AdapterNumber].DirectCloseCcb, NULL) == LLC_STATUS_SUCCESS);
+
+#else
+
+ lpAcsLan(&Adapters[AdapterNumber].DirectCloseCcb, NULL);
+
+#endif
+
+ //
+ // mark the adapter structure as no longer having the direct station open
+ //
+
+ Adapters[AdapterNumber].DirectStationOpen = FALSE;
+}
+
+
+LLC_STATUS
+BufferFree(
+ IN UCHAR AdapterNumber,
+ IN PVOID pFirstBuffer,
+ OUT LPWORD pusBuffersLeft
+ )
+
+/*++
+
+Routine Description:
+
+ Frees a SAP buffer pool in the NT DLC driver
+
+ THIS COMMAND COMPLETES SYNCHRONOUSLY
+
+Arguments:
+
+ AdapterNumber -
+ pFirstBuffer -
+ pusBuffersLeft -
+
+Return Value:
+
+ LLC_STATUS
+
+--*/
+
+{
+ LLC_CCB ccb;
+ LLC_BUFFER_FREE_PARMS parms;
+ LLC_STATUS status;
+
+ IF_DEBUG(DLC) {
+ DPUT1("BufferFree(%x)\n", pFirstBuffer);
+ }
+
+ InitializeCcb(&ccb, AdapterNumber, LLC_BUFFER_FREE, &parms);
+
+ parms.pFirstBuffer = pFirstBuffer;
+
+ status = lpAcsLan(&ccb, NULL);
+ *pusBuffersLeft = parms.cBuffersLeft;
+
+ return DLC_ERROR_STATUS(status, ccb.uchDlcStatus);
+}
+
+
+LLC_STATUS
+VrDlcInit(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ perform one-shot initialization:
+
+ * clear Adapters structures
+
+ * initialize array of buffer pool structures and initialize the buffer
+ pool critical section (InitializeBufferPools in vrdlcbuf.c)
+
+ * create all events and threads for asynchronous command completion
+ processing (InitializeEventHandler in vrdlcpst.c)
+
+ * initialize critical sections for each adapter's local-busy(buffer)
+ state information
+
+ * set the DLC initialized flag
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ LLC_STATUS
+ Success - LLC_STATUS_SUCCESS
+ DLC support already initialized or initialization completed
+ successfully
+
+ Failure - LLC_STATUS_NO_MEMORY
+ failed to create the asynchronous event thread or an event
+ object
+
+--*/
+
+{
+ static BOOLEAN VrDlcInitialized = FALSE;
+ LLC_STATUS Status = LLC_STATUS_SUCCESS;
+
+ if (!VrDlcInitialized) {
+
+ //
+ // ensure that the DOS_ADAPTER structures begin life in a known state
+ //
+
+ RtlZeroMemory(Adapters, sizeof(Adapters));
+
+ //
+ // clear out the buffer pool structures and initialize the buffer
+ // pool critical section
+ //
+
+ InitializeBufferPools();
+
+ //
+ // crreate the event handler thread and the worker thread
+ //
+
+ if (!(InitializeEventHandler() && InitializeDlcWorkerThread())) {
+ Status = LLC_STATUS_NO_MEMORY;
+ } else {
+
+ //
+ // initialize each adapter's local-busy state critical section
+ // and set the first & last indicies to -1, meaning no index
+ //
+
+ int i;
+
+ for (i = 0; i < ARRAY_ELEMENTS(Adapters); ++i) {
+ InitializeCriticalSection(&Adapters[i].EventQueueCritSec);
+ InitializeCriticalSection(&Adapters[i].LocalBusyCritSec);
+ Adapters[i].FirstIndex = Adapters[i].LastIndex = NO_LINKS_BUSY;
+ }
+ VrDlcInitialized = TRUE;
+ }
+ }
+ return Status;
+}
+
+
+VOID
+VrVdmWindowInit(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine saves the address of a VDM memory window, that is used
+ in the communication betwen VDM TSR and its virtual device driver.
+ This is called from a DOS TSR module.
+
+Arguments:
+
+ ES:BX in the VDM context are set to point to a memory window in TSR.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ IF_DEBUG(DLC) {
+ DPUT("VrVdmWindowInit\n");
+ }
+
+ //
+ // Initialize the VDM memory window addresses
+ //
+
+ dpVdmWindow = MAKE_DWORD(getES(), getBX());
+ lpVdmWindow = (LPVDM_REDIR_DOS_WINDOW)DOS_PTR_TO_FLAT(dpVdmWindow);
+
+ IF_DEBUG(DLC) {
+ DPUT2("VrVdmWindowsInit: dpVdmWindow=%08x lpVdmWindow=%08x\n", dpVdmWindow, lpVdmWindow);
+ }
+
+ //
+ // have to return success to VDM redir TSR
+ //
+
+ setCF(0);
+}
+
+
+ADAPTER_TYPE
+GetAdapterType(
+ IN UCHAR AdapterNumber
+ )
+
+/*++
+
+Routine Description:
+
+ Determines what type of adapter AdapterNumber designates
+
+ THE DIR.STATUS COMMAND COMPLETES SYNCHRONOUSLY
+
+Arguments:
+
+ AdapterNumber - number of adapter to get type of
+
+Return Value:
+
+ ADAPTER_TYPE
+ TokenRing, Ethernet, PcNetwork, or UnknownAdapter
+--*/
+
+{
+ LLC_CCB ccb;
+ LLC_DIR_STATUS_PARMS parms;
+ LLC_STATUS status;
+
+ IF_DEBUG(DLC) {
+ DPUT("GetAdapterType\n");
+ }
+
+ InitializeCcb(&ccb, AdapterNumber, LLC_DIR_STATUS, &parms);
+
+ status = lpAcsLan(&ccb, NULL);
+
+ if (status == LLC_STATUS_SUCCESS) {
+ switch (parms.usAdapterType) {
+ case 0x0001: // Token Ring Network PC Adapter
+ case 0x0002: // Token Ring Network PC Adapter II
+ case 0x0004: // Token Ring Network Adapter/A
+ case 0x0008: // Token Ring Network PC Adapter II
+ case 0x0020: // Token Ring Network 16/4 Adapter
+ case 0x0040: // Token Ring Network 16/4 Adapter/A
+ case 0x0080: // Token Ring Network Adapter/A
+ return TokenRing;
+
+ case 0x0100: //Ethernet Adapter
+ return Ethernet;
+
+ case 0x4000: // PC Network Adapter
+ case 0x8000: // PC Network Adapter/A
+ return PcNetwork;
+ }
+ }
+ return UnknownAdapter;
+}
+
+
+BOOLEAN
+LoadDlcDll(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Dynamically loads DLCAPI.DLL & fixes-up entry points
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN
+ TRUE if success else FALSE
+
+--*/
+
+{
+ HANDLE hLibrary;
+ LPWORD lpVdmPointer;
+
+ if ((hLibrary = LoadLibrary("DLCAPI")) == NULL) {
+
+ IF_DEBUG(DLC) {
+ DPUT1("LoadDlcDll: Error: cannot load DLCAPI.DLL: %d\n", GetLastError());
+ }
+
+ return FALSE;
+ }
+ if ((lpAcsLan = (ACSLAN_FUNC_PTR)GetProcAddress(hLibrary, "AcsLan")) == NULL) {
+
+ IF_DEBUG(DLC) {
+ DPUT1("LoadDlcDll: Error: cannot GetProcAddress(AcsLan): %d\n", GetLastError());
+ }
+
+ return FALSE;
+ }
+ if ((lpDlcCallDriver = (DLC_CALL_DRIVER_FUNC_PTR)GetProcAddress(hLibrary, "DlcCallDriver")) == NULL) {
+
+ IF_DEBUG(DLC) {
+ DPUT1("LoadDlcDll: Error: cannot GetProcAddress(DlcCallDriver): %d\n", GetLastError());
+ }
+
+ return FALSE;
+ }
+ if ((lpNtAcsLan = (NTACSLAN_FUNC_PTR)GetProcAddress(hLibrary, "NtAcsLan")) == NULL) {
+
+ IF_DEBUG(DLC) {
+ DPUT1("LoadDlcDll: Error: cannot GetProcAddress(NtAcsLan): %d\n", GetLastError());
+ }
+
+ return FALSE;
+ }
+
+ IF_DEBUG(DLC) {
+ DPUT("LoadDlcDll: DLCAPI.DLL loaded Ok\n");
+ }
+
+ //
+ // Initialize the VDM memory window addresses from our well-known address
+ // in the VDM Redir. Do this here because we no longer initialize 32-bit
+ // support at the point where we load the 16-bit REDIR
+ //
+
+ lpVdmPointer = POINTER_FROM_WORDS(getCS(), (DWORD)&((VDM_LOAD_INFO*)0)->DlcWindowAddr);
+ dpVdmWindow = MAKE_DWORD(GET_SEGMENT(lpVdmPointer), GET_OFFSET(lpVdmPointer));
+ lpVdmWindow = (LPVDM_REDIR_DOS_WINDOW)DOS_PTR_TO_FLAT(dpVdmWindow);
+
+ IF_DEBUG(DLC) {
+ DPUT4("LoadDlcDll: lpVdmPointer=%x dpVdmWindow = %04x:%04x lpVdmWindow=%x\n",
+ lpVdmPointer,
+ HIWORD(dpVdmWindow),
+ LOWORD(dpVdmWindow),
+ lpVdmWindow
+ );
+ }
+
+ return TRUE;
+}
+
+
+VOID
+TerminateDlcEmulation(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Closes any open adapters. Any pending commands are terminated
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ DWORD i;
+
+ IF_DEBUG(DLC) {
+ DPUT("TerminateDlcEmulation\n");
+ }
+
+ IF_DEBUG(CRITICAL) {
+ DPUT("TerminateDlcEmulation\n");
+ }
+
+ for (i = 0; i < ARRAY_ELEMENTS(Adapters); ++i) {
+ if (Adapters[i].IsOpen) {
+ CloseAdapter((BYTE)i);
+ }
+ }
+}
+
+HANDLE DlcWorkerEvent;
+HANDLE DlcWorkerCompletionEvent;
+HANDLE DlcWorkerThreadHandle;
+
+struct {
+ PLLC_CCB Input;
+ PLLC_CCB Original;
+ PLLC_CCB Output;
+ LLC_STATUS Status;
+} DlcWorkerThreadParms;
+
+
+BOOLEAN
+InitializeDlcWorkerThread(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Creates events which control VrDlcWorkerThread and starts the worker thread
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ BOOLEAN
+ TRUE - worker thread was successfully created
+ FALSE - couldn't start worker thread for some reason
+
+--*/
+
+{
+ DWORD threadId;
+
+ //
+ // create 2 auto-reset events
+ //
+
+ DlcWorkerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (DlcWorkerEvent == NULL) {
+ return FALSE;
+ }
+ DlcWorkerCompletionEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (DlcWorkerEvent == NULL) {
+ CloseHandle(DlcWorkerEvent);
+ return FALSE;
+ }
+
+ //
+ // kick off the one-and-only worker thread
+ //
+
+ DlcWorkerThreadHandle = CreateThread(NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)VrDlcWorkerThread,
+ NULL,
+ 0,
+ &threadId
+ );
+ if (DlcWorkerThreadHandle == NULL) {
+ CloseHandle(DlcWorkerEvent);
+ CloseHandle(DlcWorkerCompletionEvent);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+VOID
+VrDlcWorkerThread(
+ IN LPVOID Parameters
+ )
+
+/*++
+
+Routine Description:
+
+ Submits requests to NtAcsLan on behalf of DOS thread. This exists because of
+ a problem with 16-bit Windows apps that use DLC (like Extra!). Eg:
+
+ 1. start Extra! session. Extra submits RECEIVE command
+ 2. connect to mainframe
+ 3. start second Extra! session
+ 4. connect second instance to mainframe
+ 5. kill first Extra! session
+
+ On a DOS machine, the RECEIVE is submitted for the entire process, so when
+ the first Extra! session is killed, the RECEIVE is still active.
+
+ However, on NT, each session is represented by a separate thread in NTVDM.
+ So when the first session is killed, any outstanding IRPs are cancelled,
+ including the RECEIVE. The second instance of Extra! doesn't know that the
+ RECEIVE has been cancelled, and never receives any more data
+
+Arguments:
+
+ Parameters - unused pointer to parameter block
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ DWORD object;
+
+ UNREFERENCED_PARAMETER(Parameters);
+
+ while (TRUE) {
+ object = WaitForSingleObject(DlcWorkerEvent, INFINITE);
+ if (object == WAIT_OBJECT_0) {
+ DlcWorkerThreadParms.Status = lpNtAcsLan(DlcWorkerThreadParms.Input,
+ DlcWorkerThreadParms.Original,
+ DlcWorkerThreadParms.Output,
+ NULL
+ );
+ SetEvent(DlcWorkerCompletionEvent);
+ }
+ }
+}
+
+
+LLC_STATUS
+DlcCallWorker(
+ PLLC_CCB pInputCcb,
+ PLLC_CCB pOriginalCcb,
+ PLLC_CCB pOutputCcb
+ )
+
+/*++
+
+Routine Description:
+
+ Queues (depth is one) a request to the DLC worker thread and waits for the
+ worker thread to complete the request
+
+Arguments:
+
+ pInputCcb - pointer to input CCB. Mapped to 32-bit aligned memory
+ pOriginalCcb - address of original CCB. Can be non-aligned DOS address
+ pOutputCcb - pointer to output CCB. Can be non-aligned DOS address
+
+Return Value:
+
+ LLC_STATUS
+
+--*/
+
+{
+ DlcWorkerThreadParms.Input = pInputCcb;
+ DlcWorkerThreadParms.Original = pOriginalCcb;
+ DlcWorkerThreadParms.Output = pOutputCcb;
+ SetEvent(DlcWorkerEvent);
+ WaitForSingleObject(DlcWorkerCompletionEvent, INFINITE);
+ return DlcWorkerThreadParms.Status;
+}