windows-nt/Source/XPSP1/NT/net/dlc/driver/dlcreq.c

1871 lines
49 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 AB
Module Name:
dlcreq.c
Abstract:
This module handles the miscellaneous DLC requests (set & query information)
Contents:
DlcBufferFree
DlcBufferGet
DlcBufferCreate
DlcConnectStation
DlcFlowControl
ResetLocalBusyBufferStates
DlcReallocate
DlcReset
DirSetExceptionFlags
CompleteAsyncCommand
GetLinkStation
GetSapStation
GetStation
DlcReadCancel
DirOpenAdapter
DirCloseAdapter
CompleteDirCloseAdapter
DlcCompleteCommand
Author:
Antti Saarenheimo 22-Jul-1991 (o-anttis)
Environment:
Kernel mode
Revision History:
--*/
#include <dlc.h>
#include "dlcdebug.h"
#if 0
//
// if DLC and LLC share the same driver then we can use macros to access fields
// in the BINDING_CONTEXT and ADAPTER_CONTEXT structures
//
#if DLC_AND_LLC
#ifndef i386
#define LLC_PRIVATE_PROTOTYPES
#endif
#include "llcdef.h"
#include "llctyp.h"
#include "llcapi.h"
#endif
#endif
NTSTATUS
DlcBufferFree(
IN PIRP pIrp,
IN PDLC_FILE_CONTEXT pFileContext,
IN PNT_DLC_PARMS pDlcParms,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength
)
/*++
Routine Description:
Procedure simply releases the given user buffers.
Arguments:
pIrp - current io request packet
pFileContext - DLC address object
pDlcParms - the current parameter block
InputBufferLength - the length of input parameters
OutputBufferLength -
Return Value:
NTSTATUS:
STATUS_SUCCESS
DLC_STATUS_INADEQUATE_BUFFERS - buffer pool does not exist
DLC_STATUS_INVALID_STATION_ID -
DLC_STATUS_INVALID_BUFFER_LENGTH -
NOTE!!! BUFFER.FREE does not return error, if the
given buffer is invalid, or released twice!!!
--*/
{
NTSTATUS status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(pIrp);
UNREFERENCED_PARAMETER(OutputBufferLength);
ASSUME_IRQL(DISPATCH_LEVEL);
if (!pFileContext->hBufferPool) {
return DLC_STATUS_INADEQUATE_BUFFERS;
}
//
// The parameter list is a DLC desriptor array.
// Get the number of descriptor elements in the array.
//
if (InputBufferLength != (sizeof(NT_DLC_BUFFER_FREE_PARMS)
- sizeof(LLC_TRANSMIT_DESCRIPTOR)
+ pDlcParms->BufferFree.BufferCount
* sizeof(LLC_TRANSMIT_DESCRIPTOR))) {
return DLC_STATUS_INVALID_BUFFER_LENGTH;
}
//
// We refernce the buffer pool, because otherwise it may disappear
// immeadiately after DLC_LEAVE (when the adapter is closed)
//
ReferenceBufferPool(pFileContext);
//
// Don't try to allocate 0 buffers, it will fail.
//
if (pDlcParms->BufferFree.BufferCount) {
LEAVE_DLC(pFileContext);
RELEASE_DRIVER_LOCK();
status = BufferPoolDeallocate(pFileContext->hBufferPool,
pDlcParms->BufferFree.BufferCount,
pDlcParms->BufferFree.DlcBuffer
);
ACQUIRE_DRIVER_LOCK();
ENTER_DLC(pFileContext);
//
// Reset the local busy states, if there is now enough
// buffers the receive the expected stuff.
//
if (!IsListEmpty(&pFileContext->FlowControlQueue)
&& BufGetUncommittedSpace(pFileContext->hBufferPool) >= 0) {
ResetLocalBusyBufferStates(pFileContext);
}
#if LLC_DBG
cFramesReleased++;
#endif
}
pDlcParms->BufferFree.cBuffersLeft = (USHORT)BufferPoolCount(pFileContext->hBufferPool);
DereferenceBufferPool(pFileContext);
return status;
}
NTSTATUS
DlcBufferGet(
IN PIRP pIrp,
IN PDLC_FILE_CONTEXT pFileContext,
IN PNT_DLC_PARMS pDlcParms,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength
)
/*++
Routine Description:
Procedure allocates the requested number or size of DLC buffers
and returns them back to user in a single entry link list..
Arguments:
pIrp - current io request packet
pFileContext - DLC adapter context
pDlcParms - the current parameter block
InputBufferLength - the length of input parameters
OutputBufferLength -
Return Value:
NTSTATUS:
STATUS_SUCCESS
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
UINT SegmentSize;
UINT SizeIndex;
UINT BufferSize;
UINT PrevBufferSize;
UINT cBuffersToGet;
PDLC_BUFFER_HEADER pBufferHeader = NULL;
UNREFERENCED_PARAMETER(pIrp);
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(OutputBufferLength);
ASSUME_IRQL(DISPATCH_LEVEL);
if (pFileContext->hBufferPool == NULL) {
return DLC_STATUS_INADEQUATE_BUFFERS;
}
//
// If the segment size is 0, then we return the optimal mix
// of buffer for the requested size. Non null buffer count defines
// how many buffers having the requested size is returned.
//
cBuffersToGet = pDlcParms->BufferGet.cBuffersToGet;
/*******************************************************************************
#if PAGE_SIZE == 8192
if (cBuffersToGet == 0) {
cBuffersToGet = 1;
SegmentSize = pDlcParms->BufferGet.cbBufferSize;
SizeIndex = (UINT)(-1);
} else if (pDlcParms->BufferGet.cbBufferSize <= 256) {
SegmentSize = 256 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 5;
} else if (pDlcParms->BufferGet.cbBufferSize <= 512) {
SegmentSize = 512 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 4;
} else if (pDlcParms->BufferGet.cbBufferSize <= 1024) {
SegmentSize = 1024 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 3;
} else if (pDlcParms->BufferGet.cbBufferSize <= 2048) {
SegmentSize = 2048 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 2;
} else if (pDlcParms->BufferGet.cbBufferSize <= 4096) {
SegmentSize = 4096 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 1;
} else if (pDlcParms->BufferGet.cbBufferSize <= 8192) {
SegmentSize = 8192 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 0;
} else {
return DLC_STATUS_INVALID_BUFFER_LENGTH;
}
#elif PAGE_SIZE == 4096
if (cBuffersToGet == 0) {
cBuffersToGet = 1;
SegmentSize = pDlcParms->BufferGet.cbBufferSize;
SizeIndex = (UINT)(-1);
} else if (pDlcParms->BufferGet.cbBufferSize <= 256) {
SegmentSize = 256 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 4;
} else if (pDlcParms->BufferGet.cbBufferSize <= 512) {
SegmentSize = 512 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 3;
} else if (pDlcParms->BufferGet.cbBufferSize <= 1024) {
SegmentSize = 1024 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 2;
} else if (pDlcParms->BufferGet.cbBufferSize <= 2048) {
SegmentSize = 2048 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 1;
} else if (pDlcParms->BufferGet.cbBufferSize <= 4096) {
SegmentSize = 4096 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 0;
} else {
return DLC_STATUS_INVALID_BUFFER_LENGTH;
}
#else
#error "Target machine page size not 4096 or 8192"
#endif
*******************************************************************************/
#if defined(ALPHA)
if (cBuffersToGet == 0) {
cBuffersToGet = 1;
SegmentSize = pDlcParms->BufferGet.cbBufferSize;
SizeIndex = (UINT)(-1);
} else if (pDlcParms->BufferGet.cbBufferSize <= 256) {
SegmentSize = 256 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 5;
} else if (pDlcParms->BufferGet.cbBufferSize <= 512) {
SegmentSize = 512 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 4;
} else if (pDlcParms->BufferGet.cbBufferSize <= 1024) {
SegmentSize = 1024 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 3;
} else if (pDlcParms->BufferGet.cbBufferSize <= 2048) {
SegmentSize = 2048 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 2;
} else if (pDlcParms->BufferGet.cbBufferSize <= 4096) {
SegmentSize = 4096 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 1;
} else if (pDlcParms->BufferGet.cbBufferSize <= 8192) {
SegmentSize = 8192 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 0;
} else {
return DLC_STATUS_INVALID_BUFFER_LENGTH;
}
#else
if (cBuffersToGet == 0) {
cBuffersToGet = 1;
SegmentSize = pDlcParms->BufferGet.cbBufferSize;
SizeIndex = (UINT)(-1);
} else if (pDlcParms->BufferGet.cbBufferSize <= 256) {
SegmentSize = 256 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 4;
} else if (pDlcParms->BufferGet.cbBufferSize <= 512) {
SegmentSize = 512 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 3;
} else if (pDlcParms->BufferGet.cbBufferSize <= 1024) {
SegmentSize = 1024 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 2;
} else if (pDlcParms->BufferGet.cbBufferSize <= 2048) {
SegmentSize = 2048 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 1;
} else if (pDlcParms->BufferGet.cbBufferSize <= 4096) {
SegmentSize = 4096 - sizeof(NEXT_DLC_SEGMENT);
SizeIndex = 0;
} else {
return DLC_STATUS_INVALID_BUFFER_LENGTH;
}
#endif
//
// We refernce the buffer pool, because otherwise it may disappear
// immeadiately after DLC_LEAVE (when the adapter is closed)
//
ReferenceBufferPool(pFileContext);
//
// We don't need to initialize the LAN and DLC header sizes
// in the buffer header and we allocate the requested
// frame as a single buffer.
//
BufferSize = SegmentSize * cBuffersToGet;
if (BufferSize != 0) {
pBufferHeader = NULL;
PrevBufferSize = 0;
LEAVE_DLC(pFileContext);
do {
//
// We must again do this interlocked to avoid the buffer
// pool to be deleted while we are allocating buffers.
//
Status = BufferPoolAllocate(
#if DBG
pFileContext,
#endif
(PDLC_BUFFER_POOL)pFileContext->hBufferPool,
BufferSize,
0, // FrameHeaderSize,
0, // UserDataSize,
0, // frame length
SizeIndex, // fixed segment size set
&pBufferHeader,
&BufferSize
);
#if DBG
BufferPoolExpand(pFileContext, (PDLC_BUFFER_POOL)pFileContext->hBufferPool);
#else
BufferPoolExpand((PDLC_BUFFER_POOL)pFileContext->hBufferPool);
#endif
//
// Don't try to expand buffer pool any more, if it doesn't help!
//
if (BufferSize == PrevBufferSize) {
break;
}
PrevBufferSize = BufferSize;
} while (Status == DLC_STATUS_EXPAND_BUFFER_POOL);
ENTER_DLC(pFileContext);
if (pBufferHeader != NULL) {
pBufferHeader->FrameBuffer.BufferState = BUF_USER;
}
if (Status == STATUS_SUCCESS) {
pDlcParms->BufferGet.pFirstBuffer = (PLLC_XMIT_BUFFER)
((PUCHAR)pBufferHeader->FrameBuffer.pParent->Header.pLocalVa +
MIN_DLC_BUFFER_SEGMENT * pBufferHeader->FrameBuffer.Index);
} else {
BufferPoolDeallocateList(pFileContext->hBufferPool, pBufferHeader);
}
}
pDlcParms->BufferGet.cBuffersLeft = (USHORT)BufferPoolCount(pFileContext->hBufferPool);
DereferenceBufferPool(pFileContext);
return Status;
}
NTSTATUS
DlcBufferCreate(
IN PIRP pIrp,
IN PDLC_FILE_CONTEXT pFileContext,
IN PNT_DLC_PARMS pDlcParms,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength
)
/*++
Routine Description:
Procedure creates a new buffer pool and allocates the initial
space for it.
Arguments:
pIrp - current io request packet
pFileContext - DLC adapter context
pDlcParms - the current parameter block
InputBufferLength - the length of input parameters
OutputBufferLength -
Return Value:
NTSTATUS:
Success - STATUS_SUCCESS
Failure - DLC_STATUS_DUPLICATE_COMMAND
--*/
{
NTSTATUS status;
PVOID newBufferAddress;
ULONG newBufferSize;
PVOID hExternalBufferPool;
PVOID hBufferPool;
UNREFERENCED_PARAMETER(pIrp);
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(OutputBufferLength);
ASSUME_IRQL(DISPATCH_LEVEL);
//
// if we already have a buffer pool defined for this handle, fail the
// request
//
if (pFileContext->hBufferPool) {
return DLC_STATUS_DUPLICATE_COMMAND;
}
hExternalBufferPool = pFileContext->hExternalBufferPool;
LEAVE_DLC(pFileContext);
#if DBG
status = BufferPoolCreate(pFileContext,
#else
status = BufferPoolCreate(
#endif
pDlcParms->BufferCreate.pBuffer,
pDlcParms->BufferCreate.cbBufferSize,
pDlcParms->BufferCreate.cbMinimumSizeThreshold,
&hExternalBufferPool,
&newBufferAddress,
&newBufferSize
);
ENTER_DLC(pFileContext);
pFileContext->hExternalBufferPool = hExternalBufferPool;
if (status == STATUS_SUCCESS) {
//
// The reference count keeps the buffer pool alive
// when it is used (and simultaneously deleted by another
// thread)
//
pFileContext->BufferPoolReferenceCount = 1;
hBufferPool = pFileContext->hBufferPool;
LEAVE_DLC(pFileContext);
status = BufferPoolReference(hExternalBufferPool,
&hBufferPool
);
ENTER_DLC(pFileContext);
pFileContext->hBufferPool = hBufferPool;
pDlcParms->BufferCreate.hBufferPool = pFileContext->hExternalBufferPool;
}
// ENTER_DLC(pFileContext);
return status;
}
NTSTATUS
DlcConnectStation(
IN PIRP pIrp,
IN PDLC_FILE_CONTEXT pFileContext,
IN PNT_DLC_PARMS pDlcParms,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength
)
/*++
Routine Description:
Procedure connects an local link station to a remote node
or accepts a remote connection request.
Arguments:
pIrp - current io request packet
pFileContext - DLC adapter context
pDlcParms - the current parameter block
InputBufferLength - the length of input parameters
OutputBufferLength -
Return Value:
NTSTATUS:
STATUS_SUCCESS
--*/
{
PDLC_OBJECT pLinkStation;
NTSTATUS Status;
PUCHAR pSourceRouting = NULL;
PDLC_COMMAND pPacket;
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(OutputBufferLength);
//
// Procedure checks the sap and link station ids and
// returns the requested link station.
// The error status indicates a wrong sap or station id.
//
Status = GetLinkStation(pFileContext,
pDlcParms->Async.Parms.DlcConnectStation.StationId,
&pLinkStation
);
if (Status != STATUS_SUCCESS) {
return Status;
}
pPacket = ALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool);
if (pPacket == NULL) {
return DLC_STATUS_NO_MEMORY;
}
pPacket->pIrp = pIrp;
//
// IBM LAN Tech. Ref p 3-48 (DLC.CONNECT.STATION) states that ROUTING_ADDR
// field is ignored if the link was created due to receipt of a SABME from
// the remote station, EVEN IF THE ADDRESS IS NON-ZERO
//
if (pDlcParms->Async.Parms.DlcConnectStation.RoutingInformationLength != 0) {
pSourceRouting = pDlcParms->Async.Parms.DlcConnectStation.aRoutingInformation;
}
pLinkStation->PendingLlcRequests++;
ReferenceLlcObject(pLinkStation);
LEAVE_DLC(pFileContext);
//
// LlcConnect returns the maximum information field,
// through the tr bridges!
//
LlcConnectStation(pLinkStation->hLlcObject,
(PLLC_PACKET)pPacket,
pSourceRouting,
&pLinkStation->u.Link.MaxInfoFieldLength
);
ENTER_DLC(pFileContext);
DereferenceLlcObject(pLinkStation);
return STATUS_PENDING;
}
NTSTATUS
DlcFlowControl(
IN PIRP pIrp,
IN PDLC_FILE_CONTEXT pFileContext,
IN PNT_DLC_PARMS pDlcParms,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength
)
/*++
Routine Description:
Procedure sets or resets the loacl busy state on the given link station
or on all link stations of a sap station.
Arguments:
pIrp - current io request packet
pFileContext - DLC adapter context
pDlcParms - the current parameter block
InputBufferLength - the length of input parameters
OutputBufferLength -
Return Value:
NTSTATUS:
Success - STATUS_SUCCESS
Failure - DLC_STATUS_NO_MEMORY
--*/
{
NTSTATUS Status;
PDLC_OBJECT pDlcObject;
UNREFERENCED_PARAMETER(pIrp);
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(OutputBufferLength);
ASSUME_IRQL(DISPATCH_LEVEL);
//
// Procedure checks the sap and link station ids and
// returns the requested link station.
// The error status indicates a wrong sap or station id.
//
Status = GetStation(pFileContext,
pDlcParms->DlcFlowControl.StationId,
&pDlcObject
);
if (Status != STATUS_SUCCESS) {
return Status;
}
//
// We will queue all reset local busy buffer commands
// given to the link stations
//
if (((pDlcParms->DlcFlowControl.FlowControlOption & LLC_RESET_LOCAL_BUSY_BUFFER) == LLC_RESET_LOCAL_BUSY_BUFFER)
&& (pDlcObject->Type == DLC_LINK_OBJECT)) {
PDLC_RESET_LOCAL_BUSY_CMD pClearCmd;
pClearCmd = (PDLC_RESET_LOCAL_BUSY_CMD)ALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool);
if (pClearCmd == NULL) {
return DLC_STATUS_NO_MEMORY;
}
pClearCmd->StationId = pDlcParms->DlcFlowControl.StationId;
pClearCmd->RequiredBufferSpace = 0;
LlcInsertTailList(&pFileContext->FlowControlQueue, pClearCmd);
ResetLocalBusyBufferStates(pFileContext);
} else {
ReferenceLlcObject(pDlcObject);
LEAVE_DLC(pFileContext);
Status = LlcFlowControl(pDlcObject->hLlcObject,
pDlcParms->DlcFlowControl.FlowControlOption
);
ENTER_DLC(pFileContext);
DereferenceLlcObject(pDlcObject);
}
return Status;
}
VOID
ResetLocalBusyBufferStates(
IN PDLC_FILE_CONTEXT pFileContext
)
/*++
Routine Description:
Procedure executes the pending busy state resets when there is
enough memory in the buffer pool to receive the expected data.
Arguments:
pFileContext - DLC adapter context
Return Value:
None
--*/
{
NTSTATUS Status;
PDLC_OBJECT pDlcObject;
PDLC_RESET_LOCAL_BUSY_CMD pClearCmd;
ASSUME_IRQL(DISPATCH_LEVEL);
//
// We cannot reset anything, if the buffer pool is not yet
// defined.
//
if (pFileContext->hBufferPool == NULL) {
return;
}
ReferenceBufferPool(pFileContext);
while (!IsListEmpty(&pFileContext->FlowControlQueue)) {
pClearCmd = LlcRemoveHeadList(&pFileContext->FlowControlQueue);
Status = GetLinkStation(pFileContext,
pClearCmd->StationId,
&pDlcObject
);
//
// All commands having an invalid station id will be just removed
// from the queue. The local busy state can be reset only
// for the existing link stations
//
if (Status == STATUS_SUCCESS) {
//
// The required space is nul, when a mew packet is checked
// in the first time, the non-null value just prevents
// us to check the commited memory in the second time.
//
if (pClearCmd->RequiredBufferSpace == 0) {
//
// We must also remove the old uncommited space,
// otherwise the same buffer size could be
// committed several times, but uncommitted only once
//
if (pDlcObject->CommittedBufferSpace != 0) {
BufUncommitBuffers(pFileContext->hBufferPool,
pDlcObject->CommittedBufferSpace
);
}
pDlcObject->CommittedBufferSpace =
pClearCmd->RequiredBufferSpace = LlcGetCommittedSpace(pDlcObject->hLlcObject);
BufCommitBuffers(pFileContext->hBufferPool,
pDlcObject->CommittedBufferSpace
);
}
//
// We are be removing a local buffer busy state =>
// we must expand the buffer pools before the local busy
// is removed, but only if we are not calling this
// from a DPC level.
//
if (BufGetUncommittedSpace(pFileContext->hBufferPool) < 0) {
LEAVE_DLC(pFileContext);
#if DBG
BufferPoolExpand(pFileContext, pFileContext->hBufferPool);
#else
BufferPoolExpand(pFileContext->hBufferPool);
#endif
ENTER_DLC(pFileContext);
}
//
// Now we have expanded the buffer pool for the new
// flow control command, check if we have now enough
// memory to receive the commited size of data.
//
if (BufGetUncommittedSpace(pFileContext->hBufferPool) >= 0
&& pDlcObject->hLlcObject != NULL) {
ReferenceLlcObject(pDlcObject);
LEAVE_DLC(pFileContext);
Status = LlcFlowControl(pDlcObject->hLlcObject,
LLC_RESET_LOCAL_BUSY_BUFFER
);
ENTER_DLC(pFileContext);
DereferenceLlcObject(pDlcObject);
} else {
//
// We must exit this loop when there is not enough available
// space in the buffer pool, but we must return
// the command back to the head of the list
//
LlcInsertHeadList(&pFileContext->FlowControlQueue, pClearCmd);
break;
}
}
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pClearCmd);
}
DereferenceBufferPool(pFileContext);
}
NTSTATUS
DlcReallocate(
IN PIRP pIrp,
IN PDLC_FILE_CONTEXT pFileContext,
IN PNT_DLC_PARMS pDlcParms,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength
)
/*++
Routine Description:
Procedure changes the number of link stations allocated to a SAP
without closing or reopening the sap station.
Arguments:
pIrp - current io request packet
pFileContext - DLC adapter context
pDlcParms - the current parameter block
InputBufferLength - the length of input parameters
OutputBufferLength
Return Value:
NTSTATUS:
STATUS_SUCCESS
--*/
{
PDLC_OBJECT pSap;
UCHAR ExtraStations;
UCHAR StationCount;
NTSTATUS Status;
UNREFERENCED_PARAMETER(pIrp);
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(OutputBufferLength);
//
// Procedure checks the sap and returns the requested sap station.
// The error status indicates an invalid sap station id.
//
Status = GetSapStation(pFileContext,
pDlcParms->DlcReallocate.usStationId,
&pSap
);
if (Status != STATUS_SUCCESS) {
return Status;
}
//
// The new link station count must be more than current number
// of open link stations but less than the available number
// of link stations for the file context
//
StationCount = pDlcParms->DlcReallocate.uchStationCount;
if (StationCount != 0 && Status == STATUS_SUCCESS) {
//
// Bit7 set in options => decrease the number of available
// stations by the given station count. Otherwise we increase it.
//
if (pDlcParms->DlcReallocate.uchOption & 0x80) {
ExtraStations = pSap->u.Sap.MaxStationCount - pSap->u.Sap.LinkStationCount;
if (StationCount > ExtraStations) {
StationCount = ExtraStations;
Status = DLC_STATUS_INADEQUATE_LINKS;
}
pFileContext->LinkStationCount += StationCount;
pSap->u.Sap.MaxStationCount -= StationCount;
} else {
if (pFileContext->LinkStationCount < StationCount) {
StationCount = pFileContext->LinkStationCount;
Status = DLC_STATUS_INADEQUATE_LINKS;
}
pFileContext->LinkStationCount -= StationCount;
pSap->u.Sap.MaxStationCount += StationCount;
}
}
//
// Set the return parameters even if there would be an error
// (inadequate stations is a non fatal error)
//
pDlcParms->DlcReallocate.uchStationsAvailOnAdapter = pFileContext->LinkStationCount;
pDlcParms->DlcReallocate.uchStationsAvailOnSap = pSap->u.Sap.MaxStationCount - pSap->u.Sap.LinkStationCount;
pDlcParms->DlcReallocate.uchTotalStationsOnAdapter = MAX_LINK_STATIONS;
pDlcParms->DlcReallocate.uchTotalStationsOnSap = pSap->u.Sap.MaxStationCount;
return Status;
}
NTSTATUS
DlcReset(
IN PIRP pIrp,
IN PDLC_FILE_CONTEXT pFileContext,
IN PNT_DLC_PARMS pDlcParms,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength
)
/*++
Routine Description:
Procedure closes immediately a sap and its all link stations or
all saps and all link stations.
Arguments:
pIrp - current io request packet
pFileContext - DLC adapter context
pDlcParms - the current parameter block
InputBufferLength - the length of input parameters
OutputBufferLength -
Return Value:
NTSTATUS:
STATUS_SUCCESS
STATUS_PENDING
DLC_STATUS_NO_MEMORY
--*/
{
PDLC_OBJECT pDlcObject;
PDLC_CLOSE_WAIT_INFO pClosingInfo;
NTSTATUS Status;
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(OutputBufferLength);
ASSUME_IRQL(DISPATCH_LEVEL);
//
// Station id 0 resets the whole DLC
//
if (pDlcParms->Async.Ccb.u.dlc.usStationId == 0) {
PDLC_CLOSE_WAIT_INFO pClosingInfo;
pClosingInfo = ALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool);
if (pClosingInfo == NULL) {
Status = DLC_STATUS_NO_MEMORY;
} else {
CloseAllStations(pFileContext,
pIrp,
DLC_COMMAND_COMPLETION,
NULL,
pDlcParms,
pClosingInfo
);
Status = STATUS_PENDING;
}
} else {
BOOLEAN allClosed;
//
// We have a specific sap station
//
Status = GetSapStation(pFileContext,
pDlcParms->Async.Ccb.u.dlc.usStationId,
&pDlcObject
);
if (Status != STATUS_SUCCESS) {
return Status;
}
//
// Allocate the close/reset command completion info,
// the station count is the number of link stations and
// the sap station itself
//
pClosingInfo = ALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool);
if (pClosingInfo == NULL) {
return DLC_STATUS_NO_MEMORY;
}
pClosingInfo->pIrp = pIrp;
pClosingInfo->Event = DLC_COMMAND_COMPLETION;
pClosingInfo->CancelStatus = DLC_STATUS_CANCELLED_BY_USER;
pClosingInfo->CloseCounter = 1; // keep command alive over sync path
(USHORT)(pDlcObject->u.Sap.LinkStationCount + 1);
CloseAnyStation(pDlcObject, pClosingInfo, FALSE);
//
// RLF 05/09/93
//
// PC/3270 (DOS program) is hanging forever when we try to quit.
// It is doing this because we returned STATUS_PENDING to a DLC.RESET,
// even though the reset completed; the DOS program spins forever on
// the CCB.uchDlcStatus field, waiting for it to go non-0xFF, which it
// will never do.
//
// If we determine that the station has been reset (all links closed)
// then return success, else pending
//
allClosed = DecrementCloseCounters(pFileContext, pClosingInfo);
//
// RLF 07/21/92 Always return PENDING. Can't complete before return?
//
Status = allClosed ? STATUS_SUCCESS : STATUS_PENDING;
}
return Status;
}
NTSTATUS
DirSetExceptionFlags(
IN PIRP pIrp,
IN PDLC_FILE_CONTEXT pFileContext,
IN PNT_DLC_PARMS pDlcParms,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength
)
/*++
Routine Description:
Procedure sets the exception flags for the current adapter context.
Arguments:
pIrp - current io request packet
pFileContext - DLC adapter context
pDlcParms - the current parameter block
InputBufferLength - the length of input parameters
OutputBufferLength
Return Value:
NTSTATUS:
STATUS_SUCCESS
--*/
{
UNREFERENCED_PARAMETER(pIrp);
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(OutputBufferLength);
pFileContext->AdapterCheckFlag = pDlcParms->DirSetExceptionFlags.ulAdapterCheckFlag;
pFileContext->NetworkStatusFlag = pDlcParms->DirSetExceptionFlags.ulNetworkStatusFlag;
pFileContext->PcErrorFlag = pDlcParms->DirSetExceptionFlags.ulPcErrorFlag;
pFileContext->SystemActionFlag = pDlcParms->DirSetExceptionFlags.ulSystemActionFlag;
return STATUS_SUCCESS;
}
VOID
CompleteAsyncCommand(
IN PDLC_FILE_CONTEXT pFileContext,
IN UINT Status,
IN PIRP pIrp,
IN PVOID pUserCcbPointer,
IN BOOLEAN InCancel
)
/*++
Routine Description:
Procedure completes an asynchronous DLC command.
It also copies the optional output parameters to user parameter
table, if there is the second output buffer.
Arguments:
pFileContext - DLC driver client context.
Status - status of the complete command.
pIrp - the completed I/O request packet.
pUserCcbPointer - the next CCB address to which the command will be linked.
InCancel - TRUE if called on Irp cancel path
Return Value:
None.
--*/
{
PNT_DLC_PARMS pDlcParms;
UNREFERENCED_PARAMETER(pFileContext);
ASSUME_IRQL(DISPATCH_LEVEL);
DIAG_FUNCTION("CompleteAsyncCommand");
pDlcParms = (PNT_DLC_PARMS)pIrp->AssociatedIrp.SystemBuffer;
//
// We first map the 32-bit DLC driver status code to 8- bit API status
//
if (Status == STATUS_SUCCESS) {
pDlcParms->Async.Ccb.uchDlcStatus = (UCHAR)STATUS_SUCCESS;
} else if (Status >= DLC_STATUS_ERROR_BASE && Status < DLC_STATUS_MAX_ERROR) {
//
// We can map the normal DLC error codes directly to the 8-bit
// DLC API error codes.
//
pDlcParms->Async.Ccb.uchDlcStatus = (UCHAR)(Status - DLC_STATUS_ERROR_BASE);
} else {
//
// we have an unknown NT error status => we will return it in the CCB
//
pDlcParms->Async.Ccb.uchDlcStatus = (UCHAR)(DLC_STATUS_NT_ERROR_STATUS & 0xff);
}
pDlcParms->Async.Ccb.pCcbAddress = pUserCcbPointer;
//
// We always return success status to the I/O system. The actual status is
// copied to the CCB (= the dafault output buffer)
//
LEAVE_DLC(pFileContext);
RELEASE_DRIVER_LOCK();
DlcCompleteIoRequest(pIrp, InCancel);
ACQUIRE_DRIVER_LOCK();
ENTER_DLC(pFileContext);
DereferenceFileContext(pFileContext);
}
NTSTATUS
GetLinkStation(
IN PDLC_FILE_CONTEXT pFileContext,
IN USHORT StationId,
OUT PDLC_OBJECT *ppLinkStation
)
/*++
Routine Description:
Procedure checks and returns link station
Arguments:
pFileContext - DLC driver client context
StationId - DLC station id (ssnn, where ss = sap and nn = link station id
ppDlcObject - the returned link station
Return Value:
NTSTATUS:
STATUS_SUCCESS
DLC_STATUS_INVALID_SAP_VALUE
DLC_STATUS_INVALID_STATION_ID
--*/
{
if ((StationId & 0xff) == 0 || (StationId & 0xff00) == 0) {
return DLC_STATUS_INVALID_STATION_ID;
}
return GetStation(pFileContext, StationId, ppLinkStation);
}
NTSTATUS
GetSapStation(
IN PDLC_FILE_CONTEXT pFileContext,
IN USHORT StationId,
OUT PDLC_OBJECT *ppStation
)
/*++
Routine Description:
Procedure checks and returns link station
Arguments:
pFileContext - DLC driver client context
StationId - DLC station id (ssnn, where ss = sap and nn = link station id
ppDlcObject - the returned link station
Return Value:
NTSTATUS:
STATUS_SUCCESS
DLC_STATUS_INVALID_SAP_VALUE
DLC_STATUS_INVALID_STATION_ID
--*/
{
UINT SapId = StationId >> 9;
if (SapId >= MAX_SAP_STATIONS
|| SapId == 0
|| (StationId & GROUP_SAP_BIT)
|| (*ppStation = pFileContext->SapStationTable[SapId]) == NULL
|| (*ppStation)->State != DLC_OBJECT_OPEN) {
return DLC_STATUS_INVALID_SAP_VALUE;
}
return STATUS_SUCCESS;
}
NTSTATUS
GetStation(
IN PDLC_FILE_CONTEXT pFileContext,
IN USHORT StationId,
OUT PDLC_OBJECT *ppStation
)
/*++
Routine Description:
Procedure checks the given station id and returns a pointer to
sap, direct or link station object.
Arguments:
pFileContext - DLC driver client context
StationId - DLC station id (ssnn, where ss = sap and nn = link station id
ppStation - the returned station object
Return Value:
NTSTATUS:
STATUS_SUCCESS
DLC_STATUS_INVALID_STATION_ID
--*/
{
UINT SapId = StationId >> 9;
//
// Check if the sap or direct station exists,
// but check also the link station, if we found a valid sap id.
//
if (SapId >= MAX_SAP_STATIONS
|| (StationId & GROUP_SAP_BIT)
|| (*ppStation = pFileContext->SapStationTable[SapId]) == NULL
|| (*ppStation)->State != DLC_OBJECT_OPEN) {
if (SapId == 0) {
return DLC_STATUS_DIRECT_STATIONS_NOT_AVAILABLE;
} else {
return DLC_STATUS_INVALID_STATION_ID;
}
}
//
// The link station table will never be read, if we have found
// a valid sap or direct station. Link station must exist and
// it must be opened.
//
if (SapId != 0
&& (StationId & 0xff) != 0
&& (*ppStation = pFileContext->LinkStationTable[((StationId & 0xff) - 1)]) == NULL
|| (*ppStation)->State != DLC_OBJECT_OPEN) {
return DLC_STATUS_INVALID_STATION_ID;
}
return STATUS_SUCCESS;
}
NTSTATUS
DlcReadCancel(
IN PIRP pIrp,
IN PDLC_FILE_CONTEXT pFileContext,
IN PNT_DLC_PARMS pDlcParms,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength
)
/*++
Routine Description:
This primitive cancels a READ command, that have the given CCB pointer.
Arguments:
pIrp - current io request packet
pFileContext - DLC process specific adapter context
pDlcParms - the current parameter block
InputBufferLength - the length of input parameters
OutputBufferLength -
Return Value:
DLC_STATUS:
STATUS_SUCCESS
--*/
{
PVOID pCcbAddress = NULL;
UNREFERENCED_PARAMETER(pIrp);
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(OutputBufferLength);
DLC_TRACE('Q');
return AbortCommand(pFileContext,
(USHORT)DLC_IGNORE_STATION_ID,
(USHORT)DLC_STATION_MASK_SPECIFIC,
pDlcParms->DlcCancelCommand.CcbAddress,
&pCcbAddress,
DLC_STATUS_CANCELLED_BY_USER,
TRUE // Suppress completion
);
}
NTSTATUS
DirOpenAdapter(
IN PIRP pIrp,
IN PDLC_FILE_CONTEXT pFileContext,
IN PNT_DLC_PARMS pDlcParms,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength
)
/*++
Routine Description:
This primitive binds the DLC API driver to an adapter context of
the LLC module. The LLC mode may also bind to the given NDIS driver
and open it, if this is the first reference to the driver from DLC.
Arguments:
pIrp - current io request packet
pFileContext - DLC process specific adapter context
pDlcParms - the current parameter block
InputBufferLength - the length of input parameters
OutputBufferLength -
Return Value:
DLC_STATUS:
STATUS_SUCCESS
--*/
{
NTSTATUS Status;
UINT OpenErrorCode;
UNREFERENCED_PARAMETER(pIrp);
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(OutputBufferLength);
ASSUME_IRQL(DISPATCH_LEVEL);
if (pDlcParms->DirOpenAdapter.NtDlcIoctlVersion != NT_DLC_IOCTL_VERSION) {
return DLC_STATUS_INVALID_VERSION;
}
//
// This makes the DirOpenAdapter safe, even if there were two adapter
// opens going on simultaneously
//
//
// RLF 04/22/94
//
// this only protects against 2 threads in the same process performing
// simultaneous opens on the same adapter
//
if (pFileContext->pBindingContext) {
return DLC_STATUS_DUPLICATE_COMMAND;
}
pFileContext->pBindingContext = (PVOID)-1;
//
// if a buffer pool handle was supplied (i.e. the app already created a
// buffer pool or is otherwise sharing one) then reference it for this
// file context
//
if (pDlcParms->DirOpenAdapter.hBufferPoolHandle) {
Status = BufferPoolReference(pDlcParms->DirOpenAdapter.hBufferPoolHandle,
&pFileContext->hBufferPool
);
if (Status == STATUS_SUCCESS) {
pFileContext->BufferPoolReferenceCount = 1;
pFileContext->hExternalBufferPool = pDlcParms->DirOpenAdapter.hBufferPoolHandle;
} else {
//
// Invalid buffer pool handle, hopefully this status
// code indicates correctly, that the buffer pool
// handle is not valid.
//
pFileContext->pBindingContext = NULL;
return DLC_STATUS_INVALID_BUFFER_LENGTH;
}
}
LEAVE_DLC(pFileContext);
//
// XXXXXXX: BringUpDiagnostics are still missing!!!
//
//
// RLF 04/19/93
//
// The string we pass to LlcOpenAdapter is a pointer to a zero terminated
// wide character string, NOT a pointer to a UNICODE_STRING structure. The
// string MUST be in system memory space, i.e. copied across the kernel
// interface by NtDeviceIoControlFile
//
Status = LlcOpenAdapter(&pDlcParms->DirOpenAdapter.Buffer[0],
(PVOID)pFileContext,
LlcCommandCompletion,
LlcReceiveIndication,
LlcEventIndication,
NdisMedium802_5, // Always token-ring!
pDlcParms->DirOpenAdapter.LlcEthernetType,
pDlcParms->DirOpenAdapter.AdapterNumber,
&pFileContext->pBindingContext,
&OpenErrorCode,
&pFileContext->MaxFrameLength,
&pFileContext->ActualNdisMedium
);
//
// make sure LlcOpenAdapter didn't return with lowered IRQL
//
ASSUME_IRQL(DISPATCH_LEVEL);
//
// IBM LAN Tech. Ref. defines the open error code as a 16-bit value, the
// high 8 bits of which are 0. The MAC inclusive-ORs the open error code
// into the NDIS status. Extract it
//
pDlcParms->DirOpenAdapter.Adapter.usOpenErrorCode = (USHORT)(UCHAR)OpenErrorCode;
if (Status != STATUS_SUCCESS) {
ENTER_DLC(pFileContext);
//
// It does not matter, if we have null buffer pool handle!
//
#if DBG
BufferPoolDereference(pFileContext,
(PDLC_BUFFER_POOL*)&pFileContext->hBufferPool
);
#else
BufferPoolDereference((PDLC_BUFFER_POOL*)&pFileContext->hBufferPool);
#endif
//
// set the BINDING_CONTEXT pointer back to NULL - other routines check
// for this value, like CloseAdapterFileContext
//
pFileContext->pBindingContext = NULL;
//
// Probably the adapter was missing or it was installed improperly
//
Status = DLC_STATUS_ADAPTER_NOT_INSTALLED;
} else {
//
// Set the optional timer tick one/two values
// (if they have been set in registry)
//
LlcSetInformation(pFileContext->pBindingContext,
DLC_INFO_CLASS_DLC_TIMERS,
(PLLC_SET_INFO_BUFFER)&(pDlcParms->DirOpenAdapter.LlcTicks),
sizeof(LLC_TICKS)
);
LlcQueryInformation(pFileContext->pBindingContext,
DLC_INFO_CLASS_DIR_ADAPTER,
(PLLC_QUERY_INFO_BUFFER)pDlcParms->DirOpenAdapter.Adapter.auchNodeAddress,
sizeof(LLC_ADAPTER_INFO)
);
ENTER_DLC(pFileContext);
//
// take the missing parameters from the hat
//
pDlcParms->DirOpenAdapter.Adapter.usOpenOptions = 0;
pDlcParms->DirOpenAdapter.Adapter.usMaxFrameSize = (USHORT)(pFileContext->MaxFrameLength + 6);
pDlcParms->DirOpenAdapter.Adapter.usBringUps = 0;
pDlcParms->DirOpenAdapter.Adapter.InitWarnings = 0;
pFileContext->AdapterNumber = pDlcParms->DirOpenAdapter.AdapterNumber;
pFileContext->LinkStationCount = 255;
pFileContext->pSecurityDescriptor = pDlcParms->DirOpenAdapter.pSecurityDescriptor;
//
// Read the most recent cumulative NDIS error counters
// to the file context. DLC error counters will be counted
// from 0 and they may be reset.
//
GetDlcErrorCounters(pFileContext, NULL);
pFileContext->State = DLC_FILE_CONTEXT_OPEN;
}
//
// We may directly return whatever the LLC binding primitive gives us
//
return Status;
}
NTSTATUS
DirCloseAdapter(
IN PIRP pIrp,
IN PDLC_FILE_CONTEXT pFileContext,
IN PNT_DLC_PARMS pDlcParms,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength
)
/*++
Routine Description:
This primitive initializes the close adapter operation.
It first closes all open link and sap stations and
then optionally chains the canceled commands and
receive buffers to a READ command. If no read commands
was found, then the CCBs are linked to the CCB pointer
of this command.
The actual file close should only delete the file
object, except, if the application exits, when it still has
open dlc api handles. In that case the file close routine
will call this procedure to shut down the dlc file context.
Arguments:
pIrp - current io request packet
pFileContext - DLC process specific adapter context
pDlcParms - the current parameter block
InputBufferLength - the length of input parameters
OutputBufferLength -
Return Value:
NTSTATUS
STATUS_PENDING
The adapter is being closed
DLC_STATUS_ADAPTER_CLOSED
The adapter is already closed
NOTE: This is a SYNCHRONOUS return code! And will cause the IRP
to be completed
--*/
{
UNREFERENCED_PARAMETER(pDlcParms);
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(OutputBufferLength);
ASSUME_IRQL(DISPATCH_LEVEL);
DIAG_FUNCTION("DirCloseAdapter");
DLC_TRACE('J');
if (pFileContext->State != DLC_FILE_CONTEXT_OPEN) {
return DLC_STATUS_ADAPTER_CLOSED;
}
#if LLC_DBG == 2
DbgPrint( "*** Top memory consumption (before adapter close) *** \n" );
PrintMemStatus();
#endif
//
// This disables any further commands (including DirCloseAdapter)
//
pFileContext->State = DLC_FILE_CONTEXT_CLOSE_PENDING;
//
// Remove first all functional, group or multicast addresses
// set in the adapter by the current DLC application process
//
if (pFileContext->pBindingContext) {
LEAVE_DLC(pFileContext);
LlcResetBroadcastAddresses(pFileContext->pBindingContext);
ENTER_DLC(pFileContext);
}
//
// We must use the static closing packet, because the adapter close
// must succeed even if we could not allocate any packets
//
CloseAllStations(pFileContext,
pIrp,
DLC_COMMAND_COMPLETION,
CompleteDirCloseAdapter,
pDlcParms,
&pFileContext->ClosingPacket
);
return STATUS_PENDING;
}
VOID
CompleteDirCloseAdapter(
IN PDLC_FILE_CONTEXT pFileContext,
IN PDLC_CLOSE_WAIT_INFO pClosingInfo,
IN PVOID pCcbLink
)
/*++
Routine Description:
Finishes DIR.CLOSE.ADAPTER command
Arguments:
pFileContext - DLC adapter open context
pClosingInfo - packet structure, that includes all data of this command
pCcbLink - the orginal user mode ccb address on the next CCB, that
will be chained to the completed command.
Return Value:
None
--*/
{
ASSUME_IRQL(DISPATCH_LEVEL);
DLC_TRACE('K');
//
// reference the file context to stop any of the dereferences below, or in
// functions called by this routine destroying it
//
ReferenceFileContext(pFileContext);
//
// Disconnect (or unbind) llc driver from us
//
if (pFileContext->pBindingContext) {
LEAVE_DLC(pFileContext);
LlcDisableAdapter(pFileContext->pBindingContext);
ENTER_DLC(pFileContext);
}
if (IoGetCurrentIrpStackLocation(pClosingInfo->pIrp)->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
CompleteAsyncCommand(pFileContext, STATUS_SUCCESS, pClosingInfo->pIrp, pCcbLink, FALSE);
} else {
//
// This is a normal FILE CLOSE !!! (IRP_MJ_CLEANUP)
//
ASSERT(IoGetCurrentIrpStackLocation(pClosingInfo->pIrp)->MajorFunction == IRP_MJ_CLEANUP);
//
// Dereference for the cleanup. This will allow cleanup to become
// unblocked.
//
DereferenceFileContext(pFileContext);
}
//
// We must delete the buffer pool now, because the dereference
// of the driver object starts the final process exit
// completion, that bug chekcs, if the number of the locked
// pages is non zero.
//
DereferenceBufferPool(pFileContext);
//
// We create two references for file context, when it is created
// the other is decremented here and the other when the synchronous
// part of command completion has been done
//
pFileContext->State = DLC_FILE_CONTEXT_CLOSED;
//
// This should be the last reference of the file context
// (if no IRPs operations are in execution or pending.
//
DereferenceFileContext(pFileContext);
}
NTSTATUS
DlcCompleteCommand(
IN PIRP pIrp,
IN PDLC_FILE_CONTEXT pFileContext,
IN PNT_DLC_PARMS pDlcParms,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength
)
/*++
Routine Description:
Procedure queues the given CCB to the completion list.
This routine is used to save the synchronous commands from
DLC API DLL to event queue. This must be done whenever a
synchronous command has a non null command completion flag.
Arguments:
pIrp - current io request packet
pFileContext - DLC address object
pDlcParms - the current parameter block
InputBufferLength - the length of input parameters
OutputBufferLength -
Return Value:
NTSTATUS:
STATUS_SUCCESS
--*/
{
UNREFERENCED_PARAMETER(pIrp);
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(OutputBufferLength);
if (pDlcParms->CompleteCommand.CommandCompletionFlag == 0
|| pDlcParms->CompleteCommand.pCcbPointer == NULL) {
//
// This is more likely an internal error!
//
return DLC_STATUS_INTERNAL_ERROR;
}
return MakeDlcEvent(pFileContext,
DLC_COMMAND_COMPLETION,
pDlcParms->CompleteCommand.StationId,
NULL,
pDlcParms->CompleteCommand.pCcbPointer,
pDlcParms->CompleteCommand.CommandCompletionFlag,
FALSE
);
}