3894 lines
112 KiB
C
3894 lines
112 KiB
C
/*++
|
||
|
||
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 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 = (char)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 useless 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 (DlcWorkerCompletionEvent == 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;
|
||
}
|