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