You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3382 lines
118 KiB
3382 lines
118 KiB
/*++
|
|
|
|
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
|