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

2384 lines
74 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:
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;
}