603 lines
16 KiB
C
603 lines
16 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
dlcinfo.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
The module implements the Query/Set information commands.
|
|||
|
It also provides the statistics services for DLC api.
|
|||
|
|
|||
|
Contents:
|
|||
|
GetDlcErrorCounters
|
|||
|
DlcQueryInformation
|
|||
|
DlcSetInformation
|
|||
|
GetOpenSapAndStationCount
|
|||
|
SetupGroupSaps
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Antti Saarenheimo (o-anttis) 29-AUG-1991
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include <dlc.h>
|
|||
|
|
|||
|
static ULONG aTokenringLogOid[ADAPTER_ERROR_COUNTERS] = {
|
|||
|
OID_802_5_LINE_ERRORS,
|
|||
|
0,
|
|||
|
OID_802_5_BURST_ERRORS,
|
|||
|
OID_802_5_AC_ERRORS,
|
|||
|
OID_802_5_ABORT_DELIMETERS,
|
|||
|
0,
|
|||
|
OID_802_5_LOST_FRAMES,
|
|||
|
OID_GEN_RCV_NO_BUFFER,
|
|||
|
OID_802_5_FRAME_COPIED_ERRORS,
|
|||
|
OID_802_5_FREQUENCY_ERRORS,
|
|||
|
OID_802_5_TOKEN_ERRORS
|
|||
|
};
|
|||
|
|
|||
|
static ULONG aEthernetLogOid[ADAPTER_ERROR_COUNTERS] = {
|
|||
|
OID_802_3_XMIT_TIMES_CRS_LOST,
|
|||
|
0,
|
|||
|
OID_802_3_RCV_ERROR_ALIGNMENT,
|
|||
|
0,
|
|||
|
OID_GEN_XMIT_ERROR,
|
|||
|
0,
|
|||
|
OID_802_3_XMIT_MAX_COLLISIONS,
|
|||
|
OID_GEN_RCV_NO_BUFFER,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0
|
|||
|
};
|
|||
|
|
|||
|
static ULONG aFddiLogOid[ADAPTER_ERROR_COUNTERS] = {
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
GetDlcErrorCounters(
|
|||
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|||
|
IN PUCHAR pAdapterErrors
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Procedure reads the cumulative 32-bit adapter error counters from
|
|||
|
ethernet or token-ring adapter and returns 8-bit DLC error counters,
|
|||
|
that supports read and read & reset commands. It also maintains
|
|||
|
local copies of the NDIS error counters in the process specific
|
|||
|
adapter context, because NDIS counters cannot be reset.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pFileContext - DLC address object
|
|||
|
pAdapterErrors - DLC errors counters, if NULL => NDIS values are
|
|||
|
copied to file context.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
LLC_NDIS_REQUEST Request;
|
|||
|
PULONG pOidBuffer;
|
|||
|
ULONG counter;
|
|||
|
UINT i;
|
|||
|
UINT Status;
|
|||
|
|
|||
|
ASSUME_IRQL(DISPATCH_LEVEL);
|
|||
|
|
|||
|
//
|
|||
|
// Token-ring and ethernet uses different error counters
|
|||
|
//
|
|||
|
|
|||
|
switch (pFileContext->ActualNdisMedium) {
|
|||
|
case NdisMedium802_3:
|
|||
|
pOidBuffer = aEthernetLogOid;
|
|||
|
break;
|
|||
|
|
|||
|
case NdisMedium802_5:
|
|||
|
pOidBuffer = aTokenringLogOid;
|
|||
|
break;
|
|||
|
|
|||
|
case NdisMediumFddi:
|
|||
|
pOidBuffer = aFddiLogOid;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// read all error counters having non null NDIS OID and
|
|||
|
// decrement the previous error count value from it.
|
|||
|
// Overflowed DLC error counter is set 255 (the maximum).
|
|||
|
//
|
|||
|
|
|||
|
Request.Ndis.RequestType = NdisRequestQueryInformation;
|
|||
|
Request.Ndis.DATA.QUERY_INFORMATION.InformationBuffer = &counter;
|
|||
|
Request.Ndis.DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(counter);
|
|||
|
|
|||
|
for (i = 0; i < ADAPTER_ERROR_COUNTERS; i++) {
|
|||
|
if (pOidBuffer[i] != 0) {
|
|||
|
|
|||
|
Request.Ndis.DATA.QUERY_INFORMATION.Oid = pOidBuffer[i];
|
|||
|
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
RELEASE_DRIVER_LOCK();
|
|||
|
|
|||
|
Status = LlcNdisRequest(pFileContext->pBindingContext, &Request);
|
|||
|
|
|||
|
ACQUIRE_DRIVER_LOCK();
|
|||
|
|
|||
|
ENTER_DLC(pFileContext);
|
|||
|
|
|||
|
if (Status != STATUS_SUCCESS) {
|
|||
|
|
|||
|
#if DBG
|
|||
|
if ( Status != STATUS_NOT_SUPPORTED ){
|
|||
|
// Only print real errors.
|
|||
|
DbgPrint("DLC.GetDlcErrorCounters: LlcNdisRequest returns %x\n", Status);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
counter = 0;
|
|||
|
} else if ((counter - pFileContext->NdisErrorCounters[i] > 255)
|
|||
|
&& (pAdapterErrors != NULL)) {
|
|||
|
counter = 255;
|
|||
|
} else {
|
|||
|
counter -= pFileContext->NdisErrorCounters[i];
|
|||
|
}
|
|||
|
if (pAdapterErrors != NULL) {
|
|||
|
pAdapterErrors[i] = (UCHAR)counter;
|
|||
|
}
|
|||
|
pFileContext->NdisErrorCounters[i] += counter;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DlcQueryInformation(
|
|||
|
IN PIRP pIrp,
|
|||
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|||
|
IN PNT_DLC_PARMS pDlcParms,
|
|||
|
IN ULONG InputBufferLength,
|
|||
|
IN ULONG OutputBufferLength
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
The routine returns the DLC specific information of any DLC object.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pIrp - current io request packet
|
|||
|
pFileContext - DLC address object
|
|||
|
pDlcParms - the current parameter block
|
|||
|
InputBufferLength - the length of input parameters
|
|||
|
OutputBufferLength - the length of output parameters
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS:
|
|||
|
STATUS_SUCCESS
|
|||
|
STATUS_INVALID_COMMAND
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
PVOID hClientHandle = pFileContext->pBindingContext;
|
|||
|
PDLC_OBJECT pDlcObject;
|
|||
|
PLLC_ADAPTER_DLC_INFO pDlcAdapter;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(pIrp);
|
|||
|
UNREFERENCED_PARAMETER(InputBufferLength);
|
|||
|
|
|||
|
ASSUME_IRQL(DISPATCH_LEVEL);
|
|||
|
|
|||
|
//
|
|||
|
// NOTE: DlcQueryBuffer output buffer size was not checked by
|
|||
|
// DlcDeviceIoControl. For each class we check the size
|
|||
|
// based on what it will copy. Although it did check that
|
|||
|
// the input buffer size was at least NT_DLC_QUERY_INFORMATION_INPUT
|
|||
|
// size.
|
|||
|
//
|
|||
|
|
|||
|
switch (pDlcParms->DlcGetInformation.Header.InfoClass) {
|
|||
|
case DLC_INFO_CLASS_DLC_ADAPTER:
|
|||
|
|
|||
|
// union NT_DLC_PARMS
|
|||
|
// LLC_ADAPTER_DLC_INFO
|
|||
|
if (OutputBufferLength < sizeof(LLC_ADAPTER_DLC_INFO))
|
|||
|
{
|
|||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The output data is just written to the
|
|||
|
// beginning of the current system buffer.
|
|||
|
//
|
|||
|
|
|||
|
pDlcAdapter = (PLLC_ADAPTER_DLC_INFO)pDlcParms;
|
|||
|
GetOpenSapAndStationCount(pFileContext,
|
|||
|
&pDlcAdapter->OpenSaps,
|
|||
|
(PUCHAR)&pDlcAdapter->OpenStations
|
|||
|
);
|
|||
|
pDlcAdapter->MaxSap = 127;
|
|||
|
pDlcAdapter->MaxStations = 255;
|
|||
|
pDlcAdapter->AvailStations = (UCHAR)255 - pDlcAdapter->OpenStations;
|
|||
|
break;
|
|||
|
|
|||
|
case DLC_INFO_CLASS_ADAPTER_LOG:
|
|||
|
|
|||
|
// union NT_DLC_PARMS (pDlcParms)
|
|||
|
// union NT_DLC_QUERY_INFORMATION_PARMS DlcGetInformation
|
|||
|
// union NT_DLC_QUERY_INFORMATION_OUTPUT Info
|
|||
|
// union LLC_ADAPTER_LOG AdapterLog
|
|||
|
if (OutputBufferLength < sizeof(LLC_ADAPTER_LOG))
|
|||
|
{
|
|||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
GetDlcErrorCounters(pFileContext, (PUCHAR)&pDlcParms->DlcGetInformation);
|
|||
|
break;
|
|||
|
|
|||
|
case DLC_INFO_CLASS_LINK_STATION:
|
|||
|
|
|||
|
// union NT_DLC_PARMS (pDlcParms)
|
|||
|
// union NT_DLC_QUERY_INFORMATION_PARMS DlcGetInformation
|
|||
|
// union NT_DLC_QUERY_INFORMATION_OUTPUT Info
|
|||
|
// struct _DlcLinkInfoGet
|
|||
|
// USHORT MaxInformationField
|
|||
|
if (OutputBufferLength < sizeof(USHORT))
|
|||
|
{
|
|||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
Status = GetLinkStation(pFileContext,
|
|||
|
pDlcParms->DlcGetInformation.Header.StationId,
|
|||
|
&pDlcObject
|
|||
|
);
|
|||
|
if (Status == STATUS_SUCCESS) {
|
|||
|
|
|||
|
//
|
|||
|
// Round always the information field length to the full
|
|||
|
// dword even number => some copy operations may be much
|
|||
|
// faster (usually not, but worth of effor in any way)
|
|||
|
//
|
|||
|
|
|||
|
pDlcParms->DlcGetInformation.Info.Link.MaxInformationField = (USHORT)(pDlcObject->u.Link.MaxInfoFieldLength & -3);
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case DLC_INFO_CLASS_STATISTICS:
|
|||
|
case DLC_INFO_CLASS_STATISTICS_RESET:
|
|||
|
Status = GetStation(pFileContext,
|
|||
|
pDlcParms->DlcGetInformation.Header.StationId,
|
|||
|
&pDlcObject
|
|||
|
);
|
|||
|
if (Status != STATUS_SUCCESS) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
hClientHandle = pDlcObject->hLlcObject;
|
|||
|
|
|||
|
//
|
|||
|
// **** FALL THROUGH ****
|
|||
|
//
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
//
|
|||
|
// LLC will return invalid command, if it is not supported
|
|||
|
//
|
|||
|
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
RELEASE_DRIVER_LOCK();
|
|||
|
|
|||
|
//
|
|||
|
// LlcQueryInformation validates buffer size before copying.
|
|||
|
//
|
|||
|
|
|||
|
Status = LlcQueryInformation(hClientHandle,
|
|||
|
pDlcParms->DlcGetInformation.Header.InfoClass,
|
|||
|
(PLLC_QUERY_INFO_BUFFER)&(pDlcParms->DlcGetInformation),
|
|||
|
(UINT)OutputBufferLength
|
|||
|
);
|
|||
|
|
|||
|
ACQUIRE_DRIVER_LOCK();
|
|||
|
|
|||
|
ENTER_DLC(pFileContext);
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
return (NTSTATUS)Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DlcSetInformation(
|
|||
|
IN PIRP pIrp,
|
|||
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|||
|
IN PNT_DLC_PARMS pDlcParms,
|
|||
|
IN ULONG InputBufferLength,
|
|||
|
IN ULONG OutputBufferLength
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
|
|||
|
The routine sets new parameter values for the DLC objects.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pIrp - current io request packet
|
|||
|
pFileContext - DLC address object
|
|||
|
pDlcParms - the current parameter block
|
|||
|
InputBufferLength - the length of input parameters
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
STATUS_SUCCESS
|
|||
|
STATUS_INVALID_COMMAND
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
PDLC_OBJECT pDlcObject;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(pIrp);
|
|||
|
UNREFERENCED_PARAMETER(InputBufferLength);
|
|||
|
UNREFERENCED_PARAMETER(OutputBufferLength);
|
|||
|
|
|||
|
ASSUME_IRQL(DISPATCH_LEVEL);
|
|||
|
|
|||
|
switch (pDlcParms->DlcSetInformation.Header.InfoClass) {
|
|||
|
case DLC_INFO_CLASS_LINK_STATION:
|
|||
|
case DLC_INFO_CLASS_DIRECT_INFO:
|
|||
|
Status = GetStation(pFileContext,
|
|||
|
pDlcParms->DlcSetInformation.Header.StationId,
|
|||
|
&pDlcObject
|
|||
|
);
|
|||
|
if (Status != STATUS_SUCCESS) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
Status = LlcSetInformation(pDlcObject->hLlcObject,
|
|||
|
pDlcParms->DlcSetInformation.Header.InfoClass,
|
|||
|
(PLLC_SET_INFO_BUFFER)&(
|
|||
|
pDlcParms->DlcSetInformation.Info.LinkStation),
|
|||
|
sizeof(DLC_LINK_PARAMETERS)
|
|||
|
);
|
|||
|
break;
|
|||
|
|
|||
|
case DLC_INFO_CLASS_DLC_TIMERS:
|
|||
|
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
Status = LlcSetInformation(pFileContext->pBindingContext,
|
|||
|
pDlcParms->DlcSetInformation.Header.InfoClass,
|
|||
|
(PLLC_SET_INFO_BUFFER)&(pDlcParms->DlcSetInformation.Info.TimerParameters),
|
|||
|
sizeof(LLC_TICKS)
|
|||
|
);
|
|||
|
break;
|
|||
|
|
|||
|
case DLC_INFO_CLASS_SET_FUNCTIONAL:
|
|||
|
case DLC_INFO_CLASS_RESET_FUNCTIONAL:
|
|||
|
case DLC_INFO_CLASS_SET_GROUP:
|
|||
|
case DLC_INFO_CLASS_SET_MULTICAST:
|
|||
|
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
Status = LlcSetInformation(pFileContext->pBindingContext,
|
|||
|
pDlcParms->DlcSetInformation.Header.InfoClass,
|
|||
|
(PLLC_SET_INFO_BUFFER)&(pDlcParms->DlcSetInformation.Info.Broadcast),
|
|||
|
sizeof(TR_BROADCAST_ADDRESS)
|
|||
|
);
|
|||
|
break;
|
|||
|
|
|||
|
case DLC_INFO_CLASS_GROUP:
|
|||
|
|
|||
|
//
|
|||
|
// Setup DLC group SAPs. Group saps are used as a common address
|
|||
|
// of a SAP group. They can only receive frames.
|
|||
|
//
|
|||
|
|
|||
|
Status = GetStation(pFileContext,
|
|||
|
pDlcParms->DlcSetInformation.Header.StationId,
|
|||
|
&pDlcObject
|
|||
|
);
|
|||
|
if (Status != STATUS_SUCCESS) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
Status = SetupGroupSaps(pFileContext,
|
|||
|
pDlcObject,
|
|||
|
(UINT)pDlcParms->DlcSetInformation.Info.Sap.GroupCount,
|
|||
|
(PUCHAR)pDlcParms->DlcSetInformation.Info.Sap.GroupList
|
|||
|
);
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
Status = DLC_STATUS_INVALID_COMMAND;
|
|||
|
break;
|
|||
|
};
|
|||
|
|
|||
|
ENTER_DLC(pFileContext);
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Function returns the number of open sap and link
|
|||
|
// stations for a dlc application.
|
|||
|
//
|
|||
|
VOID
|
|||
|
GetOpenSapAndStationCount(
|
|||
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|||
|
OUT PUCHAR pOpenSaps,
|
|||
|
OUT PUCHAR pOpenStations
|
|||
|
)
|
|||
|
{
|
|||
|
UINT i, SapCount = 0;
|
|||
|
|
|||
|
for (i = 1; i < MAX_SAP_STATIONS; i++) {
|
|||
|
if (pFileContext->SapStationTable[i] != NULL) {
|
|||
|
SapCount++;
|
|||
|
}
|
|||
|
}
|
|||
|
*pOpenSaps = (UCHAR)SapCount;
|
|||
|
if (pFileContext->SapStationTable[0] != NULL) {
|
|||
|
SapCount++;
|
|||
|
}
|
|||
|
*pOpenStations = (UCHAR)(pFileContext->DlcObjectCount - SapCount);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SetupGroupSaps(
|
|||
|
IN PDLC_FILE_CONTEXT pFileContext,
|
|||
|
IN PDLC_OBJECT pDlcObject,
|
|||
|
IN UINT GroupSapCount,
|
|||
|
IN PUCHAR pGroupSapList
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
The routine deletes the current group saps list and
|
|||
|
and new group saps. Fi the new group sap list is empty, then
|
|||
|
we just delete all current group saps.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pFileContext - DLC context
|
|||
|
pDlcObject - SAP object
|
|||
|
GroupSapCount - number of new group saps
|
|||
|
pGroupSapList - list of new group saps
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS:
|
|||
|
STATUS_SUCCESS
|
|||
|
STATUS_INVALID_COMMAND
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
UINT i;
|
|||
|
UINT OpenOptions;
|
|||
|
|
|||
|
//
|
|||
|
// We must first remove all old groups sap defined for the
|
|||
|
// sap station (if any)
|
|||
|
//
|
|||
|
|
|||
|
if (pDlcObject->u.Sap.GroupSapHandleList != NULL) {
|
|||
|
for (i = 0; i < pDlcObject->u.Sap.GroupSapCount; i++) {
|
|||
|
if (pDlcObject->u.Sap.GroupSapHandleList[i] != NULL) {
|
|||
|
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
LlcCloseStation(pDlcObject->u.Sap.GroupSapHandleList[i], NULL);
|
|||
|
|
|||
|
ENTER_DLC(pFileContext);
|
|||
|
}
|
|||
|
}
|
|||
|
FREE_MEMORY_FILE(pDlcObject->u.Sap.GroupSapHandleList);
|
|||
|
pDlcObject->u.Sap.GroupSapHandleList = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Note: the old group saps can be deleted with a null list!
|
|||
|
//
|
|||
|
|
|||
|
pDlcObject->u.Sap.GroupSapCount = (UCHAR)GroupSapCount;
|
|||
|
if (GroupSapCount != 0) {
|
|||
|
pDlcObject->u.Sap.GroupSapHandleList = (PVOID*)ALLOCATE_ZEROMEMORY_FILE(
|
|||
|
GroupSapCount
|
|||
|
* sizeof(PVOID)
|
|||
|
);
|
|||
|
if (pDlcObject->u.Sap.GroupSapHandleList == NULL) {
|
|||
|
return DLC_STATUS_NO_MEMORY;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The groups sap implementation is based on the fact,
|
|||
|
// that the lower module things to run a normal sap.
|
|||
|
// The routing of UI, TEST and XID frames for all
|
|||
|
// saps sends the incoming U-frames automatically
|
|||
|
// all real saps registered to a sap. This method
|
|||
|
// could theoretically use very much memory if there were
|
|||
|
// very many saps and group saps (eg: 50 * 50 = 2500 * 100),
|
|||
|
// but that situation is actually impossible.
|
|||
|
// SNA saps (3) have one command group sap and even SNA
|
|||
|
// sap is very rarely used (not used by CommServer)
|
|||
|
//
|
|||
|
|
|||
|
for (i = 0; i < pDlcObject->u.Sap.GroupSapCount; i++) {
|
|||
|
|
|||
|
UINT Status;
|
|||
|
|
|||
|
OpenOptions = 0;
|
|||
|
if (~(pDlcObject->u.Sap.OptionsPriority & XID_HANDLING_BIT)) {
|
|||
|
OpenOptions = LLC_HANDLE_XID_COMMANDS;
|
|||
|
}
|
|||
|
|
|||
|
LEAVE_DLC(pFileContext);
|
|||
|
|
|||
|
Status = LlcOpenSap(pFileContext->pBindingContext,
|
|||
|
(PVOID)pDlcObject,
|
|||
|
(UINT)pGroupSapList[i] | 1,
|
|||
|
OpenOptions,
|
|||
|
&pDlcObject->u.Sap.GroupSapHandleList[i]
|
|||
|
);
|
|||
|
|
|||
|
ENTER_DLC(pFileContext);
|
|||
|
|
|||
|
if (Status != STATUS_SUCCESS) {
|
|||
|
return Status;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|