windows-nt/Source/XPSP1/NT/net/dlc/api/acslan.c

3383 lines
115 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1991 Microsoft Corporation
Copyright (c) 1991 Nokia Data Systems
Module Name:
acslan.c
Abstract:
The module is the entry to the OS/2 ACSLAN emulation module.
It uses the secure native NT DLC API to implement the full
IBM OS/2 DLC compatible interface for Windows/NT.
Contents:
AcsLan
NtAcsLan
GetCcbStationId
OpenDlcApiDriver
GetAdapterNameAndParameters
GetAdapterNameFromNumber
GetAdapterNumberFromName
DoSyncDeviceIoControl
DlcGetInfo
DlcSetInfo
DlcCallDriver
DllEntry
QueueCommandCompletion
Author:
Antti Saarenheimo (o-anttis) 06-JUN-1991
Revision History:
--*/
#include "dlcdll.h"
#include "dlcdebug.h"
#define DLC_UNSUPPORTED_COMMAND ((ULONG)0x7fffffff)
#define DLC_ASYNCHRONOUS_FLAG ((ULONG)0x80000000)
#define IS_SYNCHRONOUS(command) (!(IoctlCodes[command] & DLC_ASYNCHRONOUS_FLAG))
#define DLC_IOCTL(command) (IoctlCodes[command] & ~DLC_ASYNCHRONOUS_FLAG)
#define IS_TRANSMIT(command) (((command) == LLC_TRANSMIT_DIR_FRAME) \
|| ((command) == LLC_TRANSMIT_I_FRAME) \
|| ((command) == LLC_TRANSMIT_UI_FRAME) \
|| ((command) == LLC_TRANSMIT_XID_CMD) \
|| ((command) == LLC_TRANSMIT_XID_RESP_FINAL) \
|| ((command) == LLC_TRANSMIT_XID_RESP_NOT_FINAL) \
|| ((command) == LLC_TRANSMIT_TEST_CMD))
#define DEFAULT_QUERY_BUFFER_LENGTH 1024 // 512 wide chars, approx. 32 bindings
#define DEFAULT_BINDING_LENGTH 64 // 32 wide chars, double typical
#ifdef GRAB_READ
typedef struct {
PVOID List;
PLLC_CCB pCcb;
HANDLE OriginalEventHandle;
HANDLE NewEventHandle;
} READ_GRABBER, *PREAD_GRABBER;
VOID ReadGrabber(VOID);
DWORD MungeReadGrabberHandles(VOID);
VOID AddReadGrabber(PREAD_GRABBER);
PREAD_GRABBER RemoveReadGrabber(HANDLE);
#endif
//
// private data
//
static USHORT aMinDirLogSize[3] = {
sizeof(LLC_ADAPTER_LOG),
sizeof(LLC_DIRECT_LOG),
sizeof(LLC_ADAPTER_LOG) + sizeof(LLC_DIRECT_LOG)
};
CRITICAL_SECTION DriverHandlesCritSec;
HANDLE aDlcDriverHandles[LLC_MAX_ADAPTER_NUMBER];
IO_STATUS_BLOCK GlobalIoStatus;
//
// IoctlCodes - combines actual IOCTL code (giving device type, request vector,
// I/O buffer method and file access) and synchronous/asynchronous flag (high bit)
//
ULONG IoctlCodes[LLC_MAX_DLC_COMMAND] = {
DLC_UNSUPPORTED_COMMAND, // 0x00 DIR.INTERRUPT
DLC_UNSUPPORTED_COMMAND, // 0x01 DIR.MODIFY.OPEN.PARMS CCB1 ONLY
DLC_UNSUPPORTED_COMMAND, // 0x02 DIR.RESTORE.OPEN.PARMS CCB1 ONLY
IOCTL_DLC_OPEN_ADAPTER, // 0x03 DLC.OPEN.ADAPTER
IOCTL_DLC_CLOSE_ADAPTER | DLC_ASYNCHRONOUS_FLAG, // 0x04 DIR.CLOSE.ADAPTER
IOCTL_DLC_SET_INFORMATION, // 0x05 DIR.SET.MULTICAST.ADDRESS
IOCTL_DLC_SET_INFORMATION, // 0x06 DIR.SET.GROUP.ADDRESS
IOCTL_DLC_SET_INFORMATION, // 0x07 DIR.SET.FUNCTIONAL.ADDRESS
DLC_UNSUPPORTED_COMMAND, // 0x08 DIR.READ.LOG
IOCTL_DLC_TRANSMIT2 | DLC_ASYNCHRONOUS_FLAG, // 0x09 TRANSMIT.FRAMES
IOCTL_DLC_TRANSMIT | DLC_ASYNCHRONOUS_FLAG, // 0x0a TRANSMIT.DIR.FRAME
IOCTL_DLC_TRANSMIT | DLC_ASYNCHRONOUS_FLAG, // 0x0b TRANSMIT.I.FRAME
DLC_UNSUPPORTED_COMMAND, // 0x0c no command
IOCTL_DLC_TRANSMIT | DLC_ASYNCHRONOUS_FLAG, // 0x0d TRANSMIT.UI.FRAME
IOCTL_DLC_TRANSMIT | DLC_ASYNCHRONOUS_FLAG, // 0x0e TRANSMIT.XID.CMD
IOCTL_DLC_TRANSMIT | DLC_ASYNCHRONOUS_FLAG, // 0x0f TRANSMIT.XID.RESP.FINAL
IOCTL_DLC_TRANSMIT | DLC_ASYNCHRONOUS_FLAG, // 0x10 TRANSMIT.XID.RESP.NOT.FINAL
IOCTL_DLC_TRANSMIT | DLC_ASYNCHRONOUS_FLAG, // 0x11 TRANSMIT.TEST.CMD
IOCTL_DLC_QUERY_INFORMATION, // 0x12 no command
IOCTL_DLC_SET_INFORMATION, // 0x13 no command
IOCTL_DLC_RESET | DLC_ASYNCHRONOUS_FLAG, // 0x14 DLC.RESET
IOCTL_DLC_OPEN_SAP, // 0x15 DLC.OPEN.SAP
IOCTL_DLC_CLOSE_SAP | DLC_ASYNCHRONOUS_FLAG, // 0x16 DLC.CLOSE.SAP
IOCTL_DLC_REALLOCTE_STATION, // 0x17 DLC.REALLOCATE
DLC_UNSUPPORTED_COMMAND, // 0x18 no command
IOCTL_DLC_OPEN_STATION, // 0x19 DLC.OPEN.STATION
IOCTL_DLC_CLOSE_STATION | DLC_ASYNCHRONOUS_FLAG, // 0x1a DLC.CLOSE.STATION
IOCTL_DLC_CONNECT_STATION | DLC_ASYNCHRONOUS_FLAG, // 0x1b DLC.CONNECT.STATION
DLC_UNSUPPORTED_COMMAND, // 0x1c DLC.MODIFY
IOCTL_DLC_FLOW_CONTROL, // 0x1d DLC.FLOW.CONTROL
DLC_UNSUPPORTED_COMMAND, // 0x1e DLC.STATISTICS
IOCTL_DLC_FLOW_CONTROL, // 0x1f no command
IOCTL_DLC_CLOSE_ADAPTER | DLC_ASYNCHRONOUS_FLAG, // 0x20 DIR.INITIALIZE
IOCTL_DLC_QUERY_INFORMATION, // 0x21 DIR.STATUS
IOCTL_DLC_TIMER_SET | DLC_ASYNCHRONOUS_FLAG, // 0x22 DIR.TIMER.SET
IOCTL_DLC_TIMER_CANCEL, // 0x23 DIR.TIMER.CANCEL
DLC_UNSUPPORTED_COMMAND, // 0x24 PDT.TRACE.ON CCB1 ONLY
DLC_UNSUPPORTED_COMMAND, // 0x25 PDT.TRACE.OFF CCB1 ONLY
IOCTL_DLC_BUFFER_GET, // 0x26 BUFFER.GET
IOCTL_DLC_BUFFER_FREE, // 0x27 BUFFER.FREE
IOCTL_DLC_RECEIVE | DLC_ASYNCHRONOUS_FLAG, // 0x28 RECEIVE
IOCTL_DLC_RECEIVE_CANCEL, // 0x29 RECEIVE.CANCEL
DLC_UNSUPPORTED_COMMAND, // 0x2a RECEIVE.MODIFY
DLC_UNSUPPORTED_COMMAND, // 0x2b DIR.DEFINE.MIF.ENVIRONMENT CCB1 ONLY
IOCTL_DLC_TIMER_CANCEL_GROUP, // 0x2c DIR.TIMER.CANCEL.GROUP
IOCTL_DLC_SET_EXCEPTION_FLAGS, // 0x2d DIR.SET.EXCEPTION.FLAGS
DLC_UNSUPPORTED_COMMAND, // 0x2e no command
DLC_UNSUPPORTED_COMMAND, // 0x2f no command
IOCTL_DLC_BUFFER_CREATE, // 0x30 BUFFER.CREATE
IOCTL_DLC_READ | DLC_ASYNCHRONOUS_FLAG, // 0x31 READ
IOCTL_DLC_READ_CANCEL, // 0x32 READ.CANCEL
DLC_UNSUPPORTED_COMMAND, // 0x33 DLC.SET.THRESHOLD
IOCTL_DLC_CLOSE_DIRECT | DLC_ASYNCHRONOUS_FLAG, // 0x34 DIR.CLOSE.DIRECT
IOCTL_DLC_OPEN_DIRECT, // 0x35 DIR.OPEN.DIRECT
DLC_UNSUPPORTED_COMMAND // 0x36 PURGE.RESOURCES
};
CRITICAL_SECTION AdapterOpenSection;
//
// macros
//
//
// The next procedure has been implemented as macro, because it is on
// the critical path (used by BufferFree and all old transmit commands)
//
#ifdef DLCAPI_DBG
VOID
CopyToDescriptorBuffer(
IN OUT PLLC_TRANSMIT_DESCRIPTOR pDescriptors,
IN PLLC_XMIT_BUFFER pDlcBufferQueue,
IN BOOLEAN DeallocateBufferAfterUse,
IN OUT PUINT pIndex,
IN OUT PLLC_XMIT_BUFFER *ppBuffer,
IN OUT PUINT pDlcStatus
)
/*++
Routine Description:
Function translates the link list of DLC buffers to a NT DLC descriptor
array to be used as the input parameter for dlc device driver.
(NT driver may have only one input buffer => we cannot use any link
list structures to give parameters to a NT dlc driver).
Arguments:
pDescriptors - NT DLC descriptor array
pDlcBufferQueue - pointer to a link list of DLC buffers
DeallocateBufferAfterUse - the flag is set, if the dlc buffers
are released back to buffer pool when the frame is sent or
if this routine is called by buffer free.
pIndex - current index of the descriptor array
ppLastBuffer - pointer to the next buffer, if the maximum size of
the current descriptor table in stack is exceeded. This feature
is used, if the number buffers in free list given to BufferFree
is bigger that the maximum number of slots in the descriptor
array (allocated from stack).
It's an error, if this parameter has non null value, when we
returne back to the transmit command.
Return Value:
LLC_STATUS_TRANSMIT_ERROR - there are too many transmit buffers
(over 128) for the static descriptor buffer, that is allocated
from the stack.
--*/
{
*ppBuffer = pDlcBufferQueue;
*pDlcStatus = LLC_STATUS_SUCCESS;
while (*ppBuffer != NULL) {
//
// 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 (*pIndex >= MAX_TRANSMIT_SEGMENTS) {
*pDlcStatus = LLC_STATUS_TRANSMIT_ERROR;
break;
}
//
// Buffer free may free buffers having 0 data bytes (just the
// lan and LLC headers).
//
pDescriptors[*pIndex].pBuffer = &(*ppBuffer)->auchData[(*ppBuffer)->cbUserData];
pDescriptors[*pIndex].cbBuffer = (*ppBuffer)->cbBuffer;
pDescriptors[*pIndex].eSegmentType = LLC_NEXT_DATA_SEGMENT;
pDescriptors[*pIndex].boolFreeBuffer = DeallocateBufferAfterUse;
//
// We will reset all next pointers of the released buffers
// to break loops in the buffer chain of BufferFree
// request. BufferFree would loop for ever with a circular
// buffer link list.
//
if (DeallocateBufferAfterUse) {
PLLC_XMIT_BUFFER pTempNext;
pTempNext = (*ppBuffer)->pNext;
(*ppBuffer)->pNext = NULL;
*ppBuffer = pTempNext;
} else {
*ppBuffer = (*ppBuffer)->pNext;
}
*pIndex++;
}
}
#else
#define CopyToDescriptorBuffer(pDescriptors, \
pDlcBufferQueue, \
DeallocateBufferAfterUse, \
pIndex, \
ppBuffer, \
pDlcStatus \
) \
{ \
(*ppBuffer) = pDlcBufferQueue; \
(*pDlcStatus) = LLC_STATUS_SUCCESS; \
\
while ((*ppBuffer) != NULL) { \
\
if (*pIndex >= MAX_TRANSMIT_SEGMENTS) { \
(*pDlcStatus) = LLC_STATUS_TRANSMIT_ERROR; \
break; \
} \
\
pDescriptors[*pIndex].pBuffer = &((*ppBuffer)->auchData[(*ppBuffer)->cbUserData]); \
pDescriptors[*pIndex].cbBuffer = (*ppBuffer)->cbBuffer; \
pDescriptors[*pIndex].eSegmentType = LLC_NEXT_DATA_SEGMENT; \
pDescriptors[*pIndex].boolFreeBuffer = DeallocateBufferAfterUse; \
\
if (DeallocateBufferAfterUse) { \
\
PLLC_XMIT_BUFFER pTempNext; \
\
pTempNext = (*ppBuffer)->pNext; \
(*ppBuffer)->pNext = NULL; \
(*ppBuffer) = pTempNext; \
} else { \
(*ppBuffer) = (*ppBuffer)->pNext; \
} \
(*pIndex)++; \
} \
}
#endif
//
// functions
//
ACSLAN_STATUS
AcsLan(
IN OUT PLLC_CCB pCCB,
OUT PLLC_CCB* ppBadCcb
)
/*++
Routine Description:
Native NT DLC API (ACSLAN) entry point. Called from Win32 applications
Arguments:
pCCB - pointer to CCB (CCB2 = OS/2 DLC Command Control Block)
ppBadCcb - returned pointer to failing CCB
Return Value:
ACSLAN_STATUS
Success - ACSLAN_STATUS_COMMAND_ACCEPTED
Successfully accepted or completed CCB
Failure - ACSLAN_STATUS_INVALID_CCB_POINTER
Next CCB pointer field is invalid
ACSLAN_STATUS_CCB_IN_ERROR
Error code returned in only/first CCB
ACSLAN_STATUS_CHAINED_CCB_IN_ERROR
Error code returned in a chained CCB
ACSLAN_STATUS_SYSTEM_STATUS
Unexpected system error, check the system status field
ACSLAN_STATUS_INVALID_COMMAND
The first CCB pointer or bad CCB pointer was invalid
--*/
{
UINT AcslanStatus;
UINT Status;
PLLC_CCB pFirstCcb = pCCB;
IF_DEBUG(DUMP_ACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, TRUE);
}
}
try {
if (pCCB->uchDlcCommand >= LLC_MAX_DLC_COMMAND) {
pCCB->uchDlcStatus = LLC_STATUS_INVALID_COMMAND;
AcslanStatus = ACSLAN_STATUS_CCB_IN_ERROR;
} else if (pCCB->pNext == NULL) {
//
// 99.9% of all DLC commands are not chained. We execute
// them as a special case to avoid the wasting of the CPU
// cycles with that CCB chaining stuff
//
//
// DOS DLC needs three different CCB pointers.
// In Windows/Nt there is only one.
// We cannot complete the synchronous commands
// by the io-system, because another thread waiting
// for the event to complete might be signalled before
// the status and the output parameters have been set
// in the CCB and its parameter table
//
AcslanStatus = ACSLAN_STATUS_COMMAND_ACCEPTED;
if (IS_SYNCHRONOUS(pCCB->uchDlcCommand)) {
//
// synchronous command: let the driver do the work then set
// the status field in the output CCB to the value returned
// by the driver
//
Status = NtAcsLan(pCCB, pCCB, pCCB, NULL);
pCCB->uchDlcStatus = (UCHAR)Status;
if (Status != LLC_STATUS_SUCCESS) {
AcslanStatus = ACSLAN_STATUS_CCB_IN_ERROR;
//
// RLF 05/18/93
//
// If NtAcsLan returns the CCB.NEXT field pointing at the CCB,
// set it to NULL
//
if (pCCB->pNext == pCCB) {
pCCB->pNext = NULL;
}
}
//
// here we handle asyncronous completion of the synchronous
// commands by using the READ command
//
// RLF 04/23/93 Bogus: This should be handled in the driver
//
if (pCCB->ulCompletionFlag != 0) {
QueueCommandCompletion(pCCB);
}
//
// Signal the event when everything has been done
//
if (pCCB->hCompletionEvent != NULL) {
SetEvent(pCCB->hCompletionEvent);
}
} else {
//
// The command completion field is used as special
// input parameter for the chained READ commands
//
if (pCCB->uchDlcCommand == LLC_READ) {
((PNT_DLC_READ_INPUT)pCCB->u.pParameterTable)->CommandCompletionCcbLink = NULL;
}
//
// The asynchronous commands always returns a pending status
// (we cannot touch the CCB status field because it may be
// simultaneously accessed by another processor in MP systems)
//
Status = NtAcsLan(pCCB, pCCB, pCCB, pCCB->hCompletionEvent);
if ((Status != LLC_STATUS_PENDING) && (Status != LLC_STATUS_SUCCESS)) {
//printf("ACSLAN: Async Command %#x Retcode %#x\n", pCCB->uchDlcCommand, pCCB->uchDlcStatus);
//
// Only return immediate error status on asynchronous
// commands if this is a transmit
//
if (IS_TRANSMIT(pCCB->uchDlcCommand)) {
AcslanStatus = ACSLAN_STATUS_CCB_IN_ERROR;
} else if (pCCB->hCompletionEvent) {
SetEvent(pCCB->hCompletionEvent);
}
}
}
} else {
//
// here if there is a chain of CCBs
//
PLLC_CCB pNextCCB;
INT CcbCount;
//
// An evil app may have linked the CCBs in a circular list (it
// happens very easily when the same transmit commands are reused
// before they have been read from the command completion list.
// We prevent looping forever by checking the number of linked CCBs
// beforehand. (We will save the current command count, because the
// CCB chain may also be corrupted during its execution)
//
pNextCCB = pCCB->pNext;
//
// note: 10240 is an arbitrary number. Any reasonably large number
// will do, this is too large, but we'll stick with it for now
//
for (CcbCount = 1; pNextCCB != NULL && CcbCount < 10240; CcbCount++) {
pNextCCB = pNextCCB->pNext;
}
if (CcbCount == 10240) {
//
// Too many commands, the CCB list must be circular
//
AcslanStatus = ACSLAN_STATUS_INVALID_CCB_POINTER;
} else {
//
// Several CCBs may be chained together. Loop until end of
// the list or the next CCB is a special READ CCB bound to
// the current command
//
do {
//
// Set the default ACSLAN error status returned in case the
// given CCB pointer is invalid
//
AcslanStatus = ACSLAN_STATUS_INVALID_COMMAND;
//
// Reset the command completion link by default. We will set
// it if we find a READ command linked to the previous command
//
if (pCCB->uchDlcCommand == LLC_READ) {
((PNT_DLC_READ_INPUT)pCCB->u.pParameterTable)->CommandCompletionCcbLink = NULL;
} else if (pCCB->uchDlcCommand >= LLC_MAX_DLC_COMMAND) {
AcslanStatus = ACSLAN_STATUS_CCB_IN_ERROR;
pCCB->uchDlcStatus = LLC_STATUS_INVALID_COMMAND;
break;
}
//
// Check if there is a READ command linked to the CCB
// pointer of this command to be used for the command
// completion
//
pNextCCB = pCCB->pNext;
if (pNextCCB != NULL) {
AcslanStatus = ACSLAN_STATUS_INVALID_CCB_POINTER;
if (pNextCCB->uchAdapterNumber != pCCB->uchAdapterNumber) {
pCCB->uchDlcStatus = LLC_STATUS_CHAINED_DIFFERENT_ADAPTERS;
break;
} else {
if (pCCB->uchReadFlag && pCCB->ulCompletionFlag
&& pNextCCB->uchDlcCommand == LLC_READ) {
//
// Swap the actual CCB and its read command in
// the linked list of sequential CCBs.
// Note: the chain may continue after READ
//
pNextCCB = pCCB;
pCCB = pCCB->pNext;
pNextCCB->pNext = pCCB->pNext;
pCCB->pNext = pNextCCB;
((PNT_DLC_READ_INPUT)pCCB->u.pParameterTable)->CommandCompletionCcbLink = pNextCCB;
}
}
}
//
// CCB is now safe, any exceptions returned by NtAcsLan
// indicate an invalid (parameter) pointer within CCB
//
AcslanStatus = ACSLAN_STATUS_COMMAND_ACCEPTED;
//
// DOS DLC needs three different CCB pointers.
// In Windows/Nt there is only one.
// We cannot complete the synchronous commands
// by the io-system, because another thread waiting for
// the event to complete might be signalled before
// the status and the output parameters have been set
// in the CCB and its parameter table
//
Status = NtAcsLan(pCCB,
pCCB,
pCCB,
IS_SYNCHRONOUS(pCCB->uchDlcCommand)
? NULL
: pCCB->hCompletionEvent
);
if (Status != LLC_STATUS_PENDING) {
pCCB->uchDlcStatus = (UCHAR)Status;
}
//
// We must stop the command execution of all commands, when we
// hit the first error (the next commands would assume that
// this command succeeded)
//
if (pCCB->uchDlcStatus != LLC_STATUS_PENDING) {
//
// here, we handle the asyncronous command completion
// of the synchronous commands by using the READ
//
if (IS_SYNCHRONOUS(pCCB->uchDlcCommand)) {
//
// RLF 04/23/93 Bogus: This should be handled in the driver
//
if (pCCB->ulCompletionFlag != 0) {
QueueCommandCompletion(pCCB);
}
//
// Signal the event when everything has been done
//
if (pCCB->hCompletionEvent != NULL) {
SetEvent(pCCB->hCompletionEvent);
}
}
if (pCCB->uchDlcStatus != LLC_STATUS_SUCCESS) {
AcslanStatus = ACSLAN_STATUS_CCB_IN_ERROR;
break;
}
}
pCCB = pNextCCB;
CcbCount--;
} while (pCCB != NULL && CcbCount > 0);
//
// Check if the CCB list was corrupted during its use. There
// must be the same number of linked CCBs as in the beginning
//
if (pCCB != NULL && CcbCount == 0) {
AcslanStatus = ACSLAN_STATUS_INVALID_CCB_POINTER;
} else if (AcslanStatus != ACSLAN_STATUS_COMMAND_ACCEPTED) {
if (pCCB != pFirstCcb) {
*ppBadCcb = pCCB;
AcslanStatus = ACSLAN_STATUS_CHAINED_CCB_IN_ERROR;
} else {
AcslanStatus = ACSLAN_STATUS_CCB_IN_ERROR;
}
}
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// There was a bad pointer in the parameter table if the exception
// occurred in NtAcsLan! If we have a chain of CCBs then we have to
// return which was is bad, else just notify the caller that their
// data is unacceptable
//
if (AcslanStatus == ACSLAN_STATUS_COMMAND_ACCEPTED) {
pCCB->uchDlcStatus = LLC_STATUS_INVALID_PARAMETER_TABLE;
if (pCCB != pFirstCcb) {
*ppBadCcb = pCCB;
AcslanStatus = ACSLAN_STATUS_CHAINED_CCB_IN_ERROR;
} else {
AcslanStatus = ACSLAN_STATUS_CCB_IN_ERROR;
}
}
}
IF_DEBUG(DUMP_ACSLAN) {
IF_DEBUG(DUMP_OUTPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("AcsLan: returning %d [0x%x]\n", AcslanStatus, AcslanStatus));
}
}
return AcslanStatus;
}
LLC_STATUS
NtAcsLan(
IN PLLC_CCB pCCB,
IN PVOID pOriginalCcbAddress,
OUT PLLC_CCB pOutputCcb,
IN HANDLE EventHandle OPTIONAL
)
/*++
Routine Description:
Extended ACSLAN entrypoint used by the native NT DLC API and DOS (and
OS/2) DLC subsystem emulators.
This procedure can use the smaller original DOS CCBs. All unknown
DOS CCB parameter fields are optional parameters on the stack
Arguments:
pCCB - OS/2 DLC Command control block.
This must be double word aligned
pOriginalCcbAddress - the original (possibly virtual DOS) CCB address.
This pointer cannot be used in Windows/Nt address
space.
pOutputCcb - the original CCB (32bit) pointer where the status and
next CCB fields are updated.
Might not be double word aligned.
EventHandle - NT event object handle
Return Value:
LLC_STATUS - See the DLC API return values.
--*/
{
NT_DLC_PARMS NtDlcParms;
PNT_DLC_PARMS pNtParms;
PLLC_PARMS pDlcParms;
PVOID pOutputBuffer;
UINT OutputBufferLength;
PVOID pInputBuffer;
UINT InputBufferSize;
ULONG IoctlCommand;
UINT DlcStatus;
HANDLE DriverHandle;
NTSTATUS NtStatus;
UINT InfoClass;
UINT cElement;
UCHAR FrameType;
UCHAR AdapterNumber;
PUCHAR pBuffer;
UINT CopyLength;
UINT cbLogBuffer;
PLLC_XMIT_BUFFER pFirstBuffer;
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, TRUE);
}
}
//
// Setup first the default values for this CCB
//
pCCB->uchDlcStatus = (UCHAR)LLC_STATUS_PENDING;
pCCB->pNext = pOriginalCcbAddress;
pDlcParms = pCCB->u.pParameterTable;
//
// Discard immediately any commands for closed adapters,
// except adapter open and initialize
//
AdapterNumber = pCCB->uchAdapterNumber;
EnterCriticalSection(&DriverHandlesCritSec);
DriverHandle = aDlcDriverHandles[AdapterNumber];
LeaveCriticalSection(&DriverHandlesCritSec);
if (DriverHandle == NULL) {
//
// OS/2 DLC applications may issue DIR_INITIALIZE_ADAPTER before
// DIR_OPEN_ADAPTER. In that case it is simply a NOP
//
if (pCCB->uchDlcCommand != LLC_DIR_OPEN_ADAPTER
&& pCCB->uchDlcCommand != LLC_DIR_INITIALIZE) {
pCCB->uchDlcStatus = LLC_STATUS_ADAPTER_CLOSED;
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n",
pCCB->uchDlcStatus,
pCCB->uchDlcStatus
));
}
}
return pCCB->uchDlcStatus;
}
}
//
// Set the default input and output buffers and their sizes
//
IoctlCommand = DLC_IOCTL(pCCB->uchDlcCommand);
if (IoctlCommand != DLC_UNSUPPORTED_COMMAND) {
InputBufferSize = aDlcIoBuffers[((USHORT)IoctlCommand) >> 2].InputBufferSize;
OutputBufferLength = aDlcIoBuffers[((USHORT)IoctlCommand) >> 2].OutputBufferSize;
//
// Set the default input and output buffers.
//
if (OutputBufferLength != 0) {
pOutputBuffer = pOutputCcb;
} else {
pOutputBuffer = NULL;
}
} else {
OutputBufferLength = 0;
pOutputBuffer = NULL;
}
pInputBuffer = (PVOID)&NtDlcParms;
switch (pCCB->uchDlcCommand) {
case LLC_BUFFER_FREE:
//
// Copy the link list headers to the descriptor array
// and build NT CCB. Application may want to
// free more buffers at the time then allowed by
// by the maximum descriptor buffer size =>
// we must loop until the whole buffer list has been released
//
pFirstBuffer = pDlcParms->BufferFree.pFirstBuffer;
for ( pFirstBuffer = pDlcParms->BufferFree.pFirstBuffer,
DlcStatus = LLC_STATUS_SUCCESS ;
pFirstBuffer != NULL ; ) {
cElement = 0;
//
// We don't need to care about errors in the buffer chain,
// because the procedure automatically breaks all loops
// in the buffer chain
//
CopyToDescriptorBuffer(NtDlcParms.BufferFree.DlcBuffer,
pFirstBuffer,
TRUE, // DEALLOCATE_AFTER_USE
&cElement,
&pFirstBuffer,
&DlcStatus
);
NtDlcParms.BufferFree.BufferCount = (USHORT)cElement;
InputBufferSize = sizeof(LLC_TRANSMIT_DESCRIPTOR) * cElement
+ sizeof(NT_DLC_BUFFER_FREE_PARMS)
- sizeof(LLC_TRANSMIT_DESCRIPTOR);
DlcStatus = DoSyncDeviceIoControl(DriverHandle,
IoctlCommand,
&NtDlcParms,
InputBufferSize,
pDlcParms,
OutputBufferLength
);
}
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", DlcStatus, DlcStatus));
}
}
return DlcStatus;
case LLC_DIR_INITIALIZE:
//
// DIR.INITIALIZE is actually adapter close + hardware reset, but we
// must return OK status if the adapter is not opened. Apps should
// not reset the adapter without a good reason because it terminates
// all other network communication for a while, and may disconnect
// the sessions
//
RtlZeroMemory(pDlcParms, sizeof(LLC_DIR_INITIALIZE_PARMS));
if (DriverHandle == NULL) {
pCCB->uchDlcStatus = LLC_STATUS_SUCCESS;
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n",
pCCB->uchDlcStatus,
pCCB->uchDlcStatus
));
}
}
return pCCB->uchDlcStatus;
}
case LLC_DIR_CLOSE_ADAPTER:
//
// protect the close with the open critical section. We do this because
// we need to protect the driver against simultaneous closes on the same
// handle from multiple threads within the same process. The driver needs
// to be fixed ultimately
//
EnterCriticalSection(&AdapterOpenSection);
NtStatus = NtDeviceIoControlFile(DriverHandle,
EventHandle, // Event signaled when cmd compleletes
NULL,
NULL,
&GlobalIoStatus,
IOCTL_DLC_CLOSE_ADAPTER,
pCCB,
InputBufferSize,
pOutputCcb,
OutputBufferLength
);
if (NT_SUCCESS(NtStatus)) {
if ((NtStatus != STATUS_PENDING) && (pOutputCcb->uchDlcStatus == LLC_STATUS_PENDING)) {
//printf("ACSLAN: Success: DirCloseAdapter: DD returns %#x. Retcode=%#x\n",
// NtStatus, pOutputCcb->uchDlcStatus);
pOutputCcb->uchDlcStatus = (UCHAR)NtStatus;
//printf("ACSLAN: Success: DirCloseAdapter: DD returns %#x. Retcode=%#x\n",
// NtStatus, pOutputCcb->uchDlcStatus);
}
//
// it is safe to enter the handle array critical section whilst we
// are still holding the open critical section - this is the only
// code path that grabs both
//
EnterCriticalSection(&DriverHandlesCritSec);
aDlcDriverHandles[AdapterNumber] = NULL;
LeaveCriticalSection(&DriverHandlesCritSec);
//
// if the DirCloseAdapter IOCTL returns STATUS_PENDING, NtClose
// will block in the io-system until the close adapter IOCTL completes
//
NtClose(DriverHandle);
} else {
// printf("ACSLAN: Failure: DirCloseAdapter: DD returns %#x. Retcode=%#x\n",
// NtStatus, pOutputCcb->uchDlcStatus);
//
// RLF 04/21/94
//
// If we picked up a handle that has been subsequently closed by
// another thread, Io will return STATUS_INVALID_HANDLE. In this
// case, change the status code to LLC_STATUS_ADAPTER_CLOSED
//
if (NtStatus == STATUS_INVALID_HANDLE) {
pOutputCcb->uchDlcStatus = LLC_STATUS_ADAPTER_CLOSED;
}
}
LeaveCriticalSection(&AdapterOpenSection);
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n",
pCCB->uchDlcStatus,
pCCB->uchDlcStatus
));
}
}
return pCCB->uchDlcStatus;
case LLC_DIR_CLOSE_DIRECT:
pCCB->u.dlc.usStationId = 0;
//
// fall through
//
case LLC_DLC_CLOSE_STATION:
case LLC_DLC_CLOSE_SAP:
case LLC_DLC_RESET:
pInputBuffer = pCCB;
break;
case LLC_DIR_INTERRUPT:
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n",
LLC_STATUS_SUCCESS,
LLC_STATUS_SUCCESS
));
}
}
return LLC_STATUS_SUCCESS;
//
// define a few macros to make DIR.OPEN.ADAPTER code easier to read
//
//
// IO_PARMS - specifies DirOpenAdapter structure in NT_DLC_PARMS union for input
// to DLC device driver
//
#define IO_PARMS NtDlcParms.DirOpenAdapter
//
// OA_PARMS - specifies pointer to DIR.OPEN.ADAPTER parameter table which contains
// pointers to 4 other parameter tables
//
#define OA_PARMS pDlcParms->DirOpenAdapter
//
// EX_PARMS - specifies pointer to LLC_EXTENDED_ADAPTER_PARMS parameter table
//
#define EX_PARMS pDlcParms->DirOpenAdapter.pExtendedParms
//
// DLC_PARMS - specifies pointer to LLC_DLC_PARMS parameter table
//
#define DLC_PARMS pDlcParms->DirOpenAdapter.pDlcParms
case LLC_DIR_OPEN_ADAPTER:
//
// We can only open one adapter at a time. It is very hard to completely
// synchronize this in the driver
//
EnterCriticalSection(&AdapterOpenSection);
if (DriverHandle != NULL) {
DlcStatus = LLC_STATUS_ADAPTER_OPEN;
} else {
DlcStatus = OpenDlcApiDriver(EX_PARMS->pSecurityDescriptor, &DriverHandle);
}
if (DlcStatus == LLC_STATUS_SUCCESS) {
//
// We read the output to the original OS/2 CCB buffer,
// but it is too small for the complete NDIS adapter
// name => we will copy all input parameters to the NT CCB
//
pOutputBuffer = OA_PARMS.pAdapterParms;
//
// copy any input adapter parameters from the caller's
// LLC_ADAPTER_OPEN_PARMS to the device driver input buffer
//
RtlMoveMemory(&IO_PARMS.Adapter,
OA_PARMS.pAdapterParms,
sizeof(IO_PARMS.Adapter)
);
IO_PARMS.AdapterNumber = AdapterNumber;
//
// WE MUST CREATE NEW FIELD TO DEFINE, IF APPLICATION WANT
// TO USE DIX or 802.3 ethernet frames under 802.2
// (unnecessary feature, config parameter would be enough)
//
IO_PARMS.NtDlcIoctlVersion = NT_DLC_IOCTL_VERSION;
IO_PARMS.pSecurityDescriptor = EX_PARMS->pSecurityDescriptor;
IO_PARMS.hBufferPoolHandle = EX_PARMS->hBufferPool;
IO_PARMS.LlcEthernetType = EX_PARMS->LlcEthernetType;
IO_PARMS.NdisDeviceName.Buffer = (WCHAR *)IO_PARMS.Buffer;
IO_PARMS.NdisDeviceName.MaximumLength = sizeof(IO_PARMS.Buffer);
//
// get the configuration info from the registry
//
DlcStatus = GetAdapterNameAndParameters(
AdapterNumber % LLC_MAX_ADAPTERS,
&IO_PARMS.NdisDeviceName,
(PUCHAR)&IO_PARMS.LlcTicks,
&IO_PARMS.LlcEthernetType
);
if (DlcStatus == LLC_STATUS_SUCCESS) {
//
// copy the name buffer into the IO buffer and free the former
//
RtlMoveMemory(&IO_PARMS.Buffer,
IO_PARMS.NdisDeviceName.Buffer,
IO_PARMS.NdisDeviceName.Length
);
//
// ensure the name is actually zero-terminated for the call to
// RtlInitUnicodeString
//
IO_PARMS.Buffer[IO_PARMS.NdisDeviceName.Length/sizeof(WCHAR)] = 0;
//
// finished with UNICODE_STRING allocated in GetAdapterName...
//
RtlFreeUnicodeString(&IO_PARMS.NdisDeviceName);
//
// fill the UNICODE_STRING back in to point at our buffer
//
RtlInitUnicodeString(&IO_PARMS.NdisDeviceName, IO_PARMS.Buffer);
//
// now perform the actual open of the adapter for this process
//
DlcStatus = DoSyncDeviceIoControl(
DriverHandle,
IOCTL_DLC_OPEN_ADAPTER,
&NtDlcParms,
sizeof(NT_DIR_OPEN_ADAPTER_PARMS),
pOutputBuffer,
sizeof(LLC_ADAPTER_OPEN_PARMS)
);
}
if (DlcStatus == LLC_STATUS_SUCCESS) {
//
// get the timer tick values from the driver for this adapter
//
DlcStatus = DlcGetInfo(DriverHandle,
DLC_INFO_CLASS_DLC_TIMERS,
0,
&DLC_PARMS->uchT1_TickOne,
sizeof(LLC_TICKS)
);
//
// set the returned maxima to the default maxima as per the
// IBM LAN Tech. Ref.
//
DLC_PARMS->uchDlcMaxSaps = 127;
DLC_PARMS->uchDlcMaxStations = 255;
DLC_PARMS->uchDlcMaxGroupSaps = 126;
DLC_PARMS->uchDlcMaxGroupMembers = 127;
//
// this adapter is now successfully opened for this process
//
EnterCriticalSection(&DriverHandlesCritSec);
aDlcDriverHandles[AdapterNumber] = DriverHandle;
LeaveCriticalSection(&DriverHandlesCritSec);
}
}
LeaveCriticalSection(&AdapterOpenSection);
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_OUTPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", DlcStatus, DlcStatus));
}
}
return DlcStatus;
#undef IO_PARMS
#undef PO_PARMS
#undef EX_PARMS
#undef DLC_PARMS
case LLC_BUFFER_CREATE:
case LLC_BUFFER_GET:
case LLC_DIR_OPEN_DIRECT:
case LLC_DIR_SET_EXCEPTION_FLAGS:
case LLC_DLC_REALLOCATE_STATIONS:
//
// We can use the standard OS/2 CCB for input and output!
//
pOutputBuffer = pDlcParms;
pInputBuffer = pDlcParms;
break;
case LLC_DLC_STATISTICS:
//
// User may read either SAP or link statistics log
//
if ((NtDlcParms.DlcStatistics.usStationId & 0xff) == 0) {
InputBufferSize = sizeof(DLC_SAP_LOG);
} else {
InputBufferSize = sizeof(DLC_LINK_LOG);
}
if (pDlcParms->DlcStatistics.uchOptions & 0x80) {
InfoClass = DLC_INFO_CLASS_STATISTICS_RESET;
} else {
InfoClass = DLC_INFO_CLASS_STATISTICS;
}
DlcStatus = DlcGetInfo(DriverHandle,
InfoClass,
pDlcParms->DlcStatistics.usStationId,
NtDlcParms.DlcGetInformation.Info.Buffer,
InputBufferSize
);
if ((ULONG)pDlcParms->DlcStatistics.cbLogBufSize < InputBufferSize) {
InputBufferSize = (ULONG)pDlcParms->DlcStatistics.cbLogBufSize;
}
RtlMoveMemory(pDlcParms->DlcStatistics.pLogBuf,
NtDlcParms.DlcGetInformation.Info.Buffer,
InputBufferSize
);
if (DlcStatus == LLC_STATUS_SUCCESS
&& (ULONG)pDlcParms->DlcStatistics.cbLogBufSize < InputBufferSize) {
DlcStatus = LLC_STATUS_LOST_LOG_DATA;
}
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", DlcStatus, DlcStatus));
}
}
return DlcStatus;
case LLC_DIR_READ_LOG:
//
// We use two get info functions to read necessary stuff.
// Must must read even partial log buffer if user buffer
// is too small for the whole data (the user buffer could
// be even zero).
//
if (pDlcParms->DirReadLog.usTypeId > LLC_DIR_READ_LOG_BOTH) {
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n",
LLC_STATUS_INVALID_LOG_ID,
LLC_STATUS_INVALID_LOG_ID
));
}
}
return LLC_STATUS_INVALID_LOG_ID;
}
DlcStatus = STATUS_SUCCESS;
CopyLength = cbLogBuffer = pDlcParms->DirReadLog.cbLogBuffer;
pBuffer = (PUCHAR)pDlcParms->DirReadLog.pLogBuffer;
switch (pDlcParms->DirReadLog.usTypeId) {
case LLC_DIR_READ_LOG_BOTH:
case LLC_DIR_READ_LOG_ADAPTER:
if (DlcStatus == STATUS_SUCCESS) {
DlcStatus = DlcGetInfo(DriverHandle,
DLC_INFO_CLASS_ADAPTER_LOG,
0,
NtDlcParms.DlcGetInformation.Info.Buffer,
sizeof(LLC_ADAPTER_LOG)
);
}
if (cbLogBuffer > sizeof(LLC_ADAPTER_LOG)) {
CopyLength = sizeof(LLC_ADAPTER_LOG);
}
if (pDlcParms->DirReadLog.usTypeId == LLC_DIR_READ_LOG_BOTH) {
RtlMoveMemory(pBuffer,
NtDlcParms.DlcGetInformation.Info.Buffer,
CopyLength
);
cbLogBuffer -= CopyLength;
pBuffer += CopyLength;
CopyLength = cbLogBuffer;
DlcStatus = DlcGetInfo(DriverHandle,
DLC_INFO_CLASS_STATISTICS_RESET,
0,
NtDlcParms.DlcGetInformation.Info.Buffer,
sizeof(LLC_DIRECT_LOG)
);
if (cbLogBuffer > sizeof(LLC_DIRECT_LOG)) {
CopyLength = sizeof(LLC_DIRECT_LOG);
}
}
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", DlcStatus, DlcStatus));
}
}
return DlcStatus;
case LLC_DIR_READ_LOG_DIRECT:
DlcStatus = DlcGetInfo(DriverHandle,
DLC_INFO_CLASS_STATISTICS_RESET,
0,
NtDlcParms.DlcGetInformation.Info.Buffer,
sizeof(LLC_DIRECT_LOG)
);
if (cbLogBuffer > sizeof(LLC_DIRECT_LOG)) {
CopyLength = sizeof(LLC_DIRECT_LOG);
}
break;
}
RtlMoveMemory(pBuffer,
NtDlcParms.DlcGetInformation.Info.Buffer,
CopyLength
);
if (aMinDirLogSize[pDlcParms->DirReadLog.usTypeId] > pDlcParms->DirReadLog.cbLogBuffer) {
pDlcParms->DirReadLog.cbActualLength = aMinDirLogSize[pDlcParms->DirReadLog.usTypeId];
DlcStatus = LLC_STATUS_LOST_LOG_DATA;
}
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", DlcStatus, DlcStatus));
}
}
return DlcStatus;
case LLC_DIR_SET_FUNCTIONAL_ADDRESS:
if (pCCB->u.auchBuffer[0] & (UCHAR)0x80) {
InfoClass = DLC_INFO_CLASS_RESET_FUNCTIONAL;
} else {
InfoClass = DLC_INFO_CLASS_SET_FUNCTIONAL;
}
DlcStatus = DlcSetInfo(DriverHandle,
InfoClass,
0,
&NtDlcParms.DlcSetInformation,
pCCB->u.auchBuffer,
sizeof(TR_BROADCAST_ADDRESS)
);
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", DlcStatus, DlcStatus));
}
}
return DlcStatus;
case LLC_DIR_SET_GROUP_ADDRESS:
return DlcSetInfo(DriverHandle,
DLC_INFO_CLASS_SET_GROUP,
0,
&NtDlcParms.DlcSetInformation,
pCCB->u.auchBuffer,
sizeof(TR_BROADCAST_ADDRESS)
);
case LLC_DIR_SET_MULTICAST_ADDRESS:
return DlcSetInfo(DriverHandle,
DLC_INFO_CLASS_SET_MULTICAST,
0,
&NtDlcParms.DlcSetInformation,
pCCB->u.pParameterTable,
sizeof(LLC_DIR_MULTICAST_ADDRESS)
);
case LLC_DIR_STATUS:
//
// We will generic DlcGetInfo to read the status info.
// some parameters must be moved ot correct places.
//
RtlZeroMemory(pDlcParms, sizeof(LLC_DIR_STATUS_PARMS));
DlcStatus = DlcGetInfo(DriverHandle,
DLC_INFO_CLASS_DIR_ADAPTER,
0,
&NtDlcParms.DlcGetInformation.Info.DirAdapter,
sizeof(LLC_ADAPTER_INFO)
);
if (DlcStatus != LLC_STATUS_SUCCESS) {
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", DlcStatus, DlcStatus));
}
}
return DlcStatus;
}
RtlMoveMemory(pDlcParms->DirStatus.auchNodeAddress,
&NtDlcParms.DlcGetInformation.Info.DirAdapter,
sizeof(LLC_ADAPTER_INFO)
);
pDlcParms->DirStatus.usAdapterType =
NtDlcParms.DlcGetInformation.Info.DirAdapter.usAdapterType;
//
// Set the adapter config flags, the only thing we actually
// can know, if the current link speed on the adapter.
// In the other fields we just use the default values.
// Keep the bit defining extended DOS parameters unchanged,
// but all other bits may be changed.
//
pDlcParms->DirStatus.uchAdapterConfig &= ~0x20; // DOS extended parms
if (NtDlcParms.DlcGetInformation.Info.DirAdapter.ulLinkSpeed ==
TR_16Mbps_LINK_SPEED) {
pDlcParms->DirStatus.uchAdapterConfig |=
0x10 | // early release token
0x0c | // 64 kB RAM on a 4/16 IBM token-ring adapter
0x01; // adapter rate is 16 Mbps
} else {
pDlcParms->DirStatus.uchAdapterConfig |=
0x0c; // 64 kB RAM on adapter
}
DlcStatus = DlcGetInfo(DriverHandle,
DLC_INFO_CLASS_PERMANENT_ADDRESS,
0,
pDlcParms->DirStatus.auchPermanentAddress,
6
);
if (DlcStatus != LLC_STATUS_SUCCESS) {
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", DlcStatus, DlcStatus));
}
}
return DlcStatus;
}
DlcStatus = DlcGetInfo(DriverHandle,
DLC_INFO_CLASS_DLC_ADAPTER,
0,
&pDlcParms->DirStatus.uchMaxSap,
sizeof(struct _DlcAdapterInfoGet)
);
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", DlcStatus, DlcStatus));
}
}
return DlcStatus;
case LLC_READ_CANCEL:
case LLC_DIR_TIMER_CANCEL:
case LLC_RECEIVE_CANCEL:
//
// Copy pointer of the cancelled command to the
// byte aligned output buffer.
//
NtDlcParms.DlcCancelCommand.CcbAddress = (PVOID)pDlcParms;
//SmbPutUlong(&pOutputCcb->pNext, (ULONG_PTR) pDlcParms);
pOutputCcb->pNext = (PVOID) pDlcParms;
break;
case LLC_DIR_TIMER_CANCEL_GROUP:
case LLC_DIR_TIMER_SET:
pInputBuffer = pCCB;
break;
case LLC_DLC_CONNECT_STATION:
NtDlcParms.Async.Ccb = *(PNT_DLC_CCB)pCCB;
NtDlcParms.Async.Parms.DlcConnectStation.StationId = pDlcParms->DlcConnectStation.usStationId;
if (pDlcParms->DlcConnectStation.pRoutingInfo != NULL) {
NtDlcParms.Async.Parms.DlcConnectStation.RoutingInformationLength = *pDlcParms->DlcConnectStation.pRoutingInfo & (UCHAR)0x1f;
RtlMoveMemory(NtDlcParms.Async.Parms.DlcConnectStation.aRoutingInformation,
pDlcParms->DlcConnectStation.pRoutingInfo,
NtDlcParms.Async.Parms.DlcConnectStation.RoutingInformationLength
);
} else {
NtDlcParms.Async.Parms.DlcConnectStation.RoutingInformationLength=0;
}
break;
case LLC_DOS_DLC_FLOW_CONTROL:
//
// This is an official entry to DlcFlowControl used by
// VDM DLC support DLL to set a link buffer busy state.
//
NtDlcParms.DlcFlowControl.FlowControlOption = (UCHAR)pCCB->u.dlc.usParameter;
NtDlcParms.DlcFlowControl.StationId = pCCB->u.dlc.usStationId;
break;
case LLC_DLC_FLOW_CONTROL:
//
// This is the official entry to DlcFlowControl
//
NtDlcParms.DlcFlowControl.FlowControlOption = (UCHAR)(pCCB->u.dlc.usParameter & LLC_VALID_FLOW_CONTROL_BITS);
NtDlcParms.DlcFlowControl.StationId = pCCB->u.dlc.usStationId;
break;
case LLC_DLC_MODIFY:
RtlMoveMemory(&NtDlcParms.DlcSetInformation.Info.LinkStation,
&pDlcParms->DlcModify.uchT1,
sizeof(DLC_LINK_PARAMETERS)
);
NtDlcParms.DlcSetInformation.Info.LinkStation.TokenRingAccessPriority = pDlcParms->DlcModify.uchAccessPriority;
//
// This is a non-standard extension: DlcModify returns
// the maximum allowed information field lentgh for a link station.
// (it depends on length of source routing and bridges
// between two stations).
//
if ((pDlcParms->DlcModify.usStationId & 0x00ff) != 0) {
DlcStatus = DlcGetInfo(DriverHandle,
DLC_INFO_CLASS_LINK_STATION,
pDlcParms->DlcModify.usStationId,
&pDlcParms->DlcModify.usMaxInfoFieldLength,
sizeof(USHORT)
);
}
DlcStatus = DlcSetInfo(DriverHandle,
DLC_INFO_CLASS_LINK_STATION,
pDlcParms->DlcModify.usStationId,
&NtDlcParms.DlcSetInformation,
NULL,
sizeof(DLC_LINK_PARAMETERS)
);
//
// Set the group information, if there is any
//
if (DlcStatus == LLC_STATUS_SUCCESS && pDlcParms->DlcModify.cGroupCount != 0) {
NtDlcParms.DlcSetInformation.Info.Sap.GroupCount = pDlcParms->DlcModify.cGroupCount;
RtlMoveMemory(NtDlcParms.DlcSetInformation.Info.Sap.GroupList,
pDlcParms->DlcModify.pGroupList,
pDlcParms->DlcModify.cGroupCount
);
DlcStatus = DlcSetInfo(DriverHandle,
DLC_INFO_CLASS_GROUP,
pDlcParms->DlcModify.usStationId,
&NtDlcParms.DlcSetInformation,
NULL,
sizeof(struct _DlcSapInfoSet)
);
}
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", DlcStatus, DlcStatus));
}
}
return DlcStatus;
case LLC_DLC_OPEN_SAP:
//
// DlcOpenSap uses the original OS/2 CCB, but it has to modify a couple
// fields. There is a separate call to setup the group SAPS because
// they cannot use the original CCB parameter table (it's a pointer)
//
pNtParms = (PNT_DLC_PARMS)pDlcParms;
pNtParms->DlcOpenSap.LinkParameters.TokenRingAccessPriority = pDlcParms->DlcOpenSap.uchOptionsPriority & (UCHAR)0x1F;
DlcStatus = DoSyncDeviceIoControl(DriverHandle,
IOCTL_DLC_OPEN_SAP,
pNtParms,
sizeof(NT_DLC_OPEN_SAP_PARMS),
pNtParms,
sizeof(NT_DLC_OPEN_SAP_PARMS)
);
if (DlcStatus != LLC_STATUS_SUCCESS) {
pOutputCcb->uchDlcStatus = (UCHAR)DlcStatus;
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", DlcStatus, DlcStatus));
}
}
return DlcStatus;
}
//
// Check if there is defined any group saps
//
if (pDlcParms->DlcOpenSap.cGroupCount != 0) {
NtDlcParms.DlcSetInformation.Info.Sap.GroupCount = pDlcParms->DlcOpenSap.cGroupCount;
RtlMoveMemory(&NtDlcParms.DlcSetInformation.Info.Sap.GroupList,
pDlcParms->DlcOpenSap.pGroupList,
pDlcParms->DlcOpenSap.cGroupCount
);
DlcStatus = DlcSetInfo(DriverHandle,
DLC_INFO_CLASS_GROUP,
pDlcParms->DlcOpenSap.usStationId,
&NtDlcParms.DlcSetInformation,
NULL,
sizeof(struct _DlcSapInfoSet)
);
}
pOutputCcb->uchDlcStatus = (UCHAR)DlcStatus;
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", DlcStatus, DlcStatus));
}
}
return DlcStatus;
case LLC_DLC_OPEN_STATION:
NtDlcParms.DlcOpenStation.RemoteSap = pDlcParms->DlcOpenStation.uchRemoteSap;
NtDlcParms.DlcOpenStation.LinkStationId = pDlcParms->DlcOpenStation.usSapStationId;
RtlMoveMemory(NtDlcParms.DlcOpenStation.aRemoteNodeAddress,
pDlcParms->DlcOpenStation.pRemoteNodeAddress,
6
);
RtlMoveMemory(&NtDlcParms.DlcOpenStation.LinkParameters,
&pDlcParms->DlcOpenStation.uchT1,
sizeof(DLC_LINK_PARAMETERS)
);
NtDlcParms.DlcOpenStation.LinkParameters.TokenRingAccessPriority = pDlcParms->DlcOpenStation.uchAccessPriority;
pOutputBuffer = &pDlcParms->DlcOpenStation.usLinkStationId;
break;
case LLC_READ:
#ifdef GRAB_READ
if (pCCB->hCompletionEvent) {
PREAD_GRABBER pGrabberStruct;
pGrabberStruct = (PREAD_GRABBER)LocalAlloc(LMEM_FIXED, sizeof(READ_GRABBER));
pGrabberStruct->pCcb = pCCB;
pGrabberStruct->OriginalEventHandle = pCCB->hCompletionEvent;
pGrabberStruct->NewEventHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
EventHandle = pGrabberStruct->NewEventHandle;
AddReadGrabber(pGrabberStruct);
} else {
OutputDebugString(L"NtAcsLan: LLC_READ with no event!\n");
}
#endif
//
// IOCTL_DLC_READ have two output buffer, one for CCB and another
// for the actual data. IOCTL_DLC_READ2 is a read request, that
// have the second outbut buffer immediately after the firt one =>
// we don't need to lock, map, copy, unmap and unlock the second
// output buffer. The same thing has been implemented for receive.
//
if (pDlcParms != NULL && pDlcParms != (PVOID)&pCCB[1]) {
OutputBufferLength = sizeof(NT_DLC_CCB_OUTPUT);
NtDlcParms.Async.Ccb = *(PNT_DLC_CCB)pCCB;
NtDlcParms.Async.Parms.ReadInput = *(PNT_DLC_READ_INPUT)pDlcParms;
} else {
IoctlCommand = IOCTL_DLC_READ2;
OutputBufferLength = sizeof(NT_DLC_READ_PARMS) + sizeof(LLC_CCB);
pInputBuffer = pCCB;
}
break;
case LLC_RECEIVE:
OutputBufferLength = sizeof(NT_DLC_CCB_OUTPUT);
if (pDlcParms != NULL && pDlcParms != (PVOID)&pCCB[1]) {
NtDlcParms.Async.Ccb = *(PNT_DLC_CCB)pCCB;
NtDlcParms.Async.Parms.Receive = pDlcParms->Receive;
//
// We don't actually receive any data with receive command,
// if the receive flag is set.
//
if (NtDlcParms.Async.Parms.Receive.ulReceiveFlag != 0) {
IoctlCommand = IOCTL_DLC_RECEIVE2;
}
} else {
IoctlCommand = IOCTL_DLC_RECEIVE2;
pInputBuffer = pCCB;
}
break;
case LLC_TRANSMIT_DIR_FRAME:
FrameType = LLC_DIRECT_TRANSMIT;
goto TransmitHandling;
case LLC_TRANSMIT_UI_FRAME:
FrameType = LLC_UI_FRAME;
goto TransmitHandling;
case LLC_TRANSMIT_XID_CMD:
FrameType = LLC_XID_COMMAND_POLL;
goto TransmitHandling;
case LLC_TRANSMIT_XID_RESP_FINAL:
FrameType = LLC_XID_RESPONSE_FINAL;
goto TransmitHandling;
case LLC_TRANSMIT_XID_RESP_NOT_FINAL:
FrameType = LLC_XID_RESPONSE_NOT_FINAL;
goto TransmitHandling;
case LLC_TRANSMIT_TEST_CMD:
FrameType = LLC_TEST_COMMAND_POLL;
goto TransmitHandling;
case LLC_TRANSMIT_I_FRAME:
FrameType = LLC_I_FRAME;
TransmitHandling:
//
// Copy the link list headers to the descriptor array and build NT CCB.
// (BUG-BUG-BUG: We should implement the send of multiple frames.
// loop CCB chain as far as the same CCB command and transmit commands
// completed with READ)
//
OutputBufferLength = sizeof(NT_DLC_CCB_OUTPUT);
//
// This stuff is for DOS DLC, the transmit parameter table may have been
// copied if it was unaligned
//
//SmbPutUlong((PULONG)&pOutputCcb->pNext, (ULONG)pOriginalCcbAddress);
pOutputCcb->pNext = (PVOID) pOriginalCcbAddress;
RtlMoveMemory((PUCHAR)&NtDlcParms.Async.Ccb, (PUCHAR)pOutputCcb, sizeof(NT_DLC_CCB));
pOutputCcb->uchDlcStatus = (UCHAR)LLC_STATUS_PENDING;
NtDlcParms.Async.Parms.Transmit.FrameType = FrameType;
NtDlcParms.Async.Parms.Transmit.StationId = pDlcParms->Transmit.usStationId;
NtDlcParms.Async.Parms.Transmit.RemoteSap = pDlcParms->Transmit.uchRemoteSap;
NtDlcParms.Async.Parms.Transmit.XmitReadOption = pDlcParms->Transmit.uchXmitReadOption;
cElement = 0;
if (pDlcParms->Transmit.pXmitQueue1 != NULL) {
CopyToDescriptorBuffer(NtDlcParms.Async.Parms.Transmit.XmitBuffer,
pDlcParms->Transmit.pXmitQueue1,
FALSE, // DO_NOT_DEALLOCATE
&cElement,
&pFirstBuffer,
&DlcStatus
);
if (DlcStatus != STATUS_SUCCESS) {
pCCB->uchDlcStatus = (UCHAR)DlcStatus;
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", LLC_STATUS_PENDING, LLC_STATUS_PENDING));
}
}
return LLC_STATUS_PENDING;
}
}
if (pDlcParms->Transmit.pXmitQueue2 != NULL) {
CopyToDescriptorBuffer(NtDlcParms.Async.Parms.Transmit.XmitBuffer,
pDlcParms->Transmit.pXmitQueue2,
TRUE, // DEALLOCATE_AFTER_USE
&cElement,
&pFirstBuffer,
&DlcStatus
);
//
// The Queue2 pointer must be reset always.
// This doesn't work for DOS DLC buffers, but it does not
// matter, because this feature is not needed by VDM DLC
// (we cannot access pOutputCcb or its parameter block,
// because they may be unaligned)
//
pDlcParms->Transmit.pXmitQueue2 = NULL;
if (DlcStatus != STATUS_SUCCESS) {
pCCB->uchDlcStatus = (UCHAR)DlcStatus;
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", LLC_STATUS_PENDING, LLC_STATUS_PENDING));
}
}
return LLC_STATUS_PENDING;
}
}
if (pDlcParms->Transmit.cbBuffer1 != 0) {
if (cElement == MAX_TRANSMIT_SEGMENTS) {
pCCB->uchDlcStatus = LLC_STATUS_TRANSMIT_ERROR;
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", LLC_STATUS_PENDING, LLC_STATUS_PENDING));
}
}
return LLC_STATUS_PENDING;
}
NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].pBuffer = pDlcParms->Transmit.pBuffer1;
NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].cbBuffer = pDlcParms->Transmit.cbBuffer1;
NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].boolFreeBuffer = FALSE;
NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].eSegmentType = LLC_NEXT_DATA_SEGMENT;
cElement++;
}
if (pDlcParms->Transmit.cbBuffer2 != 0) {
if (cElement == MAX_TRANSMIT_SEGMENTS) {
pCCB->uchDlcStatus = LLC_STATUS_TRANSMIT_ERROR;
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_INPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", LLC_STATUS_PENDING, LLC_STATUS_PENDING));
}
}
return LLC_STATUS_PENDING;
}
NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].pBuffer = pDlcParms->Transmit.pBuffer2;
NtDlcParms.Async.Parms.Transmit.XmitBuffer[cElement].cbBuffer = pDlcParms->Transmit.cbBuffer2;
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);
break;
//
// Multiple frame transmit:
// - atomic operation: single error => all are discarded
// by error, but some successful packets may have been sent
// after the unsuccessful one.
// - No DLC frame headers are included
// - LAN header must always be in the first buffer,
// - 3 dwords reserved for DLC in the beginning.
// good: provides the minimal system overhead
// bad: error handling may be difficult in some cases
// new data link operation:
// cancel packets by request handle, called when an
// error has occurred (this would require a new
// one level request handle)
//
case LLC_TRANSMIT_FRAMES:
//
// We must copy the actual CCB to the parameter table only if
// the CCB is not allocated within the transmit command structure
//
if (&pDlcParms->Transmit2.Ccb != pCCB) {
pDlcParms->Transmit2.Ccb = *pCCB;
}
pInputBuffer = pDlcParms;
InputBufferSize = (sizeof(LLC_TRANSMIT_DESCRIPTOR)
* (UINT)pDlcParms->Transmit2.cXmitBufferCount)
+ sizeof(LLC_TRANSMIT2_COMMAND)
- sizeof(LLC_TRANSMIT_DESCRIPTOR);
break;
default:
return LLC_STATUS_INVALID_COMMAND;
}
NtStatus = NtDeviceIoControlFile(DriverHandle,
EventHandle, // Event signaled when cmd completes
NULL, // no APC routine
NULL, // no context for APC
&GlobalIoStatus, // global I/O status block
IoctlCommand, // map DLC cmd codes to Nt IoCtl codes
pInputBuffer,
InputBufferSize,
pOutputBuffer,
OutputBufferLength
);
//
// The io-completion directly updates Status and next CCB pointer
// of this CCB when the main function return status pending.
// If the status code is non-pending (error or ok), then we
// must save the status code to CCB and reset next CCB pointer.
//
if (NtStatus != STATUS_PENDING) {
//
// Reset the next pointer if the command is still linked to itself.
// For example the cancel command returns a pointer to the cancelled
// CCB in the next CCB pointer (pNext)
//
if (pCCB->pNext == pOutputCcb) {
pCCB->pNext = NULL;
}
pCCB->uchDlcStatus = (UCHAR)NtStatus;
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_OUTPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", pCCB->uchDlcStatus, pCCB->uchDlcStatus));
}
}
#if DBG
if (pOutputCcb->uchDlcStatus == 0xA1) {
OutputDebugString(TEXT("NtAcsLan returning 0xA1\n"));
//DebugBreak();
}
if (pOutputCcb->uchDlcCommand == LLC_TRANSMIT_I_FRAME && pOutputCcb->uchDlcStatus != LLC_STATUS_SUCCESS) {
WCHAR buf[80];
wsprintf(buf, TEXT("NtAcsLan: I-Frame returning %#02x\n"), pOutputCcb->uchDlcStatus);
OutputDebugString(buf);
}
if (pCCB->uchDlcStatus != pOutputCcb->uchDlcStatus) {
WCHAR buf[80];
wsprintf(buf, TEXT("NtAcsLan: pCCB->uchDlcStatus = %#02x; pOutputCcb->uchDlcStatus = %#02x\n"),
pCCB->uchDlcStatus,
pOutputCcb->uchDlcStatus
);
OutputDebugString(buf);
}
#endif
return pCCB->uchDlcStatus;
} else {
IF_DEBUG(DUMP_NTACSLAN) {
IF_DEBUG(DUMP_OUTPUT_CCB) {
DUMPCCB(pCCB, TRUE, FALSE);
}
IF_DEBUG(RETURN_CODE) {
PUT(("NtAcsLan: returning %d [0x%x]\n", LLC_STATUS_PENDING, LLC_STATUS_PENDING));
}
}
return LLC_STATUS_PENDING;
}
}
USHORT
GetCcbStationId(
IN PLLC_CCB pCCB
)
/*++
Routine Description:
The function returns the station id used by the given ccb.
-1 is returned, if the command didn't have any station id.
Arguments:
pCCB - OS/2 DLC Command control block
Return Value:
Station Id
-1 No station id
--*/
{
switch (pCCB->uchDlcCommand) {
case LLC_BUFFER_FREE:
case LLC_BUFFER_CREATE:
case LLC_BUFFER_GET:
case LLC_DLC_REALLOCATE_STATIONS:
case LLC_DLC_STATISTICS:
case LLC_READ:
case LLC_RECEIVE:
case LLC_TRANSMIT_DIR_FRAME:
case LLC_TRANSMIT_UI_FRAME:
case LLC_TRANSMIT_XID_CMD:
case LLC_TRANSMIT_XID_RESP_FINAL:
case LLC_TRANSMIT_XID_RESP_NOT_FINAL:
case LLC_TRANSMIT_TEST_CMD:
case LLC_TRANSMIT_I_FRAME:
case LLC_TRANSMIT_FRAMES:
case LLC_DLC_CONNECT_STATION:
case LLC_DLC_MODIFY:
return pCCB->u.pParameterTable->DlcModify.usStationId;
case LLC_DLC_FLOW_CONTROL:
case LLC_DLC_CLOSE_STATION:
case LLC_DLC_CLOSE_SAP:
case LLC_DIR_CLOSE_DIRECT:
case LLC_DLC_RESET:
return pCCB->u.dlc.usStationId;
default:
return (USHORT)-1;
}
}
LLC_STATUS
OpenDlcApiDriver(
IN PVOID pSecurityDescriptor,
OUT HANDLE* pHandle
)
/*++
Routine Description:
Opens a handle to the DLC driver
Arguments:
pSecurityDescriptor - pointer to security descriptor
pHandle - pointer to returned handle if success
Return Value:
LLC_STATUS
Success - LLC_STATUS_SUCCESS
Failure - LLC_STATUS_DEVICE_DRIVER_NOT_INSTALLED
--*/
{
IO_STATUS_BLOCK iosb;
OBJECT_ATTRIBUTES objattr;
UNICODE_STRING DriverName;
NTSTATUS Status;
RtlInitUnicodeString(&DriverName, DD_DLC_DEVICE_NAME);
InitializeObjectAttributes(
&objattr, // obj attr to initialize
&DriverName, // string to use
OBJ_CASE_INSENSITIVE, // Attributes
NULL, // Root directory
pSecurityDescriptor // Security Descriptor
);
Status = NtCreateFile(
pHandle, // ptr to handle
GENERIC_READ // desired...
| GENERIC_WRITE, // ...access
&objattr, // name & attributes
&iosb, // I/O status block.
NULL, // alloc size.
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_DELETE // share...
| FILE_SHARE_READ
| FILE_SHARE_WRITE, // ...access
FILE_OPEN_IF, // create disposition
0, // ...options
NULL, // EA buffer
0L // Ea buffer len
);
if (Status != STATUS_SUCCESS) {
return LLC_STATUS_DEVICE_DRIVER_NOT_INSTALLED;
}
return LLC_STATUS_SUCCESS;
}
LLC_STATUS
GetAdapterNameAndParameters(
IN UINT AdapterNumber,
OUT PUNICODE_STRING pNdisName,
OUT PUCHAR pTicks,
OUT PLLC_ETHERNET_TYPE pLlcEthernetType
)
/*++
Routine Description:
Get the adapter mapping for AdapterNumber from the registry. Also, get the
Ethernet type and
Arguments:
AdapterNumber - DLC adapter number (0, 1, 2 ... 15)
pNdisName - the returned unicode name string
pTicks -
pLlcEthernetType -
Return Value:
LLC_STATUS
--*/
{
LLC_STATUS llcStatus;
LONG regStatus;
HKEY hkey;
static LPTSTR subkey = TEXT("System\\CurrentControlSet\\Services\\Dlc\\Linkage");
regStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
subkey,
0,
KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE,
&hkey
);
if (regStatus == ERROR_SUCCESS) {
DWORD type;
DWORD dataSize;
LPTSTR buffer;
LPTSTR tempbuffer;
//
// here we try to get all the info in one go from the registry. If
// the "Bind" value has grown to more than 1024 bytes, then we must
// try to reallocate the buffer and try again. If it fails a second
// time, then give up
//
buffer = (LPTSTR)LocalAlloc(LMEM_FIXED, DEFAULT_QUERY_BUFFER_LENGTH);
if (buffer) {
dataSize = DEFAULT_QUERY_BUFFER_LENGTH;
regStatus = RegQueryValueEx(hkey,
TEXT("Bind"),
NULL, // lpdwReserved
&type,
(LPBYTE)buffer,
&dataSize
);
if (regStatus == ERROR_SUCCESS || regStatus == ERROR_MORE_DATA) {
llcStatus = LLC_STATUS_SUCCESS;
//
// This code not tested - Realloc don't work
//
if (dataSize > DEFAULT_QUERY_BUFFER_LENGTH) {
DWORD oldSize;
//
// more available than I anticipated. Try growing the buffer.
// Add an extra DEFAULT_BINDING_LENGTH in case somebody's
// adding to this entry whilst we're reading it (unlikely)
//
oldSize = dataSize;
dataSize += DEFAULT_BINDING_LENGTH;
tempbuffer = buffer;
buffer = (LPTSTR)LocalReAlloc((HLOCAL)buffer, dataSize, 0);
if (buffer) {
regStatus = RegQueryValueEx(hkey,
subkey,
NULL, // lpdwReserved
&type,
(LPBYTE)buffer,
&dataSize
);
if (regStatus != ERROR_SUCCESS) {
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
} else if (dataSize > oldSize) {
//
// data has grown since last call? Bogus?
//
llcStatus = LLC_STATUS_NO_MEMORY;
}
} else {
LocalFree(tempbuffer);
//
// Is this error code acceptable in this circumstance?
//
llcStatus = LLC_STATUS_NO_MEMORY;
}
}
if (llcStatus == LLC_STATUS_SUCCESS) {
//
// we managed to read something from the registry. Try to
// locate our adapter. The returned data is wide-character
// strings (better be, lets check the type first)
//
if (type == REG_MULTI_SZ) {
DWORD i;
LPTSTR pBinding = buffer;
for (i = 0; i != AdapterNumber; ++i) {
pBinding = wcschr(pBinding, L'\0') + 1;
if (!*pBinding) {
break;
}
}
//
// if there is a binding corresponding to this adapter
// number (e.g. \Device\IbmTok01) then make a copy of
// the string and make it into a UNICODE_STRING. The
// caller uses RtlFreeUnicodeString
//
// Does RtlFreeUnicodeString know that I used
// LocalAlloc to allocate the string?
//
if (*pBinding) {
LPTSTR bindingName;
bindingName = (LPTSTR)LocalAlloc(
LMEM_FIXED,
(wcslen(pBinding) + 1)
* sizeof(WCHAR)
);
if (bindingName) {
wcscpy(bindingName, pBinding);
RtlInitUnicodeString(pNdisName, bindingName);
//#if DBG
// DbgPrint("DLCAPI.DLL: Adapter %d maps to %ws\n",
// AdapterNumber,
// pBinding
// );
//#endif
} else {
llcStatus = LLC_STATUS_NO_MEMORY;
}
} else {
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
}
} else {
//
// unexpected type in registry
//
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
}
}
} else {
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
}
if (buffer) {
LocalFree(buffer);
}
//
// for now, default the ticks and ethernet type
//
RtlZeroMemory(pTicks, sizeof(LLC_TICKS));
//
// if the app passed in anything other than those values we
// recognize, convert to AUTO.
//
// Note: we should really return an error (invalid parameter) in
// this case, since it means the app is passing in a bad value,
// but at this late stage, it is better to accept invalid input
// and default it than to risk an app/printer monitor stopping
// working (RLF 05/10/93)
//
if (*pLlcEthernetType != LLC_ETHERNET_TYPE_AUTO
&& *pLlcEthernetType != LLC_ETHERNET_TYPE_DEFAULT
&& *pLlcEthernetType != LLC_ETHERNET_TYPE_DIX
&& *pLlcEthernetType != LLC_ETHERNET_TYPE_802_3) {
*pLlcEthernetType = LLC_ETHERNET_TYPE_AUTO;
}
} else {
//
// Is this error code acceptable in this circumstance?
//
llcStatus = LLC_STATUS_NO_MEMORY;
}
RegCloseKey(hkey);
} else {
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
}
return llcStatus;
}
LLC_STATUS
GetAdapterNameFromNumber(
IN UINT AdapterNumber,
OUT LPTSTR pNdisName
)
/*++
Routine Description:
Get the adapter name mapping for AdapterNumber from the registry.
Arguments:
AdapterNumber - DLC adapter number (0, 1, 2 ... 15)
pNdisName - the returned zero-terminated wide character string
Return Value:
LLC_STATUS
--*/
{
LLC_STATUS llcStatus;
LONG regStatus;
HKEY hkey;
static LPTSTR subkey = TEXT("System\\CurrentControlSet\\Services\\Dlc\\Linkage");
regStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
subkey,
0,
KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE,
&hkey
);
if (regStatus == ERROR_SUCCESS) {
DWORD type;
DWORD dataSize;
LPTSTR buffer;
//
// here we try to get all the info in one go from the registry. If
// the "Bind" value has grown to more than 1024 bytes, then we must
// try to reallocate the buffer and try again. If it fails a second
// time, then give up
//
buffer = (LPTSTR)LocalAlloc(LMEM_FIXED, DEFAULT_QUERY_BUFFER_LENGTH);
if (buffer) {
dataSize = DEFAULT_QUERY_BUFFER_LENGTH;
regStatus = RegQueryValueEx(hkey,
TEXT("Bind"),
NULL, // lpdwReserved
&type,
(LPBYTE)buffer,
&dataSize
);
if (regStatus == ERROR_SUCCESS || regStatus == ERROR_MORE_DATA) {
llcStatus = LLC_STATUS_SUCCESS;
//
// this code not tested - Realloc don't work
//
if (dataSize > DEFAULT_QUERY_BUFFER_LENGTH) {
DWORD oldSize;
//
// more available than I anticipated. Try growing the buffer.
// Add an extra DEFAULT_BINDING_LENGTH in case somebody's
// adding to this entry whilst we're reading it (unlikely)
//
oldSize = dataSize;
dataSize += DEFAULT_BINDING_LENGTH;
buffer = (LPTSTR)LocalReAlloc((HLOCAL)buffer, dataSize, 0);
if (buffer) {
regStatus = RegQueryValueEx(hkey,
subkey,
NULL, // lpdwReserved
&type,
(LPBYTE)buffer,
&dataSize
);
if (regStatus != ERROR_SUCCESS) {
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
} else if (dataSize > oldSize) {
//
// data has grown since last call? Bogus?
//
llcStatus = LLC_STATUS_NO_MEMORY;
}
} else {
//
// Is this error code acceptable in this circumstance?
//
llcStatus = LLC_STATUS_NO_MEMORY;
}
}
if (llcStatus == LLC_STATUS_SUCCESS) {
//
// we managed to read something from the registry. Try to
// locate our adapter. The returned data is wide-character
// strings (better be, lets check the type first)
//
if (type == REG_MULTI_SZ) {
DWORD i;
LPTSTR pBinding = buffer;
for (i = 0; i != AdapterNumber; ++i) {
pBinding = wcschr(pBinding, L'\0') + 1;
if (!*pBinding) {
break;
}
}
//
// if there is a binding corresponding to this adapter
// number (e.g. \Device\IbmTok01)
if (*pBinding) {
wcscpy(pNdisName, pBinding);
} else {
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
}
} else {
//
// unexpected type in registry
//
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
}
}
} else {
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
}
LocalFree(buffer);
}
else {
llcStatus = LLC_STATUS_NO_MEMORY;
}
RegCloseKey(hkey);
} else {
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
}
return llcStatus;
}
LLC_STATUS
GetAdapterNumberFromName(
IN LPTSTR pNdisName,
OUT UINT *AdapterNumber
)
/*++
Routine Description:
Get the adapter number mapping for AdapterName from the registry.
Arguments:
pNdisName - zero-terminated wide character string
AdapterNumber - returned DLC adapter number (0, 1, 2 ... 15)
Return Value:
LLC_STATUS
--*/
{
LLC_STATUS llcStatus;
LONG regStatus;
HKEY hkey;
static LPTSTR subkey = TEXT("System\\CurrentControlSet\\Services\\Dlc\\Linkage");
regStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
subkey,
0,
KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE,
&hkey
);
if (regStatus == ERROR_SUCCESS) {
DWORD type;
DWORD dataSize;
LPTSTR buffer;
//
// here we try to get all the info in one go from the registry. If
// the "Bind" value has grown to more than 1024 bytes, then we must
// try to reallocate the buffer and try again. If it fails a second
// time, then give up
//
buffer = (LPTSTR)LocalAlloc(LMEM_FIXED, DEFAULT_QUERY_BUFFER_LENGTH);
if (buffer) {
dataSize = DEFAULT_QUERY_BUFFER_LENGTH;
regStatus = RegQueryValueEx(hkey,
TEXT("Bind"),
NULL, // lpdwReserved
&type,
(LPBYTE)buffer,
&dataSize
);
if (regStatus == ERROR_SUCCESS || regStatus == ERROR_MORE_DATA) {
llcStatus = LLC_STATUS_SUCCESS;
//
// this code not tested - Realloc don't work
//
if (dataSize > DEFAULT_QUERY_BUFFER_LENGTH) {
DWORD oldSize;
//
// more available than I anticipated. Try growing the buffer.
// Add an extra DEFAULT_BINDING_LENGTH in case somebody's
// adding to this entry whilst we're reading it (unlikely)
//
oldSize = dataSize;
dataSize += DEFAULT_BINDING_LENGTH;
buffer = (LPTSTR)LocalReAlloc((HLOCAL)buffer, dataSize, 0);
if (buffer) {
regStatus = RegQueryValueEx(hkey,
subkey,
NULL, // lpdwReserved
&type,
(LPBYTE)buffer,
&dataSize
);
if (regStatus != ERROR_SUCCESS) {
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
} else if (dataSize > oldSize) {
//
// data has grown since last call? Bogus?
//
llcStatus = LLC_STATUS_NO_MEMORY;
}
} else {
//
// is this error code acceptable in this circumstance?
//
llcStatus = LLC_STATUS_NO_MEMORY;
}
}
if (llcStatus == LLC_STATUS_SUCCESS) {
//
// we managed to read something from the registry. Try to
// locate our adapter. The returned data is wide-character
// strings (better be, lets check the type first)
//
if (type == REG_MULTI_SZ) {
DWORD i;
LPTSTR pBinding = buffer;
// here we map the name to number
i = 0;
while (*pBinding) {
if (!_wcsnicmp(pBinding, pNdisName, DEFAULT_BINDING_LENGTH)) {
break;
}
pBinding = wcschr(pBinding, L'\0') + 1;
if (!*pBinding) {
break;
}
i++;
}
//
// if there is a binding corresponding to this adapter
// name (e.g. \Device\IbmTok01)
//
if (*pBinding) {
*AdapterNumber = i;
} else {
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
}
} else {
//
// unexpected type in registry
//
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
}
}
} else {
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
}
LocalFree(buffer);
}
else {
llcStatus = LLC_STATUS_NO_MEMORY;
}
RegCloseKey(hkey);
}
else {
llcStatus = LLC_STATUS_ADAPTER_NOT_INSTALLED;
}
return llcStatus;
}
LLC_STATUS
DoSyncDeviceIoControl(
IN HANDLE DeviceHandle,
IN ULONG IoctlCommand,
IN PVOID pInputBuffer,
IN UINT InputBufferLength,
OUT PVOID pOutputBuffer,
IN UINT OutputBufferLength
)
/*++
Routine Description:
Function makes only the IO control call little bit simpler
Arguments:
DeviceHandle - device handle of the current address object
IoctlCommand - DLC command code
pInputBuffer - input parameters
InputBufferLength - lenght of input parameters
pOutputBuffer - the returned data
OutputBufferLength - the length of the returned data
Return Value:
LLC_STATUS
--*/
{
NTSTATUS NtStatus;
NtStatus = NtDeviceIoControlFile(DeviceHandle,
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&GlobalIoStatus,
IoctlCommand,
pInputBuffer,
InputBufferLength,
pOutputBuffer,
OutputBufferLength
);
//
// NT DLC driver never returns any errors as NT error status.
// => the CCB pointer had to be invalid, if NtDeviceIoctl returns
// error
//
if (NtStatus != STATUS_SUCCESS && NtStatus != STATUS_PENDING) {
if (NtStatus > LLC_STATUS_MAX_ERROR) {
//
// NT DLC driver should never any errors as NT error status.
// => the CCB pointer must be invalid, if NtDeviceIoctl
// returns an nt error status.
//
NtStatus = LLC_STATUS_INVALID_POINTER_IN_CCB;
}
}
return (LLC_STATUS)NtStatus;
}
LLC_STATUS
DlcGetInfo(
IN HANDLE DriverHandle,
IN UINT InfoClass,
IN USHORT StationId,
IN PVOID pOutputBuffer,
IN UINT OutputBufferLength
)
/*++
Routine Description:
Function makes only the IO control call little bit simpler.
Arguments:
DriverHandle - the device handle of the address object
InfoClass - the type of the requested information
StationId - direct, link or sap station id
pOutputBuffer - the returned info structure
OutputBufferLength - output buffer length
Return Value:
LLC_STATUS
--*/
{
NT_DLC_QUERY_INFORMATION_PARMS GetInformation;
GetInformation.Header.StationId = StationId;
GetInformation.Header.InfoClass = (USHORT)InfoClass;
return DoSyncDeviceIoControl(DriverHandle,
IOCTL_DLC_QUERY_INFORMATION,
&GetInformation,
sizeof(NT_DLC_QUERY_INFORMATION_INPUT),
pOutputBuffer,
OutputBufferLength
);
}
LLC_STATUS
DlcSetInfo(
IN HANDLE DriverHandle,
IN UINT InfoClass,
IN USHORT StationId,
IN PNT_DLC_SET_INFORMATION_PARMS pSetInfoParms,
IN PVOID DataBuffer,
IN UINT DataBufferLength
)
/*++
Routine Description:
Function makes only the IO control call little bit simpler.
Arguments:
DriverHandle - the device handle of the address object
InfoClass - the type of the requested information
StationId - direct, link or sap station id
pSetInfoParms - NT DLC parameter buffer
DataBuffer - actual set data buffer copied to the parameter buffer
DataBufferLength - length of the data buffer.
Return Value:
LLC_STATUS
--*/
{
pSetInfoParms->Header.StationId = StationId;
pSetInfoParms->Header.InfoClass = (USHORT)InfoClass;
if (DataBuffer != NULL) {
RtlMoveMemory(pSetInfoParms->Info.Buffer, DataBuffer, DataBufferLength);
}
return DoSyncDeviceIoControl(DriverHandle,
IOCTL_DLC_SET_INFORMATION,
pSetInfoParms,
DataBufferLength + sizeof(struct _DlcSetInfoHeader),
NULL,
0
);
}
LLC_STATUS
DlcCallDriver(
IN UINT AdapterNumber,
IN UINT IoctlCommand,
IN PVOID pInputBuffer,
IN UINT InputBufferLength,
OUT PVOID pOutputBuffer,
IN UINT OutputBufferLength
)
/*++
Routine Description:
Function makes only the IO control call little bit simpler.
Arguments:
AdapterNumber - the requested adapter number (0 or 1)
IoctlCommand - DLC driver Ioctl code
pInputBuffer - input parameters
InputBufferLength - length of input parameters
pOutputBuffer - the returned data
OutputBufferLength - the length of the returned data
Return Value:
LLC_STATUS
--*/
{
NTSTATUS NtStatus;
HANDLE driverHandle;
EnterCriticalSection(&DriverHandlesCritSec);
driverHandle = aDlcDriverHandles[AdapterNumber];
LeaveCriticalSection(&DriverHandlesCritSec);
if (driverHandle) {
NtStatus = NtDeviceIoControlFile(driverHandle,
NULL,
NULL,
NULL,
&GlobalIoStatus,
IoctlCommand,
pInputBuffer,
InputBufferLength,
pOutputBuffer,
OutputBufferLength
);
} else {
NtStatus = STATUS_INVALID_HANDLE;
}
//
// if we get a real NT error (e.g. because the handle was invalid) then
// convert it into an adapter closed error
//
if (!NT_SUCCESS(NtStatus)) {
if ((NtStatus == STATUS_INVALID_HANDLE) || (NtStatus == STATUS_OBJECT_TYPE_MISMATCH)) {
//
// bad handle
//
return LLC_STATUS_ADAPTER_CLOSED;
} else if (NtStatus > LLC_STATUS_MAX_ERROR) {
//
// the NT DLC driver does not return any NT-level errors. If we get
// an NT-level error, then it must be because the IO subsystem
// detected an invalid pointer in the data we passed. Return an
// invalid pointer error
//
NtStatus = LLC_STATUS_INVALID_POINTER_IN_CCB;
}
} else if (NtStatus == STATUS_PENDING) {
//
// STATUS_PENDING is a success status
//
NtStatus = LLC_STATUS_PENDING;
}
return (LLC_STATUS)NtStatus;
}
#ifdef GRAB_READ
DWORD ReadGrabberCount = 2;
HANDLE ReadGrabberHandles[64];
PREAD_GRABBER ReadGrabberListHead = NULL;
PREAD_GRABBER ReadGrabberListTail = NULL;
CRITICAL_SECTION ReadGrabberListSect;
#endif
BOOLEAN
DllEntry(
IN PVOID DllHandle,
IN ULONG Reason,
IN PCONTEXT Context OPTIONAL
)
/*++
Routine Description:
Function initializes dlc for a new thread and terminates it in the
process exit.
Arguments:
DllHandle - don't need this
Reason - why this function is being called
Context - don't need this either
Return Value:
TRUE - nothing cannot go wrong in the initialization and
we cannot return error in close (or should we?).
--*/
{
static LLC_CCB OutputCcb; // MUST BE STATIC!!!
UNREFERENCED_PARAMETER(DllHandle); // avoid compiler warnings
UNREFERENCED_PARAMETER(Context); // avoid compiler warnings
if (Reason == DLL_PROCESS_ATTACH) {
#ifdef GRAB_READ
DWORD threadId;
OutputDebugString(L"DllEntry: Initializing ReadGrabber\n");
InitializeCriticalSection(&ReadGrabberListSect);
ReadGrabberHandles[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
ReadGrabberHandles[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReadGrabber, NULL, 0, &threadId);
#endif
//
// The global event handle is used to prevent the io-system
// to block calls, that are completed by READ.
//
InitializeCriticalSection(&AdapterOpenSection);
InitializeCriticalSection(&DriverHandlesCritSec);
RtlZeroMemory((PVOID)aDlcDriverHandles, sizeof(aDlcDriverHandles));
#if DBG
GetAcslanDebugFlags();
#endif
} else if (Reason == DLL_PROCESS_DETACH) {
UINT i;
LLC_CCB CloseCcb;
#ifdef GRAB_READ
ResetEvent(ReadGrabberHandles[0]);
#endif
//
// We must issue DIR_CLOSE_ADAPTER command for all
// opened adapters. Process exit does not close these
// handles before all pending IRPs have been completed.
// Thus this code actually flush all IRPs on dlc and lets
// IO- system to complete the cleanup.
//
RtlZeroMemory(&CloseCcb, sizeof(CloseCcb));
CloseCcb.uchDlcCommand = LLC_DIR_CLOSE_ADAPTER;
for (i = 0; i < LLC_MAX_ADAPTER_NUMBER; i++) {
if (aDlcDriverHandles[i] != NULL) {
CloseCcb.uchAdapterNumber = (UCHAR)i;
NtDeviceIoControlFile(aDlcDriverHandles[i],
NULL,
NULL,
NULL,
&GlobalIoStatus,
IOCTL_DLC_CLOSE_ADAPTER,
&CloseCcb,
sizeof(NT_DLC_CCB_INPUT),
&OutputCcb,
sizeof(NT_DLC_CCB_OUTPUT)
);
}
}
#if DBG
if (hDumpFile) {
fflush(hDumpFile);
fclose(hDumpFile);
}
#endif
//DeleteCriticalSection(&AdapterOpenSection);
//DeleteCriticalSection(&DriverHandlesCritSec);
}
return TRUE;
}
VOID
QueueCommandCompletion(
IN PLLC_CCB pCCB
)
/*++
Routine Description:
The routine queues a command completion event of a synchronous DLC command
to the command completion queue on the DLC driver.
RLF 04/23/93 Bogus: This should be handled in the driver
Arguments:
pCCB - OS/2 DLC Command control block, this must be double word aligned
Return Value:
LLC_STATUS - See the DLC API return values.
--*/
{
NT_DLC_COMPLETE_COMMAND_PARMS CompleteCommand;
UINT Status;
CompleteCommand.pCcbPointer = pCCB;
CompleteCommand.CommandCompletionFlag = pCCB->ulCompletionFlag;
CompleteCommand.StationId = GetCcbStationId(pCCB);
Status = DoSyncDeviceIoControl(aDlcDriverHandles[pCCB->uchAdapterNumber],
IOCTL_DLC_COMPLETE_COMMAND,
&CompleteCommand,
sizeof(CompleteCommand),
NULL,
0
);
if (Status != STATUS_SUCCESS) {
pCCB->uchDlcStatus = (UCHAR)Status;
}
}
#ifdef GRAB_READ
PLLC_CCB Last8ReadCcbs[8];
DWORD LastReadCcbIndex = 0;
PLLC_PARMS Last8ReadParameterTables[8];
DWORD LastReadParameterTableIndex = 0;
DWORD Last8IFramesReceived[8];
DWORD LastReceivedIndex = 0;
DWORD NextIFrame = 0;
VOID ReadGrabber() {
DWORD status;
PLLC_CCB readCcb;
WCHAR buf[100];
// static DWORD NextIFrame = 0;
PREAD_GRABBER grabbedRead;
while (1) {
if (ReadGrabberCount > 3) {
wsprintf(buf, L"ReadGrabber: waiting for %d handles\n", ReadGrabberCount);
OutputDebugString(buf);
}
status = WaitForMultipleObjects(ReadGrabberCount,
ReadGrabberHandles,
FALSE,
INFINITE
);
if (status >= WAIT_OBJECT_0 && status < WAIT_OBJECT_0 + ReadGrabberCount) {
if (status == WAIT_OBJECT_0) {
OutputDebugString(L"ReadGrabber terminating\n");
ExitThread(0);
} else if (status != WAIT_OBJECT_0+1) {
// wsprintf(buf, L"ReadGrabber: READ completed. Index %d\n", status);
// OutputDebugString(buf);
if (grabbedRead = RemoveReadGrabber(ReadGrabberHandles[status])) {
readCcb = grabbedRead->pCcb;
Last8ReadCcbs[LastReadCcbIndex] = readCcb;
LastReadCcbIndex = (LastReadCcbIndex + 1) & 7;
Last8ReadParameterTables[LastReadParameterTableIndex] = readCcb->u.pParameterTable;
LastReadParameterTableIndex = (LastReadParameterTableIndex + 1) & 7;
if (readCcb->u.pParameterTable->Read.uchEvent & LLC_EVENT_RECEIVE_DATA) {
PLLC_BUFFER pBuffer;
INT i;
if (readCcb->u.pParameterTable->Read.uchEvent != LLC_EVENT_RECEIVE_DATA) {
OutputDebugString(L"ReadGrabber: RECEIVED DATA + other events\n");
}
pBuffer = readCcb->u.pParameterTable->Read.Type.Event.pReceivedFrame;
for (i = readCcb->u.pParameterTable->Read.Type.Event.usReceivedFrameCount; i; --i) {
if (pBuffer->NotContiguous.uchMsgType == LLC_I_FRAME) {
DWORD thisDlcHeader;
thisDlcHeader = *(ULONG UNALIGNED*)(pBuffer->NotContiguous.auchDlcHeader);
if (thisDlcHeader & 0x00FF0000 != NextIFrame) {
wsprintf(buf,
L"Error: ReadGrabber: This=%08X. Next=%08X\n",
thisDlcHeader,
NextIFrame
);
OutputDebugString(buf);
}
NextIFrame = (thisDlcHeader + 0x00020000) & 0x00FF0000;
Last8IFramesReceived[LastReceivedIndex] = thisDlcHeader & 0x00FF0000;
LastReceivedIndex = (LastReceivedIndex + 1) & 7;
wsprintf(buf, L"%08X ", thisDlcHeader);
OutputDebugString(buf);
}
pBuffer = pBuffer->NotContiguous.pNextFrame;
if (!pBuffer && i > 1) {
OutputDebugString(L"ReadGrabber: Next frame is NULL, Count > 1!\n");
break;
}
}
if (pBuffer) {
OutputDebugString(L"ReadGrabber: Error: More frames linked!\n");
}
} else {
if (!(readCcb->u.pParameterTable->Read.uchEvent & LLC_EVENT_TRANSMIT_COMPLETION)) {
wsprintf(buf,
L"\nReadGrabber: Event = %02X\n",
readCcb->u.pParameterTable->Read.uchEvent
);
OutputDebugString(buf);
}
}
// DUMPCCB(readCcb, TRUE, FALSE);
// wsprintf(buf, L"ReadGrabber: Closing Handle %08X\n", grabbedRead->NewEventHandle);
// OutputDebugString(buf);
CloseHandle(grabbedRead->NewEventHandle);
readCcb->hCompletionEvent = grabbedRead->OriginalEventHandle;
// wsprintf(buf, L"ReadGrabber: Signalling Event %08X\n", grabbedRead->OriginalEventHandle);
// OutputDebugString(buf);
SetEvent(grabbedRead->OriginalEventHandle);
LocalFree((HLOCAL)grabbedRead);
}
// } else {
// OutputDebugString(L"ReadGrabber: something added to list!\n");
}
ReadGrabberCount = MungeReadGrabberHandles();
if (status == WAIT_OBJECT_0+1) {
ResetEvent(ReadGrabberHandles[1]);
}
} else {
INT i;
if (status == 0xFFFFFFFF) {
status = GetLastError();
}
wsprintf(buf, L"Yoiks: didn't expect this? status = %d\nHandle array:\n", status);
OutputDebugString(buf);
for (i = 0; i < ReadGrabberCount; ++i) {
wsprintf(buf, L"Handle %d: %08X\n", i, ReadGrabberHandles[i]);
OutputDebugString(buf);
}
}
}
}
DWORD MungeReadGrabberHandles() {
INT i;
PREAD_GRABBER p;
WCHAR buf[100];
EnterCriticalSection(&ReadGrabberListSect);
p = ReadGrabberListHead;
for (i = 2; p; ++i) {
ReadGrabberHandles[i] = p->NewEventHandle;
// wsprintf(buf, L"MungeReadGrabber: adding Struct %08X Ccb %08X Handle %08X, index %d\n",
// p,
// p->pCcb,
// ReadGrabberHandles[i],
// i
// );
// OutputDebugString(buf);
p = p->List;
}
LeaveCriticalSection(&ReadGrabberListSect);
return i;
}
VOID AddReadGrabber(PREAD_GRABBER pStruct) {
WCHAR buf[100];
PREAD_GRABBER pRead;
BOOL found = FALSE;
EnterCriticalSection(&ReadGrabberListSect);
// for (pRead = ReadGrabberListHead; pRead; pRead = pRead->List) {
// if (pRead->pCcb == pStruct->pCcb) {
// wsprintf(buf, L"AddReadGrabber: CCB %08X already on list. Ignoring\n",
// pStruct->pCcb
// );
// OutputDebugString(buf);
// LocalFree((HLOCAL)pStruct);
// found = TRUE;
// break;
// }
// }
// if (!found) {
if (!ReadGrabberListHead) {
ReadGrabberListHead = pStruct;
} else {
ReadGrabberListTail->List = pStruct;
}
ReadGrabberListTail = pStruct;
pStruct->List = NULL;
// wsprintf(buf, L"AddReadGrabber: adding %08X, CCB %08X New Handle %08X Old Handle %08X\n",
// pStruct,
// pStruct->pCcb,
// pStruct->NewEventHandle,
// pStruct->OriginalEventHandle
// );
// OutputDebugString(buf);
SetEvent(ReadGrabberHandles[1]);
// }
LeaveCriticalSection(&ReadGrabberListSect);
}
PREAD_GRABBER RemoveReadGrabber(HANDLE hEvent) {
PREAD_GRABBER this, prev;
WCHAR buf[100];
EnterCriticalSection(&ReadGrabberListSect);
prev = NULL;
for (this = ReadGrabberListHead; this; this = this->List) {
if (this->NewEventHandle == hEvent) {
break;
} else {
prev = this;
}
}
if (!this) {
wsprintf(buf, L"RemoveReadGrabber: Can't find handle %08X in ReadGrabberList\n",
hEvent
);
OutputDebugString(buf);
} else {
if (prev) {
prev->List = this->List;
} else {
ReadGrabberListHead = this->List;
}
if (ReadGrabberListTail == this) {
ReadGrabberListTail = prev;
}
}
LeaveCriticalSection(&ReadGrabberListSect);
// wsprintf(buf, L"RemoveReadGrabber: removed %08X, CCB %08X Handle %08X\n",
// this,
// this ? this->pCcb : 0,
// this ? this->NewEventHandle : 0
// );
// OutputDebugString(buf);
return this;
}
#endif