2020-09-30 17:12:29 +02:00

2061 lines
46 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
init.c
Abstract:
This file controls all the loading and unloading of the driver. It controls
the initial bindings and the teardown in the event it is told to unload.
Author:
Sean Selitrennikoff (seanse) 3-93
Environment:
Kernel Mode.
Revision History:
--*/
#include <ntddk.h>
#include <ndis.h>
#include "lsl.h"
#include "lslmlid.h"
#include "frames.h"
#include "mlid.h"
#include "ndismlid.h"
#define UNICODE_STRING_CONST(x) {sizeof(L##x)-2, sizeof(L##x), L##x}
//
// PLEASE NOTE: The ordering of the strings in the arrays is important.
// Code depends on the indicies of the strings. Each dependency is
// listed with the array that it depends on.
//
//
// All the string names of the counters that must be kept by a MLID.
// - ndismlid.c
// - lslmlid.c
//
static
MEON_STRING
NdisMlidGenericStatStrings[] = {
"MTotalTxPacketCount",
"MTotalRxPacketCount",
"MNoECBAvailableCount",
"MPacketTxTooBigCount",
"MPacketTxTooSmallCount",
"MPacketRxTooBigCount",
"MTotalTxMiscCount",
"MTotalRxMiscCount",
"MTotalTxOKByteCount",
"MTotalRxOKByteCount",
"MTotalGroupAddrTxCount",
"MTotalGroupAddrRxCount",
"MAdapterResetCount",
"MAdapterOprTimeStamp",
"MQDepth"
};
//
// The type of each of the above stat, where 0 == UINT32 and 1 == UINT64
// - No Dependencies
//
static
UINT32
NdisMlidGenericStatTypes[] = {
0, // TotalTxPacketCount
0, // TotalRxPacketCount
0, // NoECBAvailableCount
0, // PacketTxTooBigCount
0, // PacketRxTooSmallCount
0, // PacketRxTooBigCount
0, // TotalTxMiscCount
0, // TotalRxMiscCount
1, // TotalTxOKByteCount
1, // TotalRxOKByteCount
0, // TotalGroupAddrTxCount
0, // TotalGroupAddrRxCount
0, // AdapterResetCount
0, // AdapterOprTimeStamp
0 // QDepth
};
//
// String names for counters kept by Token Ring MLIDs
// - ndismlid.c: TRN_LastRingID
//
static
MEON_STRING
NdisMlidTokenRingStatStrings[] = {
"TRN_ACErrorCounter",
"TRN_AbortDelimiterCounter",
"TRN_BurstErrorCounter",
"TRN_FrameCopiedErrorCounter",
"TRN_FrequencyErrorCounter",
"TRN_InternalErrorCounter",
"TRN_LastRingStatus",
"TRN_LineErrorCounter",
"TRN_LostFrameCounter",
"TRN_TokenErrorCounter",
"TRN_UpstreamNodeAddress",
"TRN_LastRingID",
"TRN_LastBeaconType"
};
//
// The type of each of the above stat, where 0 == UINT32 and 1 == UINT64
// - ndismlid.c: TRN_LastRingID
//
static
UINT32
NdisMlidTokenRingStatTypes[] = {
0, // TRN_ACErrorCounter
0, // TRN_AbortDelimiterCounter
0, // TRN_BurstErrorCounter
0, // TRN_FrameCopiedErrorCounter
0, // TRN_FrequencyErrorCounter
0, // TRN_InternalErrorCounter
0, // TRN_LastRingStatus
0, // TRN_LineErrorCounter
0, // TRN_LostFrameCounter
0, // TRN_TokenErrorCounter
1, // TRN_UpstreamNodeAddress
0, // TRN_LastRingID
0 // TRN_LastBeaconType
};
//
// String names for counters kept by Ethernet MLIDs
// - No Dependencies
//
static
MEON_STRING
NdisMlidEthernetStatStrings[] = {
"ETH_TxOKSingleCollisionsCount",
"ETH_TxOKMultipleCollisionsCount",
"ETH_TxOKButDeferred",
"ETH_TxAbortLateCollision",
"ETH_TxAbortExcessCollision",
"ETH_TxAbortCarrierSense",
"ETH_TxAbortExcessiveDeferral",
"ETH_RxAbortFrameAlignment"
};
//
// The type of each of the above stats, where 0 == UINT32 and 1 == UINT64
// - No Dependencies
//
static
UINT32
NdisMlidEthernetStatTypes[] = {
0, // ETH_TxOKSingleCollisionsCount
0, // ETH_TxOKMultipleCollisionsCount
0, // ETH_TxOKButDeferred
0, // ETH_TxAbortLateCollision
0, // ETH_TxAbortExcessCollision
0, // ETH_TxAbortCarrierSense
0, // ETH_TxAbortExcessiveDeferral
0 // ETH_RxAbortFrameAlignment
};
//
// String names for counter kept by FDDI MLIDs
// - No Dependencies
//
static
MEON_STRING
NdisMlidFddiStatStrings[] = {
"FDI_ConfigurationState",
"FDI_UpstreamNode",
"FDI_DownstreamNode",
"FDI_FrameErrorCount",
"FDI_FrameLostCount",
"FDI_RingManagementCount",
"FDI_LCTFailureCount",
"FDI_LemRejectCount",
"FDI_LemCount",
"FDI_LConnectionState"
};
//
// The type of each of the above stats, where 0 == UINT32 and 1 == UINT64
// - No Dependencies
//
static
UINT32
NdisMlidFddiStatTypes[] = {
0, // FDI_ConfigurationState
1, // FDI_UpstreamNode
1, // FDI_DownstreamNode
0, // FDI_FrameErrorCount
0, // FDI_FrameLostCount
0, // FDI_RingManagementCount
0, // FDI_LCTFailureCount
0, // FDI_LemRejectCount
0, // FDI_LemCount
0 // FDI_LConnectionState
};
//
// Holds the string names of all frame types
// - No Dependencies
//
static
MEON_STRING
NdisMlidFrameTypeStrings[] = {
"VIRTUAL_LAN",
"LOCALTALK",
"ETHERNET_II",
"ETHERNET_802.2",
"TOKEN-RING",
"ETHERNET_802.3"
"802.4",
"Reserved",
"GNET",
"PRONET-10",
"ETHERNET_SNAP",
"TOKEN-RING_SNAP",
"LANPAC_II",
"ISDN",
"NOVELL_RX-NET",
"IBM_PCN2_802.2",
"IBM_PCN2_SNAP",
"OMNINET/4",
"3270_COAXA",
"IP"
"FDDI_802.2",
"IVDLAN_802.9",
"DATACO_OSI",
"FDDI_SNAP"
};
//
// Handle for this "protocol" for NDIS
//
NDIS_HANDLE NdisMlidProtocolHandle = NULL;
//
// Spin lock for accessing MLID list
//
NDIS_SPIN_LOCK NdisMlidSpinLock;
//
// Array of MLIDs
//
extern PMLID_BOARDS MlidBoards = NULL;
//
// Number of MLIDs in use
//
extern UINT32 CountMlidBoards = 0;
//
// Size of the MLID array
//
extern UINT32 AllocatedMlidBoards = 0;
NTSTATUS
NdisMlidRegisterProtocol(
IN UNICODE_STRING *NameString
);
VOID
NdisMlidDeregisterProtocol(
VOID
);
NTSTATUS
NdisMlidInitializeMlids(
VOID
);
VOID
NdisMlidUnloadMlids(
IN PDRIVER_OBJECT DriverObject
);
VOID
NdisMlidUnloadMlid(
IN PMLID_STRUCT Mlid
);
PMLID_STRUCT
NdisMlidOpenMlid(
PVOID OdiRegistryPtr,
PVOID NdisRegistryPtr,
);
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This is the entry point for the driver.
Arguments:
DriverObject - The driver object for this driver in the NT system.
RegistryPath - Path in the registry to it's parameters.
Return Value:
Indicates the success or failure of the initialization.
--*/
{
NTSTATUS Status;
UNICODE_STRING NameString = UNICODE_STRING_CONST("\\Device\\NdisMlid");
//
// Fill in unload handler
//
DriverObject->DriverUnload = NdisMlidUnloadMlids;
//
// make ourselves known to the NDIS wrapper.
//
Status = NdisMlidRegisterProtocol(&NameString);
if (!NT_SUCCESS (Status)) {
#if DBG
DbgPrint("NdisMlidInitialize: RegisterProtocol failed!\n");
#endif
return STATUS_INSUFFICIENT_RESOURCES;
}
NdisAllocateSpinLock(&NdisMlidSpinLock);
//
// Now Initialize all the boards according to registry
//
Status = NdisMlidInitializeMlids();
if (!(NT_SUCCESS(Status))) {
NdisFreeSpinLock(&NdisMlidSpinLock);
NdisMlidDeregisterProtocol();
return(Status);
}
return(STATUS_SUCCESS);
}
NTSTATUS
NdisMlidRegisterProtocol(
IN UNICODE_STRING *NameString
)
/*++
Routine Description:
This routine introduces this driver as a transport to the NDIS interface.
Arguments:
NameString - Name of this device.
Return Value:
STATUS_SUCCESS - Success.
STATUS_INSUFFICIENT_RESOURCES - Failure.
--*/
{
NDIS_STATUS NdisStatus;
PNDIS_PROTOCOL_CHARACTERISTICS ProtChars; // Used temporarily to register
ProtChars = ExAllocatePool(NonPagedPool,
sizeof(NDIS_PROTOCOL_CHARACTERISTICS) + NameString->Length
);
//
// Set up the characteristics of this protocol
//
ProtChars->MajorNdisVersion = 3;
ProtChars->MinorNdisVersion = 0;
ProtChars->Name.Length = NameString->Length;
ProtChars->Name.Buffer = (PVOID)(ProtChars + 1);
ProtChars->OpenAdapterCompleteHandler = NdisMlidOpenAdapterComplete;
ProtChars->CloseAdapterCompleteHandler = NdisMlidCloseAdapterComplete;
ProtChars->ResetCompleteHandler = NdisMlidResetComplete;
ProtChars->RequestCompleteHandler = NdisMlidRequestComplete;
ProtChars->SendCompleteHandler = NdisMlidSendComplete;
ProtChars->TransferDataCompleteHandler = NdisMlidTransferDataComplete;
ProtChars->ReceiveHandler = NdisMlidReceive;
ProtChars->ReceiveCompleteHandler = NdisMlidReceiveComplete;
ProtChars->StatusHandler = NdisMlidStatus;
ProtChars->StatusCompleteHandler = NdisMlidStatusComplete;
NdisRegisterProtocol (
&NdisStatus,
&NdisMlidProtocolHandle,
ProtChars,
(UINT)sizeof(NDIS_PROTOCOL_CHARACTERISTICS) + NameString->Length
);
ExFreePool(ProtChars);
if (NdisStatus != NDIS_STATUS_SUCCESS) {
#if DBG
DbgPrint("NdisMlidInitialize: NdisRegisterProtocol failed: %s\n", NdisStatus);
#endif
return (NTSTATUS)NdisStatus;
}
return STATUS_SUCCESS;
}
VOID
NdisMlidDeregisterProtocol(
VOID
)
/*++
Routine Description:
This routine removes this transport to the NDIS interface.
Arguments:
None.
Return Value:
None.
--*/
{
NDIS_STATUS NdisStatus;
if (NdisMlidProtocolHandle != (NDIS_HANDLE)NULL) {
NdisDeregisterProtocol (
&NdisStatus,
NdisMlidProtocolHandle
);
NdisMlidProtocolHandle = (NDIS_HANDLE)NULL;
}
}
VOID
NdisMlidUnloadMlids(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This routine shuts down all the MLIDs and unloads itself.
Arguments:
None.
Return Value:
None.
--*/
{
UINT32 i;
NdisAcquireSpinLock(&NdisMlidSpinLock);
//
// For each MLID
//
for (i = 0; i < AllocatedMlidBoards; i++) {
//
// If MLID was opened
//
if (MlidBoards[i].Mlid != NULL) {
//
// Unload MLID
//
NdisMlidUnloadMlid(MlidBoards[i].Mlid);
MlidBoards[i].Mlid = NULL;
CountMlidBoards--;
if (CountMlidBoards == 0) {
break;
}
}
}
NdisReleaseSpinLock(&NdisMlidSpinLock);
NdisFreeSpinLock(&NdisMlidSpinLock);
NdisMlidDeregisterProtocol();
ExFreePool(MlidBoards);
}
VOID
NdisMlidUnloadMlid(
IN PMLID_STRUCT Mlid
)
/*++
Routine Description:
This routine removes this transport to the NDIS interface.
NOTE: You must hold the NdisMlidSpinLock when calling this routine!!
Arguments:
None.
Return Value:
None.
--*/
{
UINT32 Status;
NDIS_STATUS NdisStatus;
PECB SendECB;
NdisAcquireSpinLock(&(Mlid->MlidSpinLock));
//
// Set unloading flag
//
Mlid->Unloading = TRUE;
//
// Remove all pending ECBs
//
while (Mlid->FirstPendingSend != NULL) {
//
// Remove ECB from queue
//
SendECB = Mlid->FirstPendingSend;
Mlid->FirstPendingSend = SendECB->ECB_NextLink;
if (SendECB->ECB_NextLink != NULL) {
SendECB->ECB_NextLink->ECB_PreviousLink = NULL;
}
if (Mlid->LastPendingSend == SendECB) {
Mlid->LastPendingSend = NULL;
}
//
// Cancel the ECB
//
SendECB->ECB_Status = (UINT16)CANCELED;
//
// Give it to LSL
//
NdisReleaseSpinLock(&(Mlid->MlidSpinLock));
(*(Mlid->LSLFunctionList->SupportAPIArray[HoldEvent_INDEX]))(
SendECB
);
NdisAcquireSpinLock(&(Mlid->MlidSpinLock));
}
//
// Service all held sends
//
NdisReleaseSpinLock(&(Mlid->MlidSpinLock));
(*(Mlid->LSLFunctionList->SupportAPIArray[ServiceEvents_INDEX]))(
);
//
// Close Ndis Adapter
//
NdisCloseAdapter(&NdisStatus, Mlid->NdisBindingHandle);
//
// If pend, wait
//
if (NdisStatus == NDIS_STATUS_PENDING) {
//
// Wait for close to complete
//
KeWaitForSingleObject(
&(Mlid->MlidRequestCompleteEvent),
Executive,
KernelMode,
TRUE,
(PTIME)NULL
);
KeResetEvent(&Mlid->MlidRequestCompleteEvent);
}
//
// Get status
//
Status = Mlid->RequestStatus;
//
// Deregister with LSL
//
(*(Mlid->LSLFunctionList->SupportAPIArray[DeRegisterMLID_INDEX]))(
Mlid->ConfigTable.MLIDCFG_BoardNumber
);
NdisAcquireSpinLock(&(Mlid->MlidSpinLock));
//
// Free up resources
//
Mlid->StatsTable->References--;
if (Mlid->StatsTable->References == 0) {
ExFreePool(Mlid->StatsTable);
}
if (Mlid->MulticastAddresses.MAAllocated != 0) {
if (Mlid->NdisMlidMedium != NdisMedium802_5) {
ExFreePool(Mlid->MulticastAddresses.Addresses);
}
ExFreePool(Mlid->MulticastAddresses.EnableCounts);
Mlid->MulticastAddresses.MAAllocated = 0;
}
NdisReleaseSpinLock(&(Mlid->MlidSpinLock));
NdisFreeSpinLock(&(Mlid->MlidSpinLock));
ExFreePool(Mlid);
}
NTSTATUS
NdisMlidInitializeMlids(
VOID
)
/*++
Routine Description:
This routine loads all ODI boards that are supposed to be loaded.
Arguments:
None.
Return Value:
NDIS_STATUS_SUCCESS if a single load succeeds.
NDIS_STATUS_FAILURE is no boards get loaded.
--*/
{
BOOLEAN LoadedADriver = FALSE;
PMLID_STRUCT Mlid;
PVOID OdiRegistryPtr;
PVOID NdisRegistryPtr;
UINT32 i;
//
// Find first ODI driver that is supposed to be loaded
//*\\ here - Config
//
// Find corresponding NDIS MAC Info
//*\\ here - Config
while (TRUE) {
//
// Open this MLID
//
Mlid = NdisMlidOpenMlid(OdiRegistryPtr, NdisRegistryPtr);
if (Mlid != NULL) {
//
// Do we have space in global array for this MLID
//
if (CountMlidBoards == AllocatedMlidBoards) {
//
// Allocate more space
//
Mlid = (PMLID_STRUCT)ExAllocatePool(
NonPagedPool,
sizeof(MLID_STRUCT) * (CountMlidBoards + 1)
);
if (Mlid == NULL) {
//
// No more memory available, unload opened MLID
//
NdisMlidUnloadMlid(Mlid);
return(STATUS_INSUFFICIENT_RESOURCES);
}
//
// Copy Array
//
RtlCopyMemory(Mlid, MlidBoards, sizeof(MLID_STRUCT) * CountMlidBoards);
//
// Init last cell
//
MlidBoards = Mlid;
MlidBoards[CountMlidBoards].Mlid = NULL;
MlidBoards[CountMlidBoards].BoardNumber = (UINT32)-1;
AllocatedMlidBoards++;
}
//
// Find an open index
//
for (i=0; i < AllocatedMlidBoards; i++) {
if (MlidBoards[i].Mlid == NULL) {
//
// Store into global array all info
//
MlidBoards[i].Mlid = Mlid;
MlidBoards[i].BoardNumber = Mlid->ConfigTable.MLIDCFG_BoardNumber;
MlidBoards[i].AdapterName = &(Mlid->AdapterName);
break;
}
}
//
// Set flag so we know to stay loaded.
//
LoadedADriver = TRUE;
}
//
// Get next ODI board to be loaded
//*\\ here - Config
//
// Get corresponding NDIS MAC Info
//*\\ here - Config
}
if (LoadedADriver) {
return(NDIS_STATUS_SUCCESS);
}
return(NDIS_STATUS_FAILURE);
}
PMLID_STRUCT
NdisMlidOpenMlid(
PVOID OdiRegistryPtr,
PVOID NdisRegistryPtr,
)
/*++
Routine Description:
This routine will create a binding from an ODI board to an NDIS MAC. If the
NDIS MAC is not yet open, it will do so.
Arguments:
OdiRegistryPtr - Pointer into the ODI registry information for this board number.
NdisRegistryPtr - Pointer into the corresponding NDIS registry for the ODI board.
Return Value:
NULL - If error, else a pointer to the MLID structure.
--*/
{
PMLID_STRUCT Mlid;
PNDISMLID_StatsTable StatsTable;
UINT32 i;
UINT32 j;
UNICODE_STRING AdapterName;
NDIS_STATUS NdisStatus;
NDIS_STATUS OpenErrorStatus;
NDIS_HANDLE NdisBindingHandle;
NDIS_MEDIUM MediumArray[] = {NdisMedium802_3, NdisMedium802_5, NdisMediumFddi };
UINT SelectedMediumIndex;
UINT32 TotalFrameSize;
UINT16 FrameID;
UINT32 NdisLinkSpeed;
UINT32 NdisMacOptions;
PCM_RESOURCE_LIST Resources;
PNDIS_REQUEST NdisMlidRequest;
//
// Allocate memory for this MLID
//
Mlid = (PMLID_STRUCT)ExAllocatePool(NonPagedPool, sizeof(MLID_STRUCT));
if (Mlid == NULL) {
return(NULL);
}
RtlZeroMemory(Mlid, sizeof(MLID_STRUCT));
//
// Allocate NDIS_REQUEST for this initialization
//
NdisMlidRequest = (PNDIS_REQUEST)ExAllocatePool(NonPagedPool, sizeof(NDIS_REQUEST));
if (NdisMlidRequest == NULL) {
goto Fail1;
}
Mlid->SendPacketPool = (NDIS_HANDLE)NULL;
Mlid->SendBufferPool = (NDIS_HANDLE)NULL;
Mlid->ReceivePacketPool = (NDIS_HANDLE)NULL;
Mlid->ReceiveBufferPool = (NDIS_HANDLE)NULL;
//
// Allocate NDIS_PACKET_POOL for sends
//
NdisAllocatePacketPool(
&NdisStatus,
&(Mlid->SendPacketPool),
NDISMLID_SENDS_PER_MLID,
sizeof(MLID_RESERVED)
);
if (NdisStatus != NDIS_STATUS_SUCCESS) {
goto Fail2;
}
//
// Allocate NDIS_BUFFER_POOL for sends
//
NdisAllocateBufferPool(
&NdisStatus,
&(Mlid->SendBufferPool),
NDISMLID_SENDS_PER_MLID * NDISMLID_BUFFERS_PER_PACKET
);
if (NdisStatus != NDIS_STATUS_SUCCESS) {
goto Fail2;
}
//
// Set LSLFunctionList here
//*\\ here - How do I get the LSLFunctionList?
//
// Fail if count of functions is not enough, or if LSL version number is
// incorrect
//*\\ here - Implement check of LSLFunctionList.
//
// Open Send stage
//
Mlid->StageOpen = TRUE;
//
// Initialize completion event
//
KeInitializeEvent(
&(Mlid->MlidRequestCompleteEvent),
NotificationEvent,
FALSE
);
Mlid->UsingEvent = TRUE;
//
// Get name of NDIS MAC from registry
//*\\ here - Config
//
// Call NdisOpenAdapter
//
NdisOpenAdapter(
&NdisStatus,
&OpenErrorStatus,
&NdisBindingHandle,
&SelectedMediumIndex,
MediumArray,
3,
NdisMlidProtocolHandle,
(NDIS_HANDLE)Mlid,
(PNDIS_STRING)(&AdapterName),
0,
NULL
);
//
// if (pending), wait
//
if (NdisStatus == NDIS_STATUS_PENDING) {
//
// Wait for open to complete
//
KeWaitForSingleObject(
&(Mlid->MlidRequestCompleteEvent),
Executive,
KernelMode,
TRUE,
(PTIME)NULL
);
KeResetEvent(&Mlid->MlidRequestCompleteEvent);
//
// Get status
//
NdisStatus = (NDIS_STATUS)(Mlid->RequestStatus);
}
//
// If failure, release resources
//
if (NdisStatus != NDIS_STATUS_SUCCESS) {
goto Fail1;
}
//
// Save Media type
//
Mlid->NdisMlidMedium = MediumArray[SelectedMediumIndex];
//
// Save Ndis MAC context
//
Mlid->NdisBindingHandle = NdisBindingHandle;
//
// Is NDIS MAC not already opened by another MLID?
//
for (i=0; i < AllocatedMlidBoards ; i++ ) {
if (MlidBoards[i].Mlid != NULL) {
//
// Are the names the same?
//
if (RtlEqualUnicodeString(&AdapterName, MlidBoards[i].AdapterName, TRUE)) {
//
// Store stat table pointer
//
StatsTable = (MlidBoards[i].Mlid)->StatsTable;
break;
}
}
}
//
// Did we find an NDIS MAC?
//
if (StatsTable == NULL) {
//
// Allocate memory for the MLID Statistic table
//
StatsTable = (PNDISMLID_StatsTable)ExAllocatePool(NonPagedPool,
sizeof(NDISMLID_StatsTable)
);
if (StatsTable == NULL) {
//
// Fail
//
goto Fail3;
}
//
// Initialize structure
//
RtlZeroMemory(StatsTable, sizeof(NDISMLID_StatsTable));
StatsTable->StatsTable.MStatTableMajorVer = 1;
StatsTable->StatsTable.MStatTableMinorVer = 0;
//
// Setup Generic counters
//
for (i = 0; i < NUM_GENERIC_COUNTS; i++) {
StatsTable->MLID_GenericCounts[i].StatString = &(NdisMlidGenericStatStrings[i]);
StatsTable->MLID_GenericCounts[i].StatUseFlag = NdisMlidGenericStatTypes[i];
StatsTable->MLID_GenericCounts[i].StatCounter = (PVOID)(&StatsTable->GenericCounts[i]);
}
StatsTable->StatsTable.MNumGenericCounters = NUM_GENERIC_COUNTS;
StatsTable->StatsTable.MGenericCountsPtr = &(StatsTable->MLID_GenericCounts);
//
// Setup Medium specific counters
//
switch (Mlid->NdisMlidMedium) {
case NdisMedium802_3:
for (i = 0; i < NUM_ETHERNET_COUNTS; i++) {
StatsTable->MLID_MediaCounts[i].StatString = &(NdisMlidEthernetStatStrings[i]);
StatsTable->MLID_MediaCounts[i].StatUseFlag = NdisMlidEthernetStatTypes[i];
StatsTable->MLID_MediaCounts[i].StatCounter = (PVOID)(&StatsTable->MediaCounts[i]);
}
StatsTable->StatsTable.MNumMediaCounters = NUM_ETHERNET_COUNTS;
StatsTable->StatsTable.MMediaCountsPtr = &(StatsTable->MLID_MediaCounts);
break;
case NdisMedium802_5:
for (i = 0; i < NUM_TOKEN_RING_COUNTS; i++) {
StatsTable->MLID_MediaCounts[i].StatString = &(NdisMlidTokenRingStatStrings[i]);
StatsTable->MLID_MediaCounts[i].StatUseFlag = NdisMlidTokenRingStatTypes[i];
StatsTable->MLID_MediaCounts[i].StatCounter = (PVOID)(&StatsTable->MediaCounts[i]);
}
StatsTable->StatsTable.MNumMediaCounters = NUM_TOKEN_RING_COUNTS;
StatsTable->StatsTable.MMediaCountsPtr = &(StatsTable->MLID_MediaCounts);
break;
case NdisMediumFddi:
for (i = 0; i < NUM_FDDI_COUNTS; i++) {
StatsTable->MLID_MediaCounts[i].StatString = &(NdisMlidFddiStatStrings[i]);
StatsTable->MLID_MediaCounts[i].StatUseFlag = NdisMlidFddiStatTypes[i];
StatsTable->MLID_MediaCounts[i].StatCounter = (PVOID)(&StatsTable->MediaCounts[i]);
}
StatsTable->StatsTable.MNumMediaCounters = NUM_FDDI_COUNTS;
StatsTable->StatsTable.MMediaCountsPtr = &(StatsTable->MLID_MediaCounts);
break;
}
}
//
// Link statistic table
//
Mlid->StatsTable = StatsTable;
//
// Increment reference count
//
StatsTable->References++;
//
// Get network address
//
NdisMlidRequest->RequestType = NdisRequestQueryInformation;
switch (Mlid->NdisMlidMedium) {
case NdisMedium802_3:
NdisMlidRequest->DATA.QUERY_INFORMATION.Oid = OID_802_3_CURRENT_ADDRESS;
break;
case NdisMedium802_5:
NdisMlidRequest->DATA.QUERY_INFORMATION.Oid = OID_802_5_CURRENT_ADDRESS;
break;
case NdisMediumFddi:
NdisMlidRequest->DATA.QUERY_INFORMATION.Oid = OID_FDDI_LONG_CURRENT_ADDR;
break;
}
NdisMlidRequest->DATA.QUERY_INFORMATION.InformationBuffer = (PVOID)&(Mlid->ConfigTable.MLIDCFG_NodeAddress[0]);
NdisMlidRequest->DATA.QUERY_INFORMATION.InformationBufferLength = 6;
NdisMlidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0;
NdisMlidRequest->DATA.QUERY_INFORMATION.BytesNeeded = 0;
NdisRequest(
&NdisStatus,
Mlid->NdisBindingHandle,
NdisMlidRequest
);
//
// if (pending), wait
//
if (NdisStatus == NDIS_STATUS_PENDING) {
//
// Wait for request to complete
//
KeWaitForSingleObject(
&(Mlid->MlidRequestCompleteEvent),
Executive,
KernelMode,
TRUE,
(PTIME)NULL
);
KeResetEvent(&Mlid->MlidRequestCompleteEvent);
//
// Get status
//
NdisStatus = (NDIS_STATUS)Mlid->RequestStatus;
}
//
// If failure, release resources and close adapter
//
if (NdisStatus != NDIS_STATUS_SUCCESS) {
goto Fail4;
}
//
// Get FrameID for this MLID from registry, check that it is one we support.
//*\\ here - Config
//
// Get maximum total frame size
//
NdisMlidRequest->DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_TOTAL_SIZE;
NdisMlidRequest->DATA.QUERY_INFORMATION.InformationBuffer = (PVOID)&(TotalFrameSize);
NdisMlidRequest->DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(TotalFrameSize);
NdisMlidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0;
NdisMlidRequest->DATA.QUERY_INFORMATION.BytesNeeded = 0;
NdisRequest(
&NdisStatus,
Mlid->NdisBindingHandle,
NdisMlidRequest
);
//
// if (pending), wait
//
if (NdisStatus == NDIS_STATUS_PENDING) {
//
// Wait for request to complete
//
KeWaitForSingleObject(
&(Mlid->MlidRequestCompleteEvent),
Executive,
KernelMode,
TRUE,
(PTIME)NULL
);
KeResetEvent(&Mlid->MlidRequestCompleteEvent);
//
// Get status
//
NdisStatus = (NDIS_STATUS)Mlid->RequestStatus;
}
//
// If failure, release resources and close adapter
//
if (NdisStatus != NDIS_STATUS_SUCCESS) {
goto Fail4;
}
//
// Get link speed
//
NdisMlidRequest->DATA.QUERY_INFORMATION.Oid = OID_GEN_LINK_SPEED;
NdisMlidRequest->DATA.QUERY_INFORMATION.InformationBuffer = (PVOID)&(NdisLinkSpeed);
NdisMlidRequest->DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(NdisLinkSpeed);
NdisMlidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0;
NdisMlidRequest->DATA.QUERY_INFORMATION.BytesNeeded = 0;
NdisRequest(
&NdisStatus,
Mlid->NdisBindingHandle,
NdisMlidRequest
);
//
// if (pending), wait
//
if (NdisStatus == NDIS_STATUS_PENDING) {
//
// Wait for request to complete
//
KeWaitForSingleObject(
&(Mlid->MlidRequestCompleteEvent),
Executive,
KernelMode,
TRUE,
(PTIME)NULL
);
KeResetEvent(&Mlid->MlidRequestCompleteEvent);
//
// Get status
//
NdisStatus = (NDIS_STATUS)Mlid->RequestStatus;
}
//
// If failure, release resources and close adapter
//
if (NdisStatus != NDIS_STATUS_SUCCESS) {
goto Fail4;
}
//
// Get MAC_OPTIONS
//
NdisMlidRequest->DATA.QUERY_INFORMATION.Oid = OID_GEN_MAC_OPTIONS;
NdisMlidRequest->DATA.QUERY_INFORMATION.InformationBuffer = (PVOID)&(NdisMacOptions);
NdisMlidRequest->DATA.QUERY_INFORMATION.InformationBufferLength = sizeof(NdisMacOptions);
NdisMlidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0;
NdisMlidRequest->DATA.QUERY_INFORMATION.BytesNeeded = 0;
NdisRequest(
&NdisStatus,
Mlid->NdisBindingHandle,
NdisMlidRequest
);
//
// if (pending), wait
//
if (NdisStatus == NDIS_STATUS_PENDING) {
//
// Wait for request to complete
//
KeWaitForSingleObject(
&(Mlid->MlidRequestCompleteEvent),
Executive,
KernelMode,
TRUE,
(PTIME)NULL
);
KeResetEvent(&Mlid->MlidRequestCompleteEvent);
//
// Get status
//
NdisStatus = (NDIS_STATUS)Mlid->RequestStatus;
}
//
// If failure, release resources and close adapter
//
if (NdisStatus != NDIS_STATUS_SUCCESS) {
goto Fail4;
}
if ((NdisMacOptions & NDIS_MAC_OPTION_RECEIVE_SERIALIZED) &&
(NdisMacOptions & NDIS_MAC_OPTION_TRANSFERS_NOT_PEND)) {
//
// Allocate NDIS_PACKET_POOL for receives
//
NdisAllocatePacketPool(
&NdisStatus,
&(Mlid->ReceivePacketPool),
1,
sizeof(MLID_RESERVED)
);
if (NdisStatus != NDIS_STATUS_SUCCESS) {
goto Fail4;
}
//
// Allocate NDIS_BUFFER_POOL for receives
// NOTE: In this case I added a huge number of NDIS_BUFFERS, since
// we only have one packet we need to be liberal in our guess of
// BUFFERs per PACKET.
//
NdisAllocateBufferPool(
&NdisStatus,
&(Mlid->ReceiveBufferPool),
NDISMLID_BUFFERS_PER_PACKET + 10
);
if (NdisStatus != NDIS_STATUS_SUCCESS) {
goto Fail4;
}
} else {
//
// Allocate NDIS_PACKET_POOL for receives
//
NdisAllocatePacketPool(
&NdisStatus,
&(Mlid->ReceivePacketPool),
NDISMLID_RECEIVES_PER_MLID,
sizeof(MLID_RESERVED)
);
if (NdisStatus != NDIS_STATUS_SUCCESS) {
goto Fail4;
}
//
// Allocate NDIS_BUFFER_POOL for receives
//
NdisAllocateBufferPool(
&NdisStatus,
&(Mlid->ReceiveBufferPool),
NDISMLID_RECEIVES_PER_MLID * NDISMLID_BUFFERS_PER_PACKET + 5
);
if (NdisStatus != NDIS_STATUS_SUCCESS) {
goto Fail4;
}
}
//
// Begin Init ConfigTable.
//
RtlCopyMemory(&(Mlid->ConfigTable.MLIDCFG_Signature[0]),
"HardwareDriverMLID ",
26
);
Mlid->ConfigTable.MLIDCFG_MajorVersion = 1;
Mlid->ConfigTable.MLIDCFG_MinorVersion = 12;
Mlid->ConfigTable.MLIDCFG_ModeFlags = 0x24C9; // PromiscuousBit |
// SupFragECB |
// SupFrameDataSize |
// RawSend |
// RealDriver |
// MulticastBit
Mlid->ConfigTable.MLIDCFG_BoardNumber = (UINT16)-1;
Mlid->ConfigTable.MLIDCFG_BoardInstance = 1; //*\\ Should this change?
Mlid->ConfigTable.MLIDCFG_MaxFrameSize = TotalFrameSize;
switch (FrameID) {
case ETHERNET_II_FRAME_ID:
Mlid->ConfigTable.MLIDCFG_BestDataSize = TotalFrameSize - sizeof(Ethernet_II_Header);
Mlid->ConfigTable.MLIDCFG_WorstDataSize = TotalFrameSize - sizeof(Ethernet_II_Header);
break;
case ETHERNET_802_2_FRAME_ID:
Mlid->ConfigTable.MLIDCFG_BestDataSize = TotalFrameSize - sizeof(Ethernet_802_2_Header);
Mlid->ConfigTable.MLIDCFG_WorstDataSize = TotalFrameSize - sizeof(Ethernet_802_2_Header);
break;
case ETHERNET_802_3_FRAME_ID:
Mlid->ConfigTable.MLIDCFG_BestDataSize = TotalFrameSize - sizeof(Ethernet_802_3_Header);
Mlid->ConfigTable.MLIDCFG_WorstDataSize = TotalFrameSize - sizeof(Ethernet_802_3_Header);
break;
case ETHERNET_SNAP_FRAME_ID:
Mlid->ConfigTable.MLIDCFG_BestDataSize = TotalFrameSize - sizeof(Ethernet_Snap_Header);
Mlid->ConfigTable.MLIDCFG_WorstDataSize = TotalFrameSize - sizeof(Ethernet_Snap_Header);
break;
case FDDI_802_2_FRAME_ID:
Mlid->ConfigTable.MLIDCFG_BestDataSize = TotalFrameSize - sizeof(Fddi_802_2_Header);
Mlid->ConfigTable.MLIDCFG_WorstDataSize = TotalFrameSize - sizeof(Fddi_802_2_Header);
break;
case FDDI_SNAP_FRAME_ID:
Mlid->ConfigTable.MLIDCFG_BestDataSize = TotalFrameSize - sizeof(Fddi_Snap_Header);
Mlid->ConfigTable.MLIDCFG_WorstDataSize = TotalFrameSize - sizeof(Fddi_Snap_Header);
break;
case TOKEN_RING_SNAP_FRAME_ID:
Mlid->ConfigTable.MLIDCFG_BestDataSize = TotalFrameSize - sizeof(TokenRing_Header) - 8;
Mlid->ConfigTable.MLIDCFG_WorstDataSize = TotalFrameSize - sizeof(TokenRing_Header) - 26;
break;
case TOKEN_RING_802_2_FRAME_ID:
Mlid->ConfigTable.MLIDCFG_BestDataSize = TotalFrameSize - sizeof(TokenRing_Header) - 3;
Mlid->ConfigTable.MLIDCFG_WorstDataSize = TotalFrameSize - sizeof(TokenRing_Header) - 21;
break;
default:
//
// Invalid frame type. We should have already checked this.
//
ASSERT(0);
}
Mlid->ConfigTable.MLIDCFG_CardName = NULL; //*\\ here
Mlid->ConfigTable.MLIDCFG_ShortName = NULL; //*\\ here
Mlid->ConfigTable.MLIDCFG_FrameTypeString = &(NdisMlidFrameTypeStrings[FrameID]);
Mlid->ConfigTable.MLIDCFG_FrameID = FrameID;
Mlid->ConfigTable.MLIDCFG_TransportTime = (0x1000 / (NdisLinkSpeed / 10)) + 1;
Mlid->ConfigTable.MLIDCFG_SourceRouting = (PVOID)NULL;
Mlid->ConfigTable.MLIDCFG_LookAheadSize = 18;
//
// Convert to KBits/Second.
//
NdisLinkSpeed /= 10;
//
// Check if we can store it like this.
//
if (NdisLinkSpeed > 0x7FFF) {
//
// Convert to MBits/Second
//
NdisLinkSpeed /= 1000;
} else {
NdisLinkSpeed |= 0x8000;
}
Mlid->ConfigTable.MLIDCFG_LineSpeed = NdisLinkSpeed;
Mlid->ConfigTable.MLIDCFG_DriverMajorVer = 3;
Mlid->ConfigTable.MLIDCFG_DriverMinorVer = 0;
Mlid->ConfigTable.MLIDCFG_Flags = 0x600; // Adapter does group address filtering
Mlid->ConfigTable.MLIDCFG_SendRetries = (Mlid->NdisMlidMedium == NdisMedium802_3) ? 10 : 0;
Mlid->ConfigTable.MLIDCFG_Slot = 0; //*\\ change for EISA and MCA cards.
//
// Now we grunge through NDIS structures to get IoPort infor, Memory Info, DMA info
// and interrupt info.
//
Resources = ((PNDIS_OPEN_BLOCK)NdisBindingHandle)->AdapterHandle->Resources;
//
// First do port information
//
{
BOOLEAN TooManyPorts = FALSE;
for (i = 0; i < Resources->Count; i++) {
for (j=0; j < Resources->List[0].PartialResourceList.Count; j++) {
if (Resources->List[0].PartialResourceList.PartialDescriptors[j].Type ==
CmResourceTypePort) {
//
// Found a port
//
if (!TooManyPorts) {
TooManyPorts = TRUE;
//
// Store in Port0 holder
//
Mlid->ConfigTable.MLIDCFG_IOPort0 = (UINT16)(
Resources->List[0].PartialResourceList.PartialDescriptors[j].u.Port.Start.LowPart);
Mlid->ConfigTable.MLIDCFG_IORange0 = (UINT16)(
Resources->List[0].PartialResourceList.PartialDescriptors[j].u.Port.Length);
} else {
//
// Store in Port1 holder and exit port.
//
Mlid->ConfigTable.MLIDCFG_IOPort1 = (UINT16)(
Resources->List[0].PartialResourceList.PartialDescriptors[j].u.Port.Start.LowPart);
Mlid->ConfigTable.MLIDCFG_IORange1 = (UINT16)(
Resources->List[0].PartialResourceList.PartialDescriptors[j].u.Port.Length);
i = Resources->Count;
break;
}
}
}
}
}
//
// Now do memory information
//
{
BOOLEAN TooManyMemorys = FALSE;
for (i = 0; i < Resources->Count; i++) {
for (j=0; j < Resources->List[0].PartialResourceList.Count; j++) {
if (Resources->List[0].PartialResourceList.PartialDescriptors[j].Type ==
CmResourceTypeMemory) {
//
// Found memory
//
if (!TooManyMemorys) {
TooManyMemorys = TRUE;
//
// Store in Memory0 holder
//
Mlid->ConfigTable.MLIDCFG_MemoryAddress0 = (UINT32)(
Resources->List[0].PartialResourceList.PartialDescriptors[j].u.Memory.Start.LowPart);
Mlid->ConfigTable.MLIDCFG_MemorySize0 = (UINT16)(
Resources->List[0].PartialResourceList.PartialDescriptors[j].u.Memory.Length / 16);
} else {
//
// Store in Memory1 holder and exit.
//
Mlid->ConfigTable.MLIDCFG_MemoryAddress1 = (UINT32)(
Resources->List[0].PartialResourceList.PartialDescriptors[j].u.Memory.Start.LowPart);
Mlid->ConfigTable.MLIDCFG_MemorySize1 = (UINT16)(
Resources->List[0].PartialResourceList.PartialDescriptors[j].u.Memory.Length / 16);
i = Resources->Count;
break;
}
}
}
}
}
//
// Now do interrupts
//
{
BOOLEAN TooManyInterrupts = FALSE;
for (i = 0; i < Resources->Count; i++) {
for (j=0; j < Resources->List[0].PartialResourceList.Count; j++) {
if (Resources->List[0].PartialResourceList.PartialDescriptors[j].Type ==
CmResourceTypeInterrupt) {
//
// Found interrupt
//
if (!TooManyInterrupts) {
TooManyInterrupts = TRUE;
//
// Store in Interrupt0 holder
//
Mlid->ConfigTable.MLIDCFG_Interrupt0 = (UINT8)(
Resources->List[0].PartialResourceList.PartialDescriptors[j].u.Interrupt.Level);
if (Resources->List[0].PartialResourceList.PartialDescriptors[j].ShareDisposition ==
CmResourceShareShared) {
Mlid->ConfigTable.MLIDCFG_SharingFlags |= 0x20;
}
} else {
//
// Store in Interrupt1 holder and exit.
//
Mlid->ConfigTable.MLIDCFG_Interrupt1 = (UINT8)(
Resources->List[0].PartialResourceList.PartialDescriptors[j].u.Interrupt.Level);
if (Resources->List[0].PartialResourceList.PartialDescriptors[j].ShareDisposition ==
CmResourceShareShared) {
Mlid->ConfigTable.MLIDCFG_SharingFlags |= 0x40;
}
i = Resources->Count;
break;
}
}
}
}
}
//
// Now do DMA channels
//
{
BOOLEAN TooManyDMAs = FALSE;
for (i = 0; i < Resources->Count; i++) {
for (j=0; j < Resources->List[0].PartialResourceList.Count; j++) {
if (Resources->List[0].PartialResourceList.PartialDescriptors[j].Type ==
CmResourceTypeDma) {
//
// Found DMA Channel
//
if (!TooManyDMAs) {
TooManyDMAs = TRUE;
//
// Store in DMALine0 holder
//
Mlid->ConfigTable.MLIDCFG_DMALine0 = (UINT8)(
Resources->List[0].PartialResourceList.PartialDescriptors[j].u.Dma.Channel);
} else {
//
// Store in DMALine1 holder and exit.
//
Mlid->ConfigTable.MLIDCFG_DMALine1 = (UINT8)(
Resources->List[0].PartialResourceList.PartialDescriptors[j].u.Dma.Channel);
i = Resources->Count;
break;
}
}
}
}
}
Mlid->ConfigTable.MLIDCFG_ResourceTag = 0;
//
// End of initialize ConfigTable.
//
//
// Register with LSL
//
(*(Mlid->LSLFunctionList->SupportAPIArray[RegisterMLID_INDEX]))(
NdisMlidHandlerInfo,
&(Mlid->ConfigTable),
&(Mlid->BoardNumber)
);
Mlid->UsingEvent = FALSE;
//
// Setup default packet filters
//
Mlid->RequestStatus = NDIS_STATUS_PENDING;
ASSERT(sizeof(TotalFrameSize) == 4);
TotalFrameSize = NDIS_PACKET_TYPE_DIRECTED |
NDIS_PACKET_TYPE_MULTICAST |
NDIS_PACKET_TYPE_BROADCAST;
NdisMlidRequest->DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
NdisMlidRequest->DATA.SET_INFORMATION.InformationBuffer = (PVOID)&(TotalFrameSize);
NdisMlidRequest->DATA.SET_INFORMATION.InformationBufferLength = sizeof(TotalFrameSize);
NdisMlidRequest->DATA.SET_INFORMATION.BytesRead = 0;
NdisMlidRequest->DATA.SET_INFORMATION.BytesNeeded = 0;
NdisRequest(
&NdisStatus,
Mlid->NdisBindingHandle,
NdisMlidRequest
);
if (NdisStatus == NDIS_STATUS_PENDING) {
//
// Get status and Semaphore is release inside RequestCompleteHandler
//
NdisStatus = (NDIS_STATUS)Mlid->RequestStatus;
} else {
ExFreePool(NdisMlidRequest);
}
//
// If failure, release resources and close adapter
//
if ((NdisStatus != NDIS_STATUS_SUCCESS) &&
(NdisStatus != NDIS_STATUS_PENDING)) {
goto Fail5;
}
//
// Time stamp the sucker
//
{
LARGE_INTEGER TimeStamp;
KeQuerySystemTime(&TimeStamp);
(*((PUINT32)((*(Mlid->StatsTable->StatsTable.MGenericCountsPtr))[13].StatCounter))) = // MAdapterOprTimeStamp
TimeStamp.LowPart;
}
NdisAcquireSpinLock(&(Mlid->MlidSpinLock));
if (Mlid->StatsTable->StatisticsOperational == FALSE) {
//
// Start the statistics timer
//
Mlid->StatsTable->StatisticsOperational = TRUE;
NdisInitializeTimer(&(Mlid->StatsTable->StatisticTimer),
NdisMlidStatisticTimer,
Mlid
);
NdisReleaseSpinLock(&(Mlid->MlidSpinLock));
NdisSetTimer(&(Mlid->StatsTable->StatisticTimer), 30000);
NdisAcquireSpinLock(&(Mlid->MlidSpinLock));
}
Mlid->NdisPacketFilterValue = NDIS_PACKET_TYPE_DIRECTED |
NDIS_PACKET_TYPE_MULTICAST |
NDIS_PACKET_TYPE_BROADCAST;
NdisReleaseSpinLock(&(Mlid->MlidSpinLock));
return(Mlid);
Fail5:
//
// Deregister with LSL
//
(*(Mlid->LSLFunctionList->SupportAPIArray[DeRegisterMLID_INDEX]))(
Mlid->ConfigTable.MLIDCFG_BoardNumber
);
Fail4:
//
// Free Receive packet pool
//
if (Mlid->ReceivePacketPool != (NDIS_HANDLE)NULL) {
NdisFreePacketPool(Mlid->ReceivePacketPool);
}
//
// Free Receive buffer pool
//
if (Mlid->ReceiveBufferPool != (NDIS_HANDLE)NULL) {
NdisFreeBufferPool(Mlid->ReceiveBufferPool);
}
//
// Free StatsTable
//
StatsTable->References--;
if (StatsTable->References == 0) {
ExFreePool(StatsTable);
}
Fail3:
//
// Close Adapter
//
NdisCloseAdapter(&NdisStatus, Mlid->NdisBindingHandle);
//
// If pend, wait
//
if (NdisStatus == NDIS_STATUS_PENDING) {
//
// Wait for close to complete
//
KeWaitForSingleObject(
&(Mlid->MlidRequestCompleteEvent),
Executive,
KernelMode,
TRUE,
(PTIME)NULL
);
KeResetEvent(&Mlid->MlidRequestCompleteEvent);
}
Fail2:
//
// Free Send packet pool
//
if (Mlid->SendPacketPool != (NDIS_HANDLE)NULL) {
NdisFreePacketPool(Mlid->SendPacketPool);
}
//
// Free Send buffer pool
//
if (Mlid->SendBufferPool != (NDIS_HANDLE)NULL) {
NdisFreeBufferPool(Mlid->SendBufferPool);
}
//
// Free NDIS_REQUEST
//
if (Mlid->UsingEvent) {
ExFreePool(NdisMlidRequest);
}
Fail1:
//
// Free Mlid structure
//
ExFreePool(Mlid);
return(NULL);
}