mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2383 lines
74 KiB
2383 lines
74 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
Copyright (c) 1991 Nokia Data Systems AB
|
|
|
|
Module Name:
|
|
|
|
dlcopen.c
|
|
|
|
Abstract:
|
|
|
|
This module implements all open and close operations for DLC objects
|
|
|
|
Contents:
|
|
DlcOpenSap
|
|
DirOpenDirect
|
|
DlcOpenLinkStation
|
|
InitializeLinkStation
|
|
DlcCloseStation
|
|
CloseAllStations
|
|
CloseAnyStation
|
|
CloseStation
|
|
CompleteCloseStation
|
|
CompleteCloseReset
|
|
CleanUpEvents
|
|
SearchReadCommandForClose
|
|
CompleteLlcObjectClose
|
|
DecrementCloseCounters
|
|
CompleteDirectOutIrp
|
|
|
|
Author:
|
|
|
|
Antti Saarenheimo 29-Aug-1991
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <dlc.h>
|
|
#include "dlcdebug.h"
|
|
#include <smbgtpt.h>
|
|
|
|
|
|
NTSTATUS
|
|
DlcOpenSap(
|
|
IN PIRP pIrp,
|
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|
IN PNT_DLC_PARMS pDlcParms,
|
|
IN ULONG InputBufferLength,
|
|
IN ULONG OutputBufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Procedure implements DLC.OPEN.SAP function in DLC API.
|
|
This implements DLC.OPEN.SAP.
|
|
|
|
Arguments:
|
|
|
|
pIrp - current io request packet
|
|
pFileContext - DLC adapter context
|
|
pDlcParms - the current parameter block
|
|
InputBufferLength - the length of input parameters
|
|
OutputBufferLength - not used
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS:
|
|
Success - STATUS_SUCCESS
|
|
Failure - DLC_STATUS_NO_MEMORY
|
|
|
|
--*/
|
|
|
|
{
|
|
PDLC_OBJECT pDlcObject;
|
|
UINT SapIndex = (pDlcParms->DlcOpenSap.SapValue >> 1);
|
|
UINT Status;
|
|
USHORT XidHandlingOption;
|
|
|
|
UNREFERENCED_PARAMETER(pIrp);
|
|
UNREFERENCED_PARAMETER(InputBufferLength);
|
|
UNREFERENCED_PARAMETER(OutputBufferLength);
|
|
|
|
DIAG_FUNCTION("DlcOpenSap");
|
|
|
|
//
|
|
// The group saps do not have any open/close context in NT DLC,
|
|
// but there is an group sap object on data link level.
|
|
// The individual sap is registered to all its group saps
|
|
// and llc level automatically routes all packets sent to
|
|
// a group sap to all its registered members. The groups saps
|
|
// are actually always open and they disappear automatically,
|
|
// when there are no references to them any more.
|
|
//
|
|
|
|
if (!(pDlcParms->DlcOpenSap.OptionsPriority &
|
|
(LLC_INDIVIDUAL_SAP | LLC_MEMBER_OF_GROUP_SAP | LLC_GROUP_SAP))) {
|
|
|
|
//
|
|
// Richard!!!!
|
|
// IBM spec says, that one of those bits must be set, on the
|
|
// other hand, Mike Allmond said, that IBM DLC accepts these
|
|
// command. I don't belive it before a DOS application
|
|
// tries to open dlc sap with all bits reset, then you
|
|
// must accept it as a undocumented feature of IBM DLC.
|
|
//
|
|
|
|
return DLC_STATUS_INVALID_OPTION;
|
|
} else if (!(pDlcParms->DlcOpenSap.OptionsPriority &
|
|
(LLC_INDIVIDUAL_SAP | LLC_MEMBER_OF_GROUP_SAP))) {
|
|
|
|
//
|
|
// It was a group sap, they do not have an open context,
|
|
// but their llc objects are created when they are referenced.
|
|
//
|
|
|
|
pDlcParms->DlcOpenSap.StationId = (USHORT)(((USHORT)pDlcParms->DlcOpenSap.SapValue << 8) | 0x0100);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// The lowest byte in sap value is undefine, we must reset
|
|
// it to make it a valid individual DLC SAP number.
|
|
//
|
|
|
|
pDlcParms->DlcOpenSap.SapValue &= 0xfe;
|
|
|
|
//
|
|
// Check the double open, the slot must be empty
|
|
//
|
|
|
|
if (SapIndex == 0
|
|
|| SapIndex >= MAX_SAP_STATIONS
|
|
|| pFileContext->SapStationTable[SapIndex] != NULL) {
|
|
return DLC_STATUS_INVALID_SAP_VALUE;
|
|
}
|
|
|
|
//
|
|
// All DLC objects have the same size and they are allocated from
|
|
// the packet pool (the normal binary buddy allocation has an average
|
|
// 33% overhead).
|
|
//
|
|
|
|
pDlcObject = (PDLC_OBJECT)ALLOCATE_PACKET_DLC_OBJ(pFileContext->hLinkStationPool);
|
|
|
|
if (pDlcObject) {
|
|
pFileContext->SapStationTable[SapIndex] = pDlcObject;
|
|
} else {
|
|
return DLC_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// We should do here some security checking using the security
|
|
// descriptor of the current file context, but we do
|
|
// not yet care about those things (nbf must implement
|
|
// them first!)
|
|
//
|
|
|
|
pDlcObject->pFileContext = pFileContext;
|
|
pDlcObject->Type = DLC_SAP_OBJECT;
|
|
pDlcParms->DlcOpenSap.StationId = pDlcObject->StationId = (USHORT)pDlcParms->DlcOpenSap.SapValue << 8;
|
|
pDlcObject->u.Sap.OptionsPriority = pDlcParms->DlcOpenSap.OptionsPriority;
|
|
pDlcObject->u.Sap.DlcStatusFlag = pDlcParms->DlcOpenSap.DlcStatusFlag;
|
|
pDlcObject->u.Sap.UserStatusValue = pDlcParms->DlcOpenSap.UserStatusValue;
|
|
pDlcObject->u.Sap.MaxStationCount = pDlcParms->DlcOpenSap.StationCount;
|
|
pDlcParms->DlcOpenSap.AvailableStations = pFileContext->LinkStationCount;
|
|
|
|
XidHandlingOption = 0;
|
|
if (!(pDlcObject->u.Sap.OptionsPriority & (UCHAR)XID_HANDLING_BIT)) {
|
|
XidHandlingOption = LLC_HANDLE_XID_COMMANDS;
|
|
}
|
|
Status = LlcOpenSap(pFileContext->pBindingContext,
|
|
pDlcObject,
|
|
(UINT)pDlcParms->DlcOpenSap.SapValue,
|
|
XidHandlingOption,
|
|
&pDlcObject->hLlcObject
|
|
);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
//
|
|
// We will save the access priority bits with the other
|
|
// link station parameters using LlcSetInformation.
|
|
//
|
|
|
|
pDlcParms->DlcOpenSap.LinkParameters.TokenRingAccessPriority = pDlcParms->DlcOpenSap.OptionsPriority & (UCHAR)0xE0;
|
|
|
|
//
|
|
// We know, that there will be no call backs from this
|
|
// set information function => we don't need to release spin
|
|
// locks.
|
|
//
|
|
|
|
LEAVE_DLC(pFileContext);
|
|
|
|
Status = LlcSetInformation(pDlcObject->hLlcObject,
|
|
DLC_INFO_CLASS_LINK_STATION,
|
|
(PLLC_SET_INFO_BUFFER)&(pDlcParms->DlcOpenSap.LinkParameters),
|
|
sizeof(DLC_LINK_PARAMETERS)
|
|
);
|
|
|
|
ENTER_DLC(pFileContext);
|
|
}
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
//
|
|
// The global group SAP (0xFF) is opened for all sap
|
|
// stations of dlc api.
|
|
// BUG-BUG-BUG: How incompatible XID handling options are
|
|
// handled in the case of the global group sap.
|
|
//
|
|
|
|
Status = LlcOpenSap(pFileContext->pBindingContext,
|
|
pDlcObject,
|
|
0xff,
|
|
LLC_HANDLE_XID_COMMANDS,
|
|
&pDlcObject->u.Sap.GlobalGroupSapHandle
|
|
);
|
|
}
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
//
|
|
// Each SAP station 'allocates' a fixed number link stations.
|
|
// This compatibility feature was implemented, because
|
|
// some dlc apps might assume to be have only a fixed number
|
|
// of link stations. We can't do the check earlier, because
|
|
// another DlcOpenSap command would have been able to allocate
|
|
// the link stations before this moment.
|
|
//
|
|
|
|
if (pDlcParms->DlcOpenSap.StationCount > pFileContext->LinkStationCount) {
|
|
Status = DLC_STATUS_INADEQUATE_LINKS;
|
|
} else {
|
|
pFileContext->LinkStationCount -= pDlcObject->u.Sap.MaxStationCount;
|
|
pDlcObject->State = DLC_OBJECT_OPEN;
|
|
pFileContext->DlcObjectCount++;
|
|
|
|
//
|
|
// The flag and these reference counters keeps the llc object
|
|
// alive, when we are working on it. We decerement
|
|
// the llc object reference count when we don't have any more
|
|
// synchronous commands going on. Zero llc reference count
|
|
// on Dlc object dereferences the llc object.
|
|
//
|
|
|
|
pDlcObject->LlcObjectExists = TRUE;
|
|
ReferenceLlcObject(pDlcObject);
|
|
LlcReferenceObject(pDlcObject->hLlcObject);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// error handling
|
|
//
|
|
|
|
pDlcParms->DlcOpenSap.AvailableStations = pFileContext->LinkStationCount;
|
|
if (pDlcObject->hLlcObject != NULL) {
|
|
pDlcObject->PendingLlcRequests++;
|
|
|
|
LEAVE_DLC(pFileContext);
|
|
|
|
LlcCloseStation(pDlcObject->hLlcObject, NULL);
|
|
if (pDlcObject->u.Sap.GlobalGroupSapHandle != NULL) {
|
|
LlcCloseStation(pDlcObject->u.Sap.GlobalGroupSapHandle, NULL);
|
|
}
|
|
|
|
ENTER_DLC(pFileContext);
|
|
}
|
|
|
|
#if LLC_DBG
|
|
pDlcObject->pLinkStationList = NULL;
|
|
#endif
|
|
|
|
DEALLOCATE_PACKET_DLC_OBJ(pFileContext->hLinkStationPool, pDlcObject);
|
|
|
|
pFileContext->SapStationTable[SapIndex] = NULL;
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DirOpenDirect(
|
|
IN PIRP pIrp,
|
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|
IN PNT_DLC_PARMS pDlcParms,
|
|
IN ULONG InputBufferLength,
|
|
IN ULONG OutputBufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Procedure opens the only direct station for a process specific adapter
|
|
context. This implements DIR.OPEN.STATION.
|
|
|
|
Arguments:
|
|
|
|
pIrp - current io request packet
|
|
pFileContext - DLC adapter context
|
|
pDlcParms - the current parameter block
|
|
InputBufferLength - the length of input parameters
|
|
OutputBufferLength - the length of output parameters
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS:
|
|
STATUS_SUCCESS
|
|
DLC_STATUS_NO_MEMORY
|
|
DLC_STATUS_DIRECT_STATIONS_NOT_AVAILABLE
|
|
--*/
|
|
|
|
{
|
|
PDLC_OBJECT pDlcObject;
|
|
UINT Status;
|
|
|
|
UNREFERENCED_PARAMETER(pIrp);
|
|
UNREFERENCED_PARAMETER(InputBufferLength);
|
|
UNREFERENCED_PARAMETER(OutputBufferLength);
|
|
|
|
//
|
|
// Check the double open, the slot must be empty
|
|
//
|
|
|
|
if (pFileContext->SapStationTable[0] != NULL) {
|
|
return DLC_STATUS_DIRECT_STATIONS_NOT_AVAILABLE;
|
|
}
|
|
|
|
//
|
|
// All DLC objects are allocated from the same size
|
|
// optimized pool (the std binary buddy has ave. 33% overhead).
|
|
//
|
|
|
|
pDlcObject = pFileContext->SapStationTable[0] = (PDLC_OBJECT)ALLOCATE_PACKET_DLC_OBJ(pFileContext->hLinkStationPool);
|
|
|
|
if (pDlcObject == NULL) {
|
|
return DLC_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// We should do here some security checking, but we do
|
|
// not care about those things now (nbf must implement
|
|
// them first!)
|
|
//
|
|
|
|
pDlcObject->pFileContext = pFileContext;
|
|
pDlcObject->Type = DLC_DIRECT_OBJECT;
|
|
pDlcObject->StationId = 0;
|
|
pDlcObject->State = DLC_OBJECT_OPEN;
|
|
|
|
LEAVE_DLC(pFileContext);
|
|
|
|
if (pDlcParms->DirOpenDirect.usEthernetType > 1500) {
|
|
|
|
//
|
|
// Open a dix station to receive the defined frames
|
|
//
|
|
|
|
Status = LlcOpenDixStation(pFileContext->pBindingContext,
|
|
(PVOID)pDlcObject,
|
|
pDlcParms->DirOpenDirect.usEthernetType,
|
|
&pDlcObject->hLlcObject
|
|
);
|
|
|
|
pDlcObject->u.Direct.ProtocolTypeMask = pDlcParms->DirOpenDirect.ulProtocolTypeMask;
|
|
pDlcObject->u.Direct.ProtocolTypeMatch = pDlcParms->DirOpenDirect.ulProtocolTypeMatch;
|
|
pDlcObject->u.Direct.ProtocolTypeOffset = pDlcParms->DirOpenDirect.usProtocolTypeOffset;
|
|
} else {
|
|
|
|
//
|
|
// Open a dix station to receive the defined frames
|
|
//
|
|
|
|
Status = LlcOpenDirectStation(pFileContext->pBindingContext,
|
|
(PVOID)pDlcObject,
|
|
0,
|
|
&pDlcObject->hLlcObject
|
|
);
|
|
}
|
|
|
|
ENTER_DLC(pFileContext);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
//
|
|
// The flag and these reference counters keeps the llc object
|
|
// alive, when we are working on it. We decerement
|
|
// the llc object reference count when we don't have any more
|
|
// synchronous commands going on. Zero llc reference count
|
|
// on Dlc object dereferences the llc object.
|
|
//
|
|
|
|
pDlcObject->LlcObjectExists = TRUE;
|
|
ReferenceLlcObject(pDlcObject);
|
|
LlcReferenceObject(pDlcObject->hLlcObject);
|
|
|
|
//
|
|
// We will receive ALL mac frame types if any of the
|
|
// mac bits has been set in the open options.
|
|
//
|
|
|
|
if (pDlcParms->DirOpenDirect.usOpenOptions & LLC_DIRECT_OPTIONS_ALL_MACS) {
|
|
pDlcObject->u.Direct.OpenOptions = (USHORT)(-1);
|
|
} else {
|
|
pDlcObject->u.Direct.OpenOptions = (USHORT)~DLC_RCV_MAC_FRAMES;
|
|
}
|
|
pFileContext->DlcObjectCount++;
|
|
} else {
|
|
pFileContext->SapStationTable[0] = NULL;
|
|
|
|
#if LLC_DBG
|
|
pDlcObject->pLinkStationList = NULL;
|
|
#endif
|
|
|
|
DEALLOCATE_PACKET_DLC_OBJ(pFileContext->hLinkStationPool, pDlcObject);
|
|
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DlcOpenLinkStation(
|
|
IN PIRP pIrp,
|
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|
IN PNT_DLC_PARMS pDlcParms,
|
|
IN ULONG InputBufferLength,
|
|
IN ULONG OutputBufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Procedure opens a new link station. DlcConnect is still needed to
|
|
create the actual connection to the remote node.
|
|
This implements DLC.OPEN.STATION
|
|
|
|
Arguments:
|
|
|
|
pIrp - current io request packet
|
|
pFileContext - DLC adapter context
|
|
pDlcParms - the current parameter block
|
|
InputBufferLength - the length of input parameters
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS:
|
|
STATUS_SUCCESS
|
|
DLC_STATUS_NO_MEMORY
|
|
DLC_STATUS_INADEQUATE_LINKS
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PDLC_OBJECT pDlcObject;
|
|
|
|
UNREFERENCED_PARAMETER(pIrp);
|
|
UNREFERENCED_PARAMETER(InputBufferLength);
|
|
UNREFERENCED_PARAMETER(OutputBufferLength);
|
|
|
|
//
|
|
// The local SAP must not be the NULL SAP or a group SAP!
|
|
//
|
|
|
|
if ((pDlcParms->DlcOpenStation.LinkStationId & 0x100) != 0
|
|
|| pDlcParms->DlcOpenStation.LinkStationId == 0) {
|
|
return DLC_STATUS_INVALID_SAP_VALUE;
|
|
}
|
|
|
|
//
|
|
// This must be a SAP station!
|
|
//
|
|
|
|
Status = GetSapStation(pFileContext,
|
|
pDlcParms->DlcOpenStation.LinkStationId,
|
|
&pDlcObject
|
|
);
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
//
|
|
// Check the remote destination address, the broadcast bit
|
|
// must not be set in that address. The remote SAP must not
|
|
// either be a group or nul SAP
|
|
//
|
|
|
|
if ((pDlcParms->DlcOpenStation.aRemoteNodeAddress[0] & 0x80) != 0
|
|
|| pDlcParms->DlcOpenStation.RemoteSap == 0
|
|
|| (pDlcParms->DlcOpenStation.RemoteSap & 1) != 0) {
|
|
return DLC_STATUS_INVALID_REMOTE_ADDRESS;
|
|
}
|
|
Status = InitializeLinkStation(pFileContext,
|
|
pDlcObject,
|
|
pDlcParms,
|
|
NULL, // this is a local connect, no LLC link handle
|
|
&pDlcObject
|
|
);
|
|
|
|
//
|
|
// Set also the link station parameters, all nul
|
|
// parameters are discarded by the data link.
|
|
//
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
LlcSetInformation(
|
|
pDlcObject->hLlcObject,
|
|
DLC_INFO_CLASS_LINK_STATION,
|
|
(PLLC_SET_INFO_BUFFER)&pDlcParms->DlcOpenStation.LinkParameters,
|
|
sizeof(DLC_LINK_PARAMETERS)
|
|
);
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
InitializeLinkStation(
|
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|
IN PDLC_OBJECT pSap,
|
|
IN PNT_DLC_PARMS pDlcParms OPTIONAL,
|
|
IN PVOID LlcLinkHandle OPTIONAL,
|
|
OUT PDLC_OBJECT *ppLinkStation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure allocates and initializes the link station.
|
|
|
|
Arguments:
|
|
|
|
pFileContext - DLC adapter context
|
|
pSap - Sap object of the new link station
|
|
pDlcParms - the current parameter block
|
|
LlcHandle - Handle
|
|
ppLinkStation - the new created link station
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS:
|
|
Success - STATUS_SUCCESS
|
|
Failure - DLC_STATUS_NO_MEMORY
|
|
DLC_STATUS_INADEQUATE_LINKS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PDLC_OBJECT pLinkStation;
|
|
UINT LinkIndex;
|
|
|
|
//
|
|
// There is allocated a limited number link stations for
|
|
// this SAP, check if there is available link stations
|
|
//
|
|
|
|
if (pSap->u.Sap.LinkStationCount >= pSap->u.Sap.MaxStationCount) {
|
|
return DLC_STATUS_INADEQUATE_LINKS;
|
|
}
|
|
|
|
//
|
|
// Search the first free link station id
|
|
//
|
|
|
|
for (LinkIndex = 0;
|
|
LinkIndex < MAX_LINK_STATIONS
|
|
&& pFileContext->LinkStationTable[LinkIndex] != NULL;
|
|
LinkIndex++) {
|
|
; // NOP
|
|
}
|
|
|
|
//#ifdef DEBUG_CHK
|
|
// //
|
|
// // Link counters are out of sync ????
|
|
// //
|
|
// if (LinkIndex == MAX_LINK_STATIONS)
|
|
// {
|
|
// DEBUG_ERROR("DLC: Linkstation counters are out of sync!");
|
|
// return DLC_STATUS_INADEQUATE_LINKS;
|
|
// }
|
|
//#endif
|
|
|
|
//
|
|
// Allocate the link station and initialize the station id field
|
|
//
|
|
|
|
pLinkStation = ALLOCATE_PACKET_DLC_OBJ(pFileContext->hLinkStationPool);
|
|
|
|
if (pLinkStation == NULL) {
|
|
return DLC_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Each link station has a preallocated event packet to receive
|
|
// all DLC Status indications from the data link. There can
|
|
// be several status indications set in the same status word.
|
|
// The status is reset when its read from the event queue.
|
|
//
|
|
|
|
pLinkStation->u.Link.pStatusEvent = ALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool);
|
|
|
|
if (pLinkStation->u.Link.pStatusEvent == NULL) {
|
|
|
|
#if LLC_DBG
|
|
pLinkStation->pLinkStationList = NULL;
|
|
#endif
|
|
|
|
DEALLOCATE_PACKET_DLC_OBJ(pFileContext->hLinkStationPool, pLinkStation);
|
|
|
|
return DLC_STATUS_NO_MEMORY;
|
|
}
|
|
*ppLinkStation = pLinkStation;
|
|
pFileContext->LinkStationTable[LinkIndex] = pLinkStation;
|
|
pLinkStation->StationId = pSap->StationId | (USHORT)(LinkIndex + 1);
|
|
pLinkStation->Type = DLC_LINK_OBJECT;
|
|
pLinkStation->State = DLC_OBJECT_OPEN;
|
|
pLinkStation->pFileContext = pFileContext;
|
|
pSap->u.Sap.LinkStationCount++;
|
|
|
|
//
|
|
// Check if this is local or remote connection request, the remote
|
|
// connection requests have already created an LLC link object
|
|
//
|
|
|
|
if (LlcLinkHandle == NULL) {
|
|
|
|
//
|
|
// local connection request
|
|
//
|
|
|
|
Status = LlcOpenLinkStation(pSap->hLlcObject, // SAP handle!
|
|
pDlcParms->DlcOpenStation.RemoteSap,
|
|
pDlcParms->DlcOpenStation.aRemoteNodeAddress,
|
|
NULL,
|
|
pLinkStation,
|
|
&pLinkStation->hLlcObject
|
|
);
|
|
if (Status != STATUS_SUCCESS) {
|
|
|
|
//
|
|
// It didn't work for some reason, we are probably out of memory.
|
|
// Free the slot in the link station table.
|
|
//
|
|
|
|
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pLinkStation->u.Link.pStatusEvent);
|
|
|
|
#if LLC_DBG
|
|
pLinkStation->pLinkStationList = NULL;
|
|
#endif
|
|
|
|
DEALLOCATE_PACKET_DLC_OBJ(pFileContext->hLinkStationPool, pLinkStation);
|
|
|
|
pFileContext->LinkStationTable[LinkIndex] = NULL;
|
|
pSap->u.Sap.LinkStationCount--;
|
|
return Status;
|
|
}
|
|
pDlcParms->DlcOpenStation.LinkStationId = pLinkStation->StationId;
|
|
} else {
|
|
|
|
//
|
|
// remote connection request
|
|
//
|
|
|
|
pLinkStation->hLlcObject = LlcLinkHandle;
|
|
|
|
//
|
|
// We must give the upper protocol handle to the link station,
|
|
// otherwise the system bug checks, when the link is closed
|
|
// before it is connected.
|
|
//
|
|
|
|
LlcBindLinkStation(LlcLinkHandle, pLinkStation);
|
|
}
|
|
|
|
//
|
|
// The flag and these reference counters keeps the LLC object
|
|
// alive, when we are working on it. We decerement
|
|
// the LLC object reference count when we don't have any more
|
|
// synchronous commands going on. Zero LLC reference count
|
|
// on DLC object dereferences the LLC object
|
|
//
|
|
|
|
pLinkStation->LlcObjectExists = TRUE;
|
|
ReferenceLlcObject(pLinkStation);
|
|
LlcReferenceObject(pLinkStation->hLlcObject);
|
|
|
|
//
|
|
// Link this link station to the link list of all
|
|
// link stations belonging to this sap (!?)
|
|
//
|
|
|
|
pFileContext->DlcObjectCount++;
|
|
pLinkStation->u.Link.pSap = pSap;
|
|
pLinkStation->pLinkStationList = pSap->pLinkStationList;
|
|
pSap->pLinkStationList = pLinkStation;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DlcCloseStation(
|
|
IN PIRP pIrp,
|
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|
IN PNT_DLC_PARMS pDlcParms,
|
|
IN ULONG InputBufferLength,
|
|
IN ULONG OutputBufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Procedure closes a link, SAP or direct station. This implements
|
|
DLC.CLOSE.STATION, DLC.CLOSE.SAP and DIR.CLOSE.DIRECT
|
|
|
|
Arguments:
|
|
|
|
pIrp - current io request packet
|
|
pFileContext - DLC adapter context
|
|
pDlcParms - the current parameter block
|
|
InputBufferLength - the length of input parameters
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS:
|
|
Success - STATUS_SUCCESS
|
|
STATUS_PENDING
|
|
Failure - DLC_STATUS_NO_MEMORY
|
|
|
|
--*/
|
|
|
|
{
|
|
PDLC_OBJECT pDlcObject;
|
|
NTSTATUS Status;
|
|
PDLC_CLOSE_WAIT_INFO pClosingInfo;
|
|
|
|
UNREFERENCED_PARAMETER(InputBufferLength);
|
|
UNREFERENCED_PARAMETER(OutputBufferLength);
|
|
|
|
DIAG_FUNCTION("DlcCloseStation");
|
|
|
|
//
|
|
// It's OK to close any group sap id (we don't test them, because
|
|
// those objects exists only in llc driver. The full implementation
|
|
// of group saps would make this driver just too complicated)
|
|
//
|
|
|
|
if (pDlcParms->Async.Ccb.u.dlc.usStationId & 0x0100) {
|
|
CompleteDirectOutIrp(pIrp, STATUS_SUCCESS, NULL);
|
|
CompleteAsyncCommand(pFileContext, STATUS_SUCCESS, pIrp, NULL, FALSE);
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
//
|
|
// 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->Async.Ccb.u.dlc.usStationId,
|
|
&pDlcObject
|
|
);
|
|
if (Status != STATUS_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Sap station cannot be closed until its all link stations
|
|
// have been closed.
|
|
//
|
|
|
|
if ((pDlcObject->Type == DLC_SAP_OBJECT)
|
|
&& (pDlcObject->u.Sap.LinkStationCount != 0)) {
|
|
return DLC_STATUS_LINK_STATIONS_OPEN;
|
|
}
|
|
|
|
pClosingInfo = ALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool);
|
|
|
|
if (pClosingInfo == NULL) {
|
|
return DLC_STATUS_NO_MEMORY;
|
|
}
|
|
pClosingInfo->pIrp = pIrp;
|
|
pClosingInfo->CloseCounter = 0;
|
|
pClosingInfo->Event = DLC_COMMAND_COMPLETION;
|
|
pClosingInfo->CancelStatus = DLC_STATUS_CANCELLED_BY_USER;
|
|
pClosingInfo->CancelReceive = TRUE;
|
|
|
|
if (pDlcParms->Async.Ccb.CommandCompletionFlag != 0) {
|
|
pClosingInfo->ChainCommands = TRUE;
|
|
SearchReadCommandForClose(pFileContext,
|
|
pClosingInfo,
|
|
pDlcParms->Async.Ccb.pCcbAddress,
|
|
pDlcParms->Async.Ccb.CommandCompletionFlag,
|
|
pDlcObject->StationId,
|
|
(USHORT)DLC_STATION_MASK_SPECIFIC
|
|
);
|
|
}
|
|
|
|
CloseAnyStation(pDlcObject, pClosingInfo, FALSE);
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
/*++
|
|
|
|
Design notes about the dlc close commands
|
|
-----------------------------------------
|
|
|
|
All close operation must wait until the pending NDIS transmits
|
|
have been completed. Thus they are asynchronous commands.
|
|
The close commands of a DLC object will also return all
|
|
CCBs of the pending commands and the received frames not yet read
|
|
with the read command. The normal non-urgent close commands must
|
|
wait also the queued DLC transmit to complete.
|
|
|
|
|
|
There are three functions closing the dlc stations:
|
|
- DlcCloseObject
|
|
- DlcReset (sap and its link stations or all link stations)
|
|
- DirCloseAdapter (almost same as reset, except marks the file object closed,
|
|
the actual file close disconnects the NDIS adapter.
|
|
|
|
All higher level functions allocates close completion data structure,
|
|
that is used to complete the command, when there are no pending
|
|
transmits in the associated stations.
|
|
|
|
The lower level functions
|
|
CloseAllStations - closes all open sap stations and the only direct station
|
|
CloseAnyStation - initializes the close of any station,
|
|
for a sap station it also calls recursively the
|
|
link stations.
|
|
CloseStation - closes the object immediately or waits
|
|
until transmits have been completed and then
|
|
does the same thing again.
|
|
|
|
|
|
About the simultaneous reset and close commands
|
|
-------------------------------------------------
|
|
|
|
There are some very difficult problems with the
|
|
simultaneous reset and close commands: each command
|
|
must wait until all dlc objects associated with the command
|
|
has been closed before the command itself cab be completed.
|
|
It is completely legal to make first close for a station, then
|
|
reset the sap of the same station (before the close completes) and
|
|
then reset all sap stations (before the sap reset completes).
|
|
On the other hand a dlc object can be linked only to one
|
|
close/reset command and in this case all three commands should wait
|
|
the completion of the same link station.
|
|
|
|
//Solution 1 (a bad one):
|
|
//There can be any number aof simultaneous close commands, because it
|
|
//can be done to a dlc object only if it has no open substations.
|
|
//There must be no other pending reset or close commands when a reset
|
|
//command is executed, because some of its substations may already
|
|
//be closing and they cannot be linked to the reset command.
|
|
//
|
|
//=>
|
|
//We have a global close/reset command counter and a link list for the
|
|
//pending reset commands. A close command can be given in any time
|
|
//(even during a reset command, because all stations associated with
|
|
//a reset are already closed and cannot be closed again).
|
|
//A reset can be executed only if the global close/reset is zero.
|
|
//The pending resets are queued. The close command completion
|
|
//routines executes the first reset command from the queue, if the
|
|
//!! Reset command must immediately mark all associated stations
|
|
// closed to prevent any further use of those commands.
|
|
|
|
Solution 2 (the final solution):
|
|
-------------------------------
|
|
|
|
The sequential and simultaneous close and reset commands can be given
|
|
only to the dlc object being higher (or same) level than the destination
|
|
of the previous command (close link, reset sap, reset all saps, close adapter)
|
|
=> close/reset events can be linked in the dlc objects (simultaneous
|
|
close commands creates a split tree).
|
|
The counters in all close/reset events are decremented and possibly
|
|
executed when the dlc object is closed (when all transmits have been
|
|
completed and the link stations disconnected).
|
|
|
|
|
|
//
|
|
// IBM has implemented the different close commands in this way:
|
|
//
|
|
// 1. DIR.CLOSE.DIRECT
|
|
// - Undefined, I will do the same as with DLC.CLOSE.X commands
|
|
// 2. DLC.CLOSE.SAP, DLC.CLOSE.STATION
|
|
// - receive ccb linked to next ccb field without completion flag,
|
|
// the receive command is completed normally, only return code
|
|
// is set.
|
|
// - all terminated commands linked after the receive ccb if
|
|
// completion flag was defined and command itself read by
|
|
// READ from the compeltion list.
|
|
// The terminated commands (except receive) are completed normally.
|
|
// 3. DLC.RESET
|
|
// - The terminated pending CCBs are linked only if command completion
|
|
// flag was defined.
|
|
// 4. DIR.CLOSE.ADAPTER, DIR.INITIALIZE
|
|
// - the terminated CCBs linked to the next ccb field of the command.
|
|
// The command itself can be read with read command, if defined.
|
|
//
|
|
// (and now we do the same (12-FEB-1992))
|
|
|
|
Note: all close functions returns VOID, because they can never fail.
|
|
A hanging close command hangs up the process or event the whole system.
|
|
We have a problem: the llc adapter may be closed, while there are
|
|
still active LLC command packets pending in the NDIS queues => the
|
|
NDIS adapter close must wait (sleep and loop) until its all objects
|
|
have been deleted.
|
|
|
|
--*/
|
|
|
|
|
|
BOOLEAN
|
|
CloseAllStations(
|
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|
IN PIRP pIrp OPTIONAL,
|
|
IN ULONG Event,
|
|
IN PFCLOSE_COMPLETE pfCloseComplete OPTIONAL,
|
|
IN PNT_DLC_PARMS pDlcParms OPTIONAL,
|
|
IN PDLC_CLOSE_WAIT_INFO pClosingInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes closing all DLC stations. The command is
|
|
asynchronously completed when all pending transmits have been sent
|
|
and the link stations have been disconnected
|
|
|
|
Arguments:
|
|
|
|
pFileContext - process specific device context
|
|
pIrp - the i.o request packet of the related command
|
|
Event - event flag used to search a matching read for this command
|
|
pfCloseComplete - completion appendage used when the DLC driver (or its
|
|
process context) is closed.
|
|
pDlcParms - DLC parameters from original system call
|
|
pClosingInfo - pointer to DLC_CLOSE_WAIT_INFO structure
|
|
|
|
Return Value:
|
|
|
|
None - this must succeed always!
|
|
|
|
--*/
|
|
|
|
{
|
|
PDLC_PACKET pPacket;
|
|
USHORT i;
|
|
USHORT FirstSap;
|
|
BOOLEAN DoImmediateClose;
|
|
NT_DLC_CCB AsyncCcb;
|
|
|
|
ASSUME_IRQL(DISPATCH_LEVEL);
|
|
|
|
if (pDlcParms == NULL) {
|
|
|
|
//
|
|
// Adapter Close always returns a pending status!
|
|
// It completes the io- request by itself.
|
|
//
|
|
|
|
pDlcParms = (PNT_DLC_PARMS)&AsyncCcb;
|
|
LlcZeroMem(&AsyncCcb, sizeof(AsyncCcb));
|
|
}
|
|
|
|
pClosingInfo->pIrp = pIrp;
|
|
pClosingInfo->CloseCounter = 1; // keep object alive during sync path
|
|
pClosingInfo->Event = Event;
|
|
pClosingInfo->pfCloseComplete = pfCloseComplete;
|
|
pClosingInfo->CancelStatus = (ULONG)DLC_STATUS_CANCELLED_BY_SYSTEM_ACTION;
|
|
|
|
//
|
|
// We zero the memory by default:
|
|
// pClosingInfo->pCcbLink = NULL
|
|
// pClosingInfo->pReadCommand = NULL
|
|
//
|
|
|
|
//
|
|
// DLC.RESET must not close the direct station.
|
|
// This flag is false for DLC.RESET but set for DIR.CLOSE.ADAPTER etc.
|
|
//
|
|
|
|
if (pDlcParms->Async.Ccb.uchDlcCommand == LLC_DLC_RESET) {
|
|
FirstSap = 1; // don't delete the direct station
|
|
DoImmediateClose = FALSE;
|
|
pClosingInfo->CancelStatus = DLC_STATUS_CANCELLED_BY_USER;
|
|
} else {
|
|
FirstSap = 0; // if DIR.CLOSE.ADAPTER, can close everything
|
|
DoImmediateClose = TRUE;
|
|
pClosingInfo->ClosingAdapter = TRUE;
|
|
}
|
|
|
|
//
|
|
// Chain the cancelled CCBs to the adapter close command or to a closing
|
|
// event, if this is a global close command for adapter or a normal close
|
|
// command (dlc.close.xxx, dlc.reset, dir.close.station) with a command
|
|
// completion flag
|
|
//
|
|
|
|
if (Event == LLC_CRITICAL_EXCEPTION || pDlcParms->Async.Ccb.CommandCompletionFlag != 0) {
|
|
pClosingInfo->ChainCommands = TRUE;
|
|
SearchReadCommandForClose(pFileContext,
|
|
pClosingInfo,
|
|
pDlcParms->Async.Ccb.pCcbAddress,
|
|
pDlcParms->Async.Ccb.CommandCompletionFlag,
|
|
0,
|
|
0 // search commands for all station ids
|
|
);
|
|
}
|
|
|
|
//
|
|
// This flag has been set, when user has issued DIR.INITIALIZE command,
|
|
// that makes the hard reset for NDIS
|
|
//
|
|
|
|
if (pDlcParms->Async.Ccb.uchDlcCommand == LLC_DIR_INITIALIZE) {
|
|
|
|
//
|
|
// We cannot stop to closing, if the memory allocation fails,
|
|
// It's better just to close the adapter without hard reset
|
|
// that to fail the whole DIR.INITIALIZE command.
|
|
//
|
|
|
|
pPacket = ALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool);
|
|
|
|
if (pPacket != NULL) {
|
|
pClosingInfo->CloseCounter++;
|
|
|
|
pPacket->ResetPacket.pClosingInfo = pClosingInfo;
|
|
|
|
//
|
|
// The reset command cancels all pending transmit requests!
|
|
// => the closing of the stations should be very fast
|
|
//
|
|
|
|
LEAVE_DLC(pFileContext);
|
|
|
|
LlcNdisReset(pFileContext->pBindingContext, &pPacket->LlcPacket);
|
|
|
|
ENTER_DLC(pFileContext);
|
|
|
|
}
|
|
}
|
|
if (pFileContext->DlcObjectCount != 0) {
|
|
for (i = FirstSap; i < MAX_SAP_STATIONS; i++) {
|
|
if (pFileContext->SapStationTable[i] != NULL) {
|
|
CloseAnyStation(pFileContext->SapStationTable[i],
|
|
pClosingInfo,
|
|
DoImmediateClose
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Complete close command, if this was the last reference
|
|
// of the close information
|
|
//
|
|
|
|
return DecrementCloseCounters(pFileContext, pClosingInfo);
|
|
}
|
|
|
|
|
|
VOID
|
|
CloseAnyStation(
|
|
IN PDLC_OBJECT pDlcObject,
|
|
IN PDLC_CLOSE_WAIT_INFO pClosingInfo,
|
|
IN BOOLEAN DoImmediateClose
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Procedure closes any station, and in the case of sap station it
|
|
also calls recursively sap's all link stations before
|
|
it closes the actual sap station.
|
|
|
|
Arguments:
|
|
|
|
pDlcObject - DLC object
|
|
pClosingInfo - the information needed for the close/reset command
|
|
completion (optionally by DLC read).
|
|
DoImmediateClose - flag set when the stations are closed immediately
|
|
|
|
Return Value:
|
|
|
|
None - this must succeed always!
|
|
|
|
--*/
|
|
|
|
{
|
|
PDLC_FILE_CONTEXT pFileContext = pDlcObject->pFileContext;
|
|
PDLC_CLOSE_WAIT_INFO pCurrentClosingInfo;
|
|
UINT i;
|
|
|
|
DIAG_FUNCTION("CloseAnyStation");
|
|
|
|
//
|
|
// first close all link stations on this SAP. This must be done before the
|
|
// object can be marked as deleted
|
|
//
|
|
|
|
if (pDlcObject->Type == DLC_SAP_OBJECT) {
|
|
|
|
BOOLEAN SapObjectIsBadFood = FALSE;
|
|
|
|
//
|
|
// Delete all link stations using the current sap station.
|
|
//
|
|
|
|
for (i = 0; i < MAX_LINK_STATIONS; i++) {
|
|
if (pFileContext->LinkStationTable[i] != NULL
|
|
&& pFileContext->LinkStationTable[i]->u.Link.pSap == pDlcObject) {
|
|
|
|
//
|
|
// the SAP object will be deleted when its last link object has
|
|
// been deleted
|
|
//
|
|
|
|
if (pDlcObject->State == DLC_OBJECT_CLOSING) {
|
|
SapObjectIsBadFood = TRUE;
|
|
}
|
|
CloseAnyStation(pFileContext->LinkStationTable[i],
|
|
pClosingInfo,
|
|
DoImmediateClose
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// it is highly probable that the SAP object is already deleted. The
|
|
// close info counter was also decremented because the current closing
|
|
// info packet was linked behind the old close packet by link station
|
|
// cleanup, then completed when the SAP was closed after its last link
|
|
// station was deleted
|
|
//
|
|
|
|
if (SapObjectIsBadFood) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We must queue simultaneous close/reset commands
|
|
//
|
|
|
|
if (pDlcObject->State == DLC_OBJECT_OPEN) {
|
|
pDlcObject->State = DLC_OBJECT_CLOSING;
|
|
pDlcObject->pClosingInfo = pClosingInfo;
|
|
|
|
//
|
|
// The close command has been queued, increment the counter
|
|
//
|
|
|
|
pClosingInfo->CloseCounter++;
|
|
} else {
|
|
|
|
//
|
|
// Queue all simultaneous close/reset commands to a spanning TREE
|
|
//
|
|
// The linked closing info packets creates a spanning tree.
|
|
// The newest (and stronges) close command is always the
|
|
// root node. Even stronger close command would combine
|
|
// the separate spanning trees to one single tree.
|
|
// The root commands are completed, when its all sub-stations
|
|
// have been closed and older commands have been completed.
|
|
//
|
|
// It is possible to have simultaneously pending:
|
|
// 1. Close link station (pending)
|
|
// 2. Close sap station (pending)
|
|
// 3. Reset sap station (pending)
|
|
// 4. Reset all saps with single command (pending)
|
|
// 5. Resetting NDIS adapter with DirInitialize
|
|
// OR Closing adapter with DirCloseAdapter
|
|
// OR close initiated by process exit
|
|
//
|
|
// The commands cannot be executed in the reverse order,
|
|
// because the stronger command would have already closed
|
|
// affected station(s) or adapter.
|
|
//
|
|
|
|
for (pCurrentClosingInfo = pDlcObject->pClosingInfo;
|
|
pCurrentClosingInfo != pClosingInfo;
|
|
pCurrentClosingInfo = pCurrentClosingInfo->pNext) {
|
|
|
|
if (pCurrentClosingInfo->pNext == NULL) {
|
|
pCurrentClosingInfo->pNext = pClosingInfo;
|
|
|
|
//
|
|
// We link this close packet to many other close commands,
|
|
// => we must add the count of all pending closing stations
|
|
// to the current packet
|
|
// (fix 28-03-1992, bug check when process exit during a
|
|
// pending dlc reset).
|
|
// (Bug-bug: close counter is not correct, when the previous
|
|
// close command is still in sync code path => dlc reset
|
|
// must decrement the next pointers in the queue).
|
|
//
|
|
|
|
pClosingInfo->CloseCounter += pCurrentClosingInfo->CloseCounter;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseStation(pFileContext, pDlcObject, DoImmediateClose);
|
|
}
|
|
|
|
|
|
VOID
|
|
CloseStation(
|
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|
IN PDLC_OBJECT pDlcObject,
|
|
IN BOOLEAN DoImmediateClose
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure starts the asychronous close of any DLC station
|
|
object. It creates an asynchronous command completion packet
|
|
for the station and returns.
|
|
|
|
Arguments:
|
|
|
|
pFileContext -
|
|
pDlcObject - DLC object
|
|
DoImmediateClose - the flag is set when the LLC object must be closed
|
|
immediately without waiting the pending transmits
|
|
|
|
Return Value:
|
|
|
|
None - this must succeed always!
|
|
|
|
--*/
|
|
|
|
{
|
|
PLLC_PACKET pClosePacket;
|
|
|
|
DIAG_FUNCTION("CloseStation");
|
|
|
|
//
|
|
// we must cancel all pending transmits immediately,
|
|
// when the adapter is closed.
|
|
//
|
|
|
|
if ((pDlcObject->State == DLC_OBJECT_CLOSING
|
|
&& (DoImmediateClose || pDlcObject->PendingLlcRequests == 0)
|
|
&& !(pDlcObject->Type == DLC_SAP_OBJECT && pDlcObject->u.Sap.LinkStationCount != 0))
|
|
|
|
||
|
|
|
|
//
|
|
// This condition forces the link to close even if
|
|
// there was a pending disconnect command (it may be
|
|
// waiting the other side and that may take a quite a while).
|
|
// Otherwise the exit of a DLC app may hung up to 5 - 60 seconds)
|
|
//
|
|
|
|
(DoImmediateClose
|
|
&& pDlcObject->hLlcObject != NULL
|
|
&& pDlcObject->Type == DLC_LINK_OBJECT)) {
|
|
|
|
//
|
|
// Llc objects can be closed in any phase of operation.
|
|
// The close command will cancel all transmit commands
|
|
// not yet queued to NDIS and returns an asynchronous
|
|
// completion status, when the pending NDIS commands
|
|
// have been completed. The CloseCompletion indication
|
|
// handler uses the same PendingLlcRequestser as
|
|
// with the normal pending transmit commands.
|
|
// The immediate close first closes the LLC object and then
|
|
// waits the pending transmits (=> waits only transmits
|
|
// queued on NDIS).
|
|
// A graceful close does it vice versa: it first waits
|
|
// pending transmits and then does the actual close.
|
|
//
|
|
|
|
ASSERT(pDlcObject->ClosePacketInUse == 0);
|
|
|
|
if (pDlcObject->ClosePacketInUse == 1) {
|
|
pClosePacket = ALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool);
|
|
|
|
if (pClosePacket == NULL) {
|
|
//
|
|
// We don't have enough memory to make a graceful closing,
|
|
// We must do it in a quick and dirty way.
|
|
// We cannoot either wait llc to acknowledge the
|
|
// close, because we don't have any close packet
|
|
//
|
|
DoImmediateClose = TRUE;
|
|
} else {
|
|
pDlcObject->PendingLlcRequests++;
|
|
}
|
|
} else {
|
|
pClosePacket = &pDlcObject->ClosePacket;
|
|
pDlcObject->ClosePacketInUse = 1;
|
|
pDlcObject->PendingLlcRequests++;
|
|
}
|
|
|
|
pDlcObject->State = DLC_OBJECT_CLOSED;
|
|
if (pDlcObject->Type == DLC_LINK_OBJECT && !DoImmediateClose) {
|
|
|
|
//
|
|
// LlcDisconnect completion routine will close the link
|
|
// station and call this routine again, when the
|
|
// link station routine completes.
|
|
// We must reference the LLC object before the operation,
|
|
// otherwise there is a very small time window, that allows
|
|
// LLC object to be deleted while the disconnect
|
|
// operation is going on (and crash the system).
|
|
// (I hate pointer based interfaces)
|
|
//
|
|
|
|
ReferenceLlcObject(pDlcObject);
|
|
|
|
LEAVE_DLC(pFileContext);
|
|
|
|
//
|
|
// Data link driver returns a synchronous status only if
|
|
// if cannot complete command asynchronously, because it
|
|
// doesn't have a handle to the DLC object (the link
|
|
// station has not yet been
|
|
//
|
|
|
|
LlcDisconnectStation(pDlcObject->hLlcObject, pClosePacket);
|
|
|
|
ENTER_DLC(pFileContext);
|
|
|
|
DereferenceLlcObject(pDlcObject);
|
|
} else {
|
|
|
|
//
|
|
// we must close the link station immediately, if we for
|
|
// some reason cannot disconnect it normally.
|
|
//
|
|
|
|
if (pDlcObject->LlcObjectExists == TRUE) {
|
|
pDlcObject->LlcObjectExists = FALSE;
|
|
|
|
LEAVE_DLC(pFileContext);
|
|
|
|
LlcCloseStation(pDlcObject->hLlcObject, pClosePacket);
|
|
|
|
ENTER_DLC(pFileContext);
|
|
|
|
DereferenceLlcObject(pDlcObject);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We must be able to close the driver even in out of memory conditions.
|
|
// LLC driver won't acknowledge the close if we connot allocate a packet
|
|
// for it
|
|
//
|
|
|
|
if (pClosePacket == NULL) {
|
|
CompleteCloseStation(pFileContext, pDlcObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
CompleteCloseStation(
|
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|
IN PDLC_OBJECT pDlcObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Procedure completes the close operation for any station object.
|
|
It also completes all close commands, that have been waiting
|
|
the closing of this station (or this station as the last member
|
|
of a group).
|
|
|
|
Arguments:
|
|
|
|
pFileContext - identifies owner of object
|
|
pDlcObject - dlc object
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// We must keep the LLC object alive, as far as there is any
|
|
// pending (transmit) commands in LLC.
|
|
//
|
|
|
|
if (pDlcObject->PendingLlcRequests == 0) {
|
|
|
|
//
|
|
// The station may still be waiting its transmit (and receive)
|
|
// commands to complete. We must poll the close station.
|
|
// if the status is still just closing.
|
|
//
|
|
|
|
if (pDlcObject->State == DLC_OBJECT_CLOSING) {
|
|
CloseStation(pFileContext, pDlcObject, FALSE);
|
|
} else {
|
|
|
|
PDLC_OBJECT pSapObject = NULL;
|
|
PDLC_CLOSE_WAIT_INFO pClosingInfo;
|
|
|
|
DLC_TRACE('N');
|
|
|
|
//
|
|
// The object must have been deleted from the file
|
|
// context when we enable spin lock in the next time,
|
|
// because the object is not any more in a consistent
|
|
// state.
|
|
//
|
|
|
|
if (pDlcObject->Type == DLC_LINK_OBJECT) {
|
|
|
|
DLC_TRACE('c');
|
|
|
|
//
|
|
// Remove the link station from the link station
|
|
// link list of its sap and link station table
|
|
// of the file context.
|
|
//
|
|
|
|
RemoveFromLinkList((PVOID *)&(pDlcObject->u.Link.pSap->pLinkStationList),
|
|
pDlcObject
|
|
);
|
|
pFileContext->LinkStationTable[(pDlcObject->StationId & 0xff) - 1] = NULL;
|
|
|
|
//
|
|
// Data link events have always the next pointer
|
|
// non-null, when they are in the event queue.
|
|
// The cleanup routine will remove and deallocate
|
|
// the packet when it is in the event queue.
|
|
//
|
|
|
|
if (pDlcObject->u.Link.pStatusEvent->LlcPacket.pNext == NULL) {
|
|
|
|
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool,
|
|
pDlcObject->u.Link.pStatusEvent
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Remove the possible memory committed by this link
|
|
// station during a buffer busy state. Normally
|
|
// the committed space is zero.
|
|
//
|
|
|
|
if (pFileContext->hBufferPool != NULL) {
|
|
BufUncommitBuffers(pFileContext->hBufferPool,
|
|
pDlcObject->CommittedBufferSpace
|
|
);
|
|
}
|
|
|
|
//
|
|
// The sap station must wait until its all link stations
|
|
// have been closed.
|
|
// Otherwise we will corrupt memory!!!!!!!!!
|
|
// ((would have been very hard and uncommon bug: reset
|
|
// of one station migth have corrupted a new dlc object
|
|
// created simultaneusly))
|
|
//
|
|
|
|
pDlcObject->u.Link.pSap->u.Sap.LinkStationCount--;
|
|
if (pDlcObject->u.Link.pSap->u.Sap.LinkStationCount == 0
|
|
&& pDlcObject->u.Link.pSap->State == DLC_OBJECT_CLOSING) {
|
|
pSapObject = pDlcObject->u.Link.pSap;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// SAP station must wait until its all link stations
|
|
// have been closed!
|
|
//
|
|
|
|
if (pDlcObject->Type == DLC_SAP_OBJECT) {
|
|
if (pDlcObject->u.Sap.LinkStationCount != 0) {
|
|
return;
|
|
}
|
|
|
|
DLC_TRACE('d');
|
|
|
|
//
|
|
// All link stations have now been deleted, we can return
|
|
// the sap's link stations back to the global pool.
|
|
// The group sap can be deleted also.
|
|
//
|
|
|
|
pFileContext->LinkStationCount += pDlcObject->u.Sap.MaxStationCount;
|
|
|
|
LEAVE_DLC(pFileContext);
|
|
|
|
LlcCloseStation(pDlcObject->u.Sap.GlobalGroupSapHandle, NULL);
|
|
|
|
ENTER_DLC(pFileContext);
|
|
|
|
//
|
|
// Delete all group saps defined for this sap station
|
|
//
|
|
|
|
SetupGroupSaps(pFileContext, pDlcObject, 0, NULL);
|
|
}
|
|
pFileContext->SapStationTable[pDlcObject->StationId >> 9] = NULL;
|
|
}
|
|
pFileContext->DlcObjectCount--;
|
|
|
|
//
|
|
// The first and most specific close command will get all
|
|
// received frames and the transmit chain of the deleted object.
|
|
//
|
|
|
|
CleanUpEvents(pFileContext, pDlcObject->pClosingInfo, pDlcObject);
|
|
|
|
//
|
|
// The parallel close/reset commands have been queued in a
|
|
// link list, We must decrement and notify all dlc objects
|
|
//
|
|
|
|
// DecrementCloseCounters(pFileContext, pDlcObject->pClosingInfo);
|
|
|
|
//
|
|
// It's best to deallocate event packet after the
|
|
// cleanup of the event queue
|
|
//
|
|
|
|
#if LLC_DBG
|
|
pDlcObject->pLinkStationList = NULL;
|
|
pDlcObject->State = DLC_OBJECT_INVALID_TYPE;
|
|
#endif
|
|
|
|
//
|
|
// RLF 08/17/94
|
|
//
|
|
// grab the pointer to the closing info structure before deallocating
|
|
// the DLC object
|
|
//
|
|
|
|
pClosingInfo = pDlcObject->pClosingInfo;
|
|
DEALLOCATE_PACKET_DLC_OBJ(pFileContext->hLinkStationPool, pDlcObject);
|
|
|
|
//
|
|
// the close completion of the last link station closes
|
|
// also the sap object of that link station, if the
|
|
// sap closing was waiting to closing of the link station
|
|
//
|
|
|
|
if (pSapObject != NULL) {
|
|
CloseStation(pFileContext, pSapObject, TRUE);
|
|
|
|
//
|
|
// TRUE: must be at least
|
|
// DLC.RESET
|
|
//
|
|
|
|
}
|
|
|
|
//
|
|
// RLF 08/17/94
|
|
//
|
|
// Moved this call to DecrementCloseCounters from its previous
|
|
// place above. Once again, we find that things are happening out
|
|
// of sequence: this time, if we decrement the close counters,
|
|
// causing them to go to zero before we have freed the DLC object
|
|
// then the file context structure and its buffer pools are
|
|
// deallocated. But the DLC object was allocated from the now
|
|
// deleted pool, meaning sooner or later we corrupt non-paged pool
|
|
//
|
|
|
|
//
|
|
// The parallel close/reset commands have been queued in a
|
|
// link list, We must decrement and notify all dlc objects
|
|
//
|
|
|
|
DecrementCloseCounters(pFileContext, pClosingInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
CompleteCloseReset(
|
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|
IN PDLC_CLOSE_WAIT_INFO pClosingInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The primitive builds a completion event for the close/reset
|
|
of a dlc object. The close/reset may have been initiated by
|
|
DlcReset, DirCloseAdapter, DirInitialize or because the NDIS
|
|
driver is closing (eg. Unbind command).
|
|
In the last case pClosingInfo->pIrp is NULL, because there
|
|
is no command related to the event.
|
|
|
|
The only (major) difference from the IBM OS/2 DLC is, that
|
|
the first_buffer_addr parameter is not supported, because it
|
|
is meaningless with he NT buffer management.
|
|
The buffer pool is managed by dlc, not by the application.
|
|
|
|
Arguments:
|
|
|
|
FileContext - the process specific device context
|
|
pClosingInfo - all information needed for close or reset command completion
|
|
pDlcObject - the closed or deleted object
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN completeRead = FALSE;
|
|
BOOLEAN deallocatePacket = FALSE;
|
|
BOOLEAN derefFileContext = FALSE;
|
|
|
|
//
|
|
// Now we can cancel and chain all commands, that are still left,
|
|
// if we are really closing this adapter context
|
|
// (there should be no events any more, because the were deleted
|
|
// with their owner objects).
|
|
//
|
|
|
|
if (pClosingInfo->ClosingAdapter) {
|
|
for (;;) {
|
|
|
|
NTSTATUS Status;
|
|
|
|
Status = AbortCommand(
|
|
pFileContext,
|
|
DLC_IGNORE_STATION_ID, // station id may be anything
|
|
DLC_STATION_MASK_ALL, // mask for all station ids!
|
|
DLC_MATCH_ANY_COMMAND, // mask for all commands
|
|
&pClosingInfo->pCcbLink, // link them to here
|
|
pClosingInfo->CancelStatus, // cancel with this status
|
|
FALSE // Don't suppress completion
|
|
);
|
|
if (Status != STATUS_SUCCESS) {
|
|
break;
|
|
}
|
|
pClosingInfo->CcbCount++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The receive command must be linked to the first CCB immediately
|
|
// after the actual cancelling command.
|
|
//
|
|
|
|
if (pClosingInfo->pRcvCommand != NULL) {
|
|
CancelDlcCommand(pFileContext,
|
|
pClosingInfo->pRcvCommand,
|
|
&pClosingInfo->pCcbLink,
|
|
pClosingInfo->CancelStatus,
|
|
TRUE // disable command completion for RECEIVE
|
|
);
|
|
pClosingInfo->CcbCount++;
|
|
}
|
|
|
|
//
|
|
// Should the completed commands to be saved as a completion event.
|
|
//
|
|
|
|
if (pClosingInfo->pCompletionInfo != NULL) {
|
|
|
|
PDLC_COMPLETION_EVENT_INFO pCompletionInfo;
|
|
|
|
pCompletionInfo = pClosingInfo->pCompletionInfo;
|
|
|
|
//
|
|
// search all receive data events destinated to the closed or
|
|
// reset station or stations. We must chain all those
|
|
// buffer to the single NULL terminated list
|
|
//
|
|
|
|
pCompletionInfo->CcbCount = (USHORT)(pClosingInfo->CcbCount + 1);
|
|
|
|
//
|
|
// Save the received frames to the completion information!
|
|
// NOTE: The received frames returned by DIR.CLOSE.ADAPTER
|
|
// cannot be released using the same adapter handle.
|
|
// They are released and unlocked if the closed adapter
|
|
// was the only user of the pool. Otherwise those frames
|
|
// must be unlocked using another adapter handle, that
|
|
// shares the same buffer pool.
|
|
// !!! Actually we should automatically free all receive
|
|
// buffers when an adapter is closed and do not to return
|
|
// them to application !!!
|
|
//
|
|
|
|
pCompletionInfo->pReceiveBuffers = pClosingInfo->pRcvFrames;
|
|
|
|
//
|
|
// Execute the old READ command or queue the command completion
|
|
// request to the command queue.
|
|
//
|
|
|
|
if (pClosingInfo->pReadCommand != NULL) {
|
|
|
|
//
|
|
// RLF 03/25/94
|
|
//
|
|
// See below
|
|
//
|
|
|
|
completeRead = TRUE;
|
|
|
|
/*
|
|
|
|
pClosingInfo->pReadCommand->Overlay.pfCompletionHandler(
|
|
pFileContext,
|
|
NULL,
|
|
pClosingInfo->pReadCommand->pIrp,
|
|
(UINT)pClosingInfo->Event,
|
|
pCompletionInfo,
|
|
0
|
|
);
|
|
|
|
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pClosingInfo->pReadCommand);
|
|
|
|
*/
|
|
|
|
} else {
|
|
|
|
//
|
|
// Queue the completion event, Note: we will return all
|
|
// CCBs linked to the issued close CCB in any way.
|
|
// It does not matter for eg. DirCloseAdapter, if there
|
|
// is an extra event queued. It will be deleted when
|
|
// the command completes and all memory resources are
|
|
// released.
|
|
//
|
|
|
|
MakeDlcEvent(pFileContext,
|
|
pClosingInfo->Event,
|
|
pCompletionInfo->StationId,
|
|
NULL,
|
|
pCompletionInfo,
|
|
0,
|
|
pClosingInfo->FreeCompletionInfo
|
|
);
|
|
}
|
|
} else if (pFileContext->hBufferPool != NULL) {
|
|
|
|
//
|
|
// Free the received frames in the buffer pool, they are
|
|
// not saved to the command completion list.
|
|
//
|
|
|
|
BufferPoolDeallocateList(pFileContext->hBufferPool,
|
|
pClosingInfo->pRcvFrames
|
|
);
|
|
}
|
|
|
|
//
|
|
// DirCloseAdapter requires a special post routine, that will
|
|
// close the NDIS binding when all pending transmits have completed
|
|
// and the the requests have been cancelled.
|
|
// Note: the close adapter packet is not allocated from packet pool!
|
|
//
|
|
|
|
/*
|
|
|
|
//
|
|
// RLF 08/17/94
|
|
//
|
|
|
|
if (pClosingInfo->pfCloseComplete != NULL) {
|
|
pClosingInfo->pfCloseComplete(pFileContext,
|
|
pClosingInfo,
|
|
pClosingInfo->pCcbLink
|
|
);
|
|
|
|
} else {
|
|
|
|
*/
|
|
|
|
if (pClosingInfo->pfCloseComplete == NULL) {
|
|
if (pClosingInfo->pIrp != NULL) {
|
|
CompleteDirectOutIrp(pClosingInfo->pIrp,
|
|
STATUS_SUCCESS,
|
|
pClosingInfo->pCcbLink
|
|
);
|
|
CompleteAsyncCommand(pFileContext,
|
|
STATUS_SUCCESS,
|
|
pClosingInfo->pIrp,
|
|
pClosingInfo->pCcbLink,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
#if LLC_DBG
|
|
pClosingInfo->pNext = NULL;
|
|
#endif
|
|
|
|
//
|
|
// RLF 03/25/94
|
|
//
|
|
// More asynchronicity with READs causing fatal errors in an application.
|
|
// This actual case was in HPMON:
|
|
//
|
|
// 1. application submits DLC.CLOSE.STATION
|
|
// 2. this routine puts DLC.CLOSE.STATION command in command complete
|
|
// list of READ parameter table. READ IRP is completed
|
|
// 3. app gets READ completion, sees DLC.CLOSE.STATION is complete
|
|
// and frees DLC.CLOSE.STATION CCB to heap: heap manager writes
|
|
// signature data over freed CCB
|
|
// 4. this routine completes original DLC.CLOSE.STATION IRP, writing
|
|
// 8 bytes over original CCB, now just part of heap
|
|
// 5. some time later, heap allocation request made. Heap manager
|
|
// finds the heap has been trashed and goes on strike
|
|
//
|
|
|
|
if (completeRead) {
|
|
pClosingInfo->pReadCommand->Overlay.pfCompletionHandler(
|
|
pFileContext,
|
|
NULL,
|
|
pClosingInfo->pReadCommand->pIrp,
|
|
(UINT)pClosingInfo->Event,
|
|
pClosingInfo->pCompletionInfo,
|
|
0
|
|
);
|
|
|
|
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pClosingInfo->pReadCommand);
|
|
|
|
}
|
|
|
|
//
|
|
// RLF 08/17/94
|
|
//
|
|
// don't deallocate the packet now - we may need to use it if we call
|
|
// the close completion handler below
|
|
//
|
|
|
|
deallocatePacket = TRUE;
|
|
|
|
/*
|
|
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pClosingInfo);
|
|
*/
|
|
|
|
}
|
|
|
|
//
|
|
// RLF 08/17/94
|
|
//
|
|
// Moved the DirCloseAdapter processing here to give the client chance to
|
|
// receive the event before we close the adapter and maybe kill the file
|
|
// context
|
|
//
|
|
|
|
if (pClosingInfo->pfCloseComplete) {
|
|
|
|
//
|
|
// RLF 08/17/94
|
|
//
|
|
// This is bad: this close complete call may cause the file context to
|
|
// become completely dereferenced, and hence free it and its buffer
|
|
// pools. But we still have the closing info packet allocated, so
|
|
// increase the reference count, free up the packet below, then deref
|
|
// the file context again, and cause it to be deleted (if that would
|
|
// have happened anyway)
|
|
//
|
|
|
|
ReferenceFileContext(pFileContext);
|
|
derefFileContext = TRUE;
|
|
|
|
pClosingInfo->pfCloseComplete(pFileContext, pClosingInfo, pClosingInfo->pCcbLink);
|
|
}
|
|
|
|
if (deallocatePacket) {
|
|
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pClosingInfo);
|
|
}
|
|
|
|
if (derefFileContext) {
|
|
DereferenceFileContext(pFileContext);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
CleanUpEvents(
|
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|
IN PDLC_CLOSE_WAIT_INFO pClosingInfo,
|
|
IN PDLC_OBJECT pDlcObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The primitive cleans up or modifies all events having a pointer
|
|
to the closed station object. This also chains one and only one
|
|
transmit ccb chain to the end of CCB chain, because the completed
|
|
commands (as transmit and command completion events in the event queue)
|
|
cannot be linked any more to other commands. Thus there can be
|
|
only one single chain in the end of the actual chain of the pending
|
|
commands.
|
|
|
|
This routines cleans up the commands as well.
|
|
|
|
Arguments:
|
|
|
|
FileContext - the process specific device context
|
|
pClosingInfo - all information needed for close or reset command
|
|
completion
|
|
pDlcObject - the closed or deleted object
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDLC_EVENT pNextEvent;
|
|
PDLC_EVENT pEvent;
|
|
BOOLEAN RemoveNextPacket;
|
|
|
|
for (pEvent = (PDLC_EVENT)pFileContext->EventQueue.Flink;
|
|
pEvent != (PDLC_EVENT)&pFileContext->EventQueue;
|
|
pEvent = pNextEvent) {
|
|
|
|
pNextEvent = (PDLC_EVENT)pEvent->LlcPacket.pNext;
|
|
|
|
if (pEvent->pOwnerObject == pDlcObject) {
|
|
|
|
//
|
|
// By defult we free the next packet
|
|
//
|
|
|
|
RemoveNextPacket = TRUE;
|
|
|
|
switch (pEvent->Event) {
|
|
case LLC_RECEIVE_DATA:
|
|
|
|
//
|
|
// The received frames are saved to circular lists,
|
|
// where the head points to the newest frame and
|
|
// the next element from it is the oldest one.
|
|
// We simply the new frames to the old head of list.
|
|
//
|
|
|
|
if (pClosingInfo->pRcvFrames == NULL) {
|
|
pClosingInfo->pRcvFrames = pEvent->pEventInformation;
|
|
} else {
|
|
|
|
//
|
|
// Initial state:
|
|
// H1 = N1->O1...->N1 and H2 = N2->O2...->N2
|
|
//
|
|
// End state:
|
|
// H1 = N1->O2...->N2->O1...->N1
|
|
//
|
|
// => Operations must be:
|
|
// Temp = H2->Next;
|
|
// H2->Next = H1->Next;
|
|
// H1->Next = Temp;
|
|
// (where H = list head, N = newest element, O = oldest)
|
|
//
|
|
|
|
PDLC_BUFFER_HEADER pTemp;
|
|
|
|
pTemp = ((PDLC_BUFFER_HEADER)pEvent->pEventInformation)->FrameBuffer.pNextFrame;
|
|
((PDLC_BUFFER_HEADER)pEvent->pEventInformation)->FrameBuffer.pNextFrame =
|
|
((PDLC_BUFFER_HEADER)pClosingInfo->pRcvFrames)->FrameBuffer.pNextFrame;
|
|
((PDLC_BUFFER_HEADER)pClosingInfo->pRcvFrames)->FrameBuffer.pNextFrame = pTemp;
|
|
}
|
|
pDlcObject->pReceiveEvent = NULL;
|
|
break;
|
|
|
|
case LLC_TRANSMIT_COMPLETION:
|
|
|
|
//
|
|
// We cannot do nothing for single transmit commands, because
|
|
// they have already been completed, and the completed CCBs
|
|
// cannot any more be linked together. Thus we leave
|
|
// them to the event queue and will hope, that somebody
|
|
// will read them from the event queue. The memory is
|
|
// released when the file context is closed (eg. file close in
|
|
// the process exit). We just reset the dlc object pointer,
|
|
// that nobody would later use invalid pointer.
|
|
//
|
|
// The transmit commands chained of one closed station can
|
|
// be removed from the event list and chained to
|
|
// the end of the CCBs, BUT ONLY ONE! All other
|
|
// transmit completion events must be saved as a normal
|
|
// events with an invalid CCB count!!!!!
|
|
//
|
|
|
|
if (pClosingInfo->pCcbLink == NULL && pClosingInfo->ChainCommands) {
|
|
pClosingInfo->pCcbLink = pDlcObject->pPrevXmitCcbAddress;
|
|
pClosingInfo->CcbCount += pDlcObject->ChainedTransmitCount;
|
|
} else {
|
|
|
|
//
|
|
// We must change the format of this transmit completion
|
|
// into the similar to the command completion event
|
|
// packet of close command. Otherwise the application
|
|
// could lose the transmit CCBs, when it closes or
|
|
// resets a station.
|
|
//
|
|
|
|
PDLC_COMPLETION_EVENT_INFO pCompletionInfo;
|
|
|
|
pCompletionInfo = (PDLC_COMPLETION_EVENT_INFO)
|
|
ALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool);
|
|
|
|
if (pCompletionInfo != NULL) {
|
|
pCompletionInfo->CcbCount = pDlcObject->ChainedTransmitCount;
|
|
pCompletionInfo->pCcbAddress = pDlcObject->pPrevXmitCcbAddress;
|
|
pCompletionInfo->CommandCompletionFlag = pEvent->SecondaryInfo;
|
|
pEvent->SecondaryInfo = 0;
|
|
pEvent->pEventInformation = pCompletionInfo;
|
|
pEvent->bFreeEventInfo = TRUE;
|
|
RemoveNextPacket = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
//
|
|
// case DLC_COMMAND_COMPLETION ?
|
|
// The command completions have been saved without the
|
|
// the link to the Dlc structure -> they are automatically
|
|
// left into the completion queue.
|
|
//
|
|
|
|
case LLC_STATUS_CHANGE:
|
|
|
|
//
|
|
// Link station status changes cannot mean anytging after the
|
|
// link station has been deleted.
|
|
//
|
|
|
|
break;
|
|
|
|
#if LLC_DBG
|
|
default:
|
|
LlcInvalidObjectType();
|
|
break;
|
|
#endif
|
|
};
|
|
if (RemoveNextPacket) {
|
|
LlcRemoveEntryList(pEvent);
|
|
|
|
DEALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool, pEvent);
|
|
|
|
} else {
|
|
|
|
//
|
|
// We must remove the reference to the deleted object
|
|
//
|
|
|
|
pEvent->pOwnerObject = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// The optional receive command must be removed first, otherwise
|
|
// it is cancelled with the other commands given to the deleted
|
|
// object.
|
|
//
|
|
|
|
if (pClosingInfo->CancelReceive && pDlcObject->pRcvParms != NULL) {
|
|
|
|
//
|
|
// The receive command linked to the DLC object is a special case:
|
|
// we must link it immediately after the close CCB
|
|
//
|
|
|
|
pClosingInfo->pRcvCommand = SearchAndRemoveAnyCommand(
|
|
pFileContext,
|
|
(ULONG)(-1),
|
|
(USHORT)DLC_IGNORE_STATION_ID,
|
|
(USHORT)DLC_STATION_MASK_SPECIFIC,
|
|
pDlcObject->pRcvParms->Async.Ccb.pCcbAddress
|
|
);
|
|
pDlcObject->pRcvParms = NULL;
|
|
}
|
|
|
|
//
|
|
// Cleanup the commands given to the dleted object
|
|
//
|
|
|
|
for (;;) {
|
|
|
|
NTSTATUS Status;
|
|
|
|
Status = AbortCommand(pFileContext,
|
|
pDlcObject->StationId,
|
|
(USHORT)(pDlcObject->Type == (UCHAR)DLC_SAP_OBJECT
|
|
? DLC_STATION_MASK_SAP
|
|
: DLC_STATION_MASK_SPECIFIC),
|
|
DLC_IGNORE_SEARCH_HANDLE,
|
|
&pClosingInfo->pCcbLink,
|
|
pClosingInfo->CancelStatus,
|
|
FALSE // Don't suppress completion
|
|
);
|
|
if (Status != STATUS_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Now we can cancel and chain all commands destinated to the
|
|
// closed/reset station id.
|
|
// We always complete the commands given to the deletcd object,
|
|
// but we chain them together only if this flag has been set.
|
|
//
|
|
|
|
if (pClosingInfo->ChainCommands == FALSE) {
|
|
|
|
//
|
|
// Don't link the cancelled CCBs together
|
|
//
|
|
|
|
pClosingInfo->pCcbLink = NULL;
|
|
} else {
|
|
pClosingInfo->CcbCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SearchReadCommandForClose(
|
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|
IN PDLC_CLOSE_WAIT_INFO pClosingInfo,
|
|
IN PVOID pCcbAddress,
|
|
IN ULONG CommandCompletionFlag,
|
|
IN USHORT StationId,
|
|
IN USHORT StationIdMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The primitive searches a read command for a close command
|
|
and saves it into the closing info structure.
|
|
|
|
Arguments:
|
|
|
|
FileContext - the process specific device context
|
|
pClosingInfo - all information needed for close or reset command
|
|
completion
|
|
pCcbAddress - ccb address of the searched read command
|
|
StationId -
|
|
StationIdMask -
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Allocate the parameter buffer for command completion with
|
|
// read if it is needed OR if we are closing everything because
|
|
// of a critical exception (ie. pIrp == NULL)
|
|
//
|
|
|
|
pClosingInfo->pCompletionInfo = (PDLC_COMPLETION_EVENT_INFO)
|
|
ALLOCATE_PACKET_DLC_PKT(pFileContext->hPacketPool);
|
|
|
|
if (pClosingInfo->pCompletionInfo != NULL) {
|
|
|
|
pClosingInfo->FreeCompletionInfo = TRUE;
|
|
|
|
//
|
|
// We must link all commands given to the deleted objects
|
|
//
|
|
|
|
pClosingInfo->pCompletionInfo->pCcbAddress = pCcbAddress;
|
|
pClosingInfo->pCompletionInfo->CommandCompletionFlag = CommandCompletionFlag;
|
|
pClosingInfo->ChainCommands = TRUE;
|
|
|
|
//
|
|
// A link station close command we be read as a command completion
|
|
// on its sap, but the other close commands cannot use any station id
|
|
//
|
|
|
|
if (StationIdMask == DLC_STATION_MASK_SPECIFIC) {
|
|
pClosingInfo->pCompletionInfo->StationId = (USHORT)(StationId & DLC_STATION_MASK_SAP);
|
|
} else {
|
|
pClosingInfo->pCompletionInfo->StationId = 0;
|
|
}
|
|
|
|
//
|
|
// Search first a special READ command dedicated only for
|
|
// this command completion. We must read the optional
|
|
// read command NOW. Otherwise it will be canceled
|
|
// with the other commands, that have been given for
|
|
// the deleted station(s).
|
|
//
|
|
|
|
pClosingInfo->pReadCommand = SearchAndRemoveCommandByHandle(
|
|
&pFileContext->CommandQueue,
|
|
pClosingInfo->Event,
|
|
(USHORT)DLC_IGNORE_STATION_ID,
|
|
(USHORT)DLC_STATION_MASK_SPECIFIC,
|
|
pCcbAddress
|
|
);
|
|
if (pClosingInfo->pReadCommand == NULL) {
|
|
|
|
//
|
|
// We do not really care about the result, because
|
|
// it is OK to return NULL. This completion event
|
|
// may be read sometime later.
|
|
//
|
|
|
|
pClosingInfo->pReadCommand = SearchAndRemoveCommand(
|
|
&pFileContext->CommandQueue,
|
|
pClosingInfo->Event,
|
|
StationId,
|
|
StationIdMask
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
CompleteLlcObjectClose(
|
|
IN PDLC_OBJECT pDlcObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine just derefernces a llc object, when its reference count
|
|
in DLC driver has been decremented to zero.
|
|
The reference count is used to prevent the closing of llc object
|
|
when it is simultaneously called elsewhere (that would invalidate
|
|
the llc object pointer)
|
|
|
|
Arguments:
|
|
|
|
pDlcObject - any DLC object.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID hLlcObject = pDlcObject->hLlcObject;
|
|
|
|
if (hLlcObject != NULL) {
|
|
|
|
DLC_TRACE('P');
|
|
|
|
pDlcObject->hLlcObject = NULL;
|
|
LEAVE_DLC(pDlcObject->pFileContext);
|
|
|
|
LlcDereferenceObject(hLlcObject);
|
|
|
|
ENTER_DLC(pDlcObject->pFileContext);
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
DecrementCloseCounters(
|
|
PDLC_FILE_CONTEXT pFileContext,
|
|
PDLC_CLOSE_WAIT_INFO pClosingInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine decrements the count of existing objects in the
|
|
chianed close command packets and completes the close commands,
|
|
if the count of the existing objects hits zero.
|
|
|
|
Arguments:
|
|
|
|
pFileContext - file handle context
|
|
pClosingInfo - close command packet
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN
|
|
TRUE - all pending close/resets have been completed
|
|
FALSE - close/resets still pending
|
|
|
|
--*/
|
|
|
|
{
|
|
PDLC_CLOSE_WAIT_INFO pNextClosingInfo;
|
|
UINT loopCounter, closeCounter;
|
|
|
|
//
|
|
// Complete the reset command if all objects have been deleted,
|
|
// There may be another DirCloseAdapter chained its next pointer
|
|
//
|
|
|
|
for (loopCounter = 0, closeCounter = 0;
|
|
pClosingInfo != NULL;
|
|
pClosingInfo = pNextClosingInfo, ++loopCounter) {
|
|
|
|
pNextClosingInfo = pClosingInfo->pNext;
|
|
pClosingInfo->CloseCounter--;
|
|
if (pClosingInfo->CloseCounter == 0) {
|
|
|
|
//
|
|
// Call the completion routine of the close command.
|
|
// We don't need to check the status code.
|
|
//
|
|
|
|
CompleteCloseReset(pFileContext, pClosingInfo);
|
|
++closeCounter;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if we completed every close/reset we found then return TRUE
|
|
//
|
|
|
|
return loopCounter == closeCounter;
|
|
}
|
|
|
|
|
|
VOID
|
|
CompleteDirectOutIrp(
|
|
IN PIRP Irp,
|
|
IN UCHAR Status,
|
|
IN PLLC_CCB NextCcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For an IRP submitted as method DIRECT_OUT (DLC.CLOSE.STATION) complete the
|
|
CCB in user space by getting the mapped system address of the CCB and update
|
|
it with the completion code and next CCB pointer
|
|
|
|
Arguments:
|
|
|
|
Irp - pointer to DIRECT_OUT IRP to complete
|
|
Status - DLC status code
|
|
NextCcb - pointer to next CCB to chain
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLLC_CCB ccb;
|
|
|
|
ccb = (PLLC_CCB)MmGetSystemAddressForMdl(Irp->MdlAddress);
|
|
RtlStoreUlongPtr((PULONG_PTR)&ccb->pNext, (ULONG_PTR)NextCcb);
|
|
ccb->uchDlcStatus = Status;
|
|
}
|