mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3893 lines
112 KiB
3893 lines
112 KiB
/*++
|
|
|
|
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;
|
|
}
|