windows-nt/Source/XPSP1/NT/net/ndis/sys/ndis.c
2020-09-26 16:20:57 +08:00

3557 lines
99 KiB
C

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
ndis.c
Abstract:
NDIS wrapper functions
Author:
Adam Barr (adamba) 11-Jul-1990
Environment:
Kernel mode, FSD
Revision History:
10-Jul-1995 JameelH Make NDIS.SYS a device-driver and add PnP support
--*/
#include <precomp.h>
#pragma hdrstop
//
// Define the module number for debug code.
//
#define MODULE_NUMBER MODULE_NDIS
#define NDIS_DEVICE_NAME L"\\Device\\Ndis"
#define NDIS_SYMBOLIC_NAME L"\\Global??\\NDIS"
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
NDIS wrapper driver entry point.
Arguments:
DriverObject - Pointer to the driver object created by the system.
RegistryPath - Pointer to the registry section where the parameters reside.
Return Value:
Return value from IoCreateDevice
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING DeviceName;
UINT i;
OBJECT_ATTRIBUTES ObjectAttr;
UNICODE_STRING CallbackObjectName;
NTSTATUS NtStatus;
SYSTEM_BATTERY_STATE ndisSystemBatteryState;
HANDLE ThreadHandle;
BOOLEAN fDerefCallbackObject = FALSE, fDeregisterCallback = FALSE;
#define GET_TEXT_1(_T) #_T
#define GET_TEXT(_T) GET_TEXT_1(_T)
NdisInitializeString(&ndisBuildDate, __DATE__);
NdisInitializeString(&ndisBuildTime, __TIME__);
NdisInitializeString(&ndisBuiltBy, GET_TEXT(BUILT_BY));
ndisDriverObject = DriverObject;
//
// Create the device object.
//
RtlInitUnicodeString(&DeviceName, NDIS_DEVICE_NAME);
ndisNumberOfProcessors = KeNumberProcessors;
Status = IoCreateDevice(DriverObject, // DriverObject
0, // DeviceExtension
&DeviceName, // DeviceName
FILE_DEVICE_NETWORK, // DeviceType
FILE_DEVICE_SECURE_OPEN, // DeviceCharacteristics
FALSE, // Exclusive
&ndisDeviceObject); // DeviceObject
if (NT_SUCCESS(Status))
{
UNICODE_STRING SymbolicLinkName;
// Create a symbolic link to this device
RtlInitUnicodeString(&SymbolicLinkName, NDIS_SYMBOLIC_NAME);
Status = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
ndisDeviceObject->Flags |= DO_DIRECT_IO;
// Initialize the driver object for this file system driver.
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = ndisDispatchRequest;
}
//
// create a security descriptor for NDIS device object
//
Status = ndisCreateSecurityDescriptor(ndisDeviceObject,
&ndisSecurityDescriptor,
TRUE);
Status = CreateDeviceDriverSecurityDescriptor(DriverObject);
Status = CreateDeviceDriverSecurityDescriptor(DriverObject->DeviceObject);
Status = CreateDeviceDriverSecurityDescriptor(ndisDeviceObject);
//
// disable for now
//
#if NDIS_UNLOAD
DriverObject->DriverUnload = ndisUnload;
#else
DriverObject->DriverUnload = NULL;
#endif
INITIALIZE_SPIN_LOCK(&ndisGlobalLock);
INITIALIZE_SPIN_LOCK(&ndisMiniDriverListLock);
INITIALIZE_SPIN_LOCK(&ndisProtocolListLock);
INITIALIZE_SPIN_LOCK(&ndisMiniportListLock);
INITIALIZE_SPIN_LOCK(&ndisGlobalPacketPoolListLock);
INITIALIZE_SPIN_LOCK(&ndisGlobalOpenListLock);
ndisDmaAlignment = HalGetDmaAlignmentRequirement();
if (sizeof(ULONG) > ndisDmaAlignment)
{
ndisDmaAlignment = sizeof(ULONG);
}
ndisTimeIncrement = KeQueryTimeIncrement();
//
// Get handles for all conditionally lockable sections
//
for (i = 0; i < MAX_PKG; i++)
{
ndisInitializePackage(&ndisPkgs[i]);
}
ExInitializeResourceLite(&SharedMemoryResource);
ndisReadRegistry();
//
// don't let use set this bit through registry
//
ndisFlags &= ~NDIS_GFLAG_TRACK_MEM_ALLOCATION;
Status = STATUS_SUCCESS;
ndisSystemProcess = NtCurrentProcess();
//
// Now create a worker thread for use by NDIS
// This is so that when we queue PnP events upto transports
// and they need worker threads as well ...
//
KeInitializeQueue(&ndisWorkerQueue, 0);
Status = PsCreateSystemThread(&ThreadHandle,
THREAD_ALL_ACCESS,
NULL,
NtCurrentProcess(),
NULL,
ndisWorkerThread,
NULL);
if (!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ALL, DBG_LEVEL_ERR,
("NDIS DriverEntry: Cannot create worker thread, Status %lx\n", Status));
}
else
{
NtClose(ThreadHandle);
}
}
KeQuerySystemTime(&KeBootTime);
ConvertSecondsToTicks(POOL_AGING_TIME, &PoolAgingTicks);
//
// verifir intialization. in case ndis tester wants to verify
// the drivers by intercepting ndis entry points, ndis should
// not verify the calls
//
if (!(ndisFlags & NDIS_GFLAG_DONT_VERIFY))
ndisVerifierInitialization();
#if DBG
if (ndisDebugBreakPoint)
{
DbgPrint("Ndis: DriverEntry\n");
DbgBreakPoint();
}
#endif
#ifdef TRACK_MOPEN_REFCOUNTS
NdisZeroMemory (&ndisLogfile, sizeof(UINT) * NDIS_LOGFILE_SIZE);
#endif
#ifdef TRACK_MINIPORT_REFCOUNTS
NdisZeroMemory (&ndisMiniportLogfile, sizeof(UINT) * NDIS_MINIPORT_LOGFILE_SIZE);
#endif
//
// create a callback options for those kernel mode components that like
// to hear about Bind/Unbind events
//
RtlInitUnicodeString(&CallbackObjectName, NDIS_BIND_UNBIND_CALLBACK_NAME);
InitializeObjectAttributes(&ObjectAttr,
&CallbackObjectName,
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
NULL,
NULL);
NtStatus = ExCreateCallback(&ndisBindUnbindCallbackObject,
&ObjectAttr,
TRUE, // create
TRUE); // allow multiple callback registeration
if (!NT_SUCCESS(NtStatus))
{
DbgPrint("Ndis: failed to create a Callback object. Status %lx\n", NtStatus);
}
#if 0
else
{
//
// for test purpose
//
ndisBindUnbindCallbackRegisterationHandle = ExRegisterCallback(ndisBindUnbindCallbackObject,
ndisBindUnbindCallback,
(PVOID)NULL);
if (ndisBindUnbindCallbackRegisterationHandle == NULL)
{
DbgPrint("Ndis: failed to register a BindUnbind callback routine\n");
}
}
#endif
//
// register a notification callback for power state changes
//
RtlInitUnicodeString(&CallbackObjectName, L"\\CallBack\\PowerState");
InitializeObjectAttributes(&ObjectAttr,
&CallbackObjectName,
OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
NULL,
NULL);
NtStatus = ExCreateCallback(&ndisPowerStateCallbackObject,
&ObjectAttr,
FALSE,
TRUE);
if (!NT_SUCCESS(Status))
{
DbgPrint("Ndis: failed to create a Callback object. Status %lx\n", NtStatus);
}
else
{
fDerefCallbackObject = TRUE;
ndisPowerStateCallbackHandle = ExRegisterCallback(ndisPowerStateCallbackObject,
(PCALLBACK_FUNCTION)&ndisPowerStateCallback,
(PVOID)NULL);
if (ndisPowerStateCallbackHandle == NULL)
{
DbgPrint("Ndis: failed to register a power state Callback routine\n");
}
else
{
fDeregisterCallback = TRUE;
}
RtlZeroMemory(&ndisSystemBatteryState, sizeof(SYSTEM_BATTERY_STATE));
//
// get the current power source
//
NtStatus = ZwPowerInformation(SystemBatteryState,
NULL,
0,
&ndisSystemBatteryState,
sizeof(SYSTEM_BATTERY_STATE));
if (NT_SUCCESS(NtStatus))
{
ndisAcOnLine = (ndisSystemBatteryState.AcOnLine == TRUE) ? 1 : 0;
}
fDerefCallbackObject = FALSE;
fDeregisterCallback = FALSE;
}
InitializeListHead(&ndisGlobalPacketPoolList);
if (fDeregisterCallback)
{
ExUnregisterCallback(ndisPowerStateCallbackHandle);
}
if (fDerefCallbackObject)
{
ObDereferenceObject(ndisPowerStateCallbackObject);
}
INITIALIZE_MUTEX(&ndisPnPMutex);
return Status;
}
#if NDIS_UNLOAD
VOID
ndisUnload(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This is the unload routine for the Appletalk driver.
NOTE: Unload will not be called until all the handles have been
closed successfully. We just shutdown all the ports, and do
misc. cleanup.
Arguments:
DriverObject - Pointer to driver object for this driver.
Return Value:
None.
--*/
{
NTSTATUS Status;
UNICODE_STRING SymbolicLinkName;
UINT i;
NdisFreeString(ndisBuildDate);
NdisFreeString(ndisBuildTime);
NdisFreeString(ndisBuiltBy);
if (ndisPowerStateCallbackHandle)
{
ExUnregisterCallback(ndisPowerStateCallbackHandle);
}
if (ndisPowerStateCallbackObject)
{
ObDereferenceObject(ndisPowerStateCallbackObject);
}
ExDeleteResourceLite(&SharedMemoryResource);
//
// Tell the ndisWorkerThread to quit
//
INITIALIZE_WORK_ITEM(&ndisPoisonPill, NULL, &ndisPoisonPill);
QUEUE_WORK_ITEM(&ndisPoisonPill, CriticalWorkQueue);
WAIT_FOR_OBJECT(ndisThreadObject, 0);
ObDereferenceObject(ndisThreadObject);
RtlInitUnicodeString(&SymbolicLinkName, NDIS_SYMBOLIC_NAME);
Status = IoDeleteSymbolicLink(&SymbolicLinkName);
ASSERT(NT_SUCCESS(Status));
IoDeleteDevice(ndisDeviceObject);
//
// ASSERT that all the packages are unlocked
//
for (i = 0; i < MAX_PKG; i++)
{
ASSERT(ndisPkgs[i].ReferenceCount == 0);
}
}
#endif
VOID
ndisReadRegistry(
VOID
)
{
RTL_QUERY_REGISTRY_TABLE QueryTable[8];
UCHAR c;
ULONG DefaultZero = 0;
//
// First we need to initialize the processor information incase
// the registry is empty.
//
for (c = 0; c < ndisNumberOfProcessors; c++)
{
ndisValidProcessors[c] = c;
}
ndisCurrentProcessor = ndisMaximumProcessor = c - 1;
//
// 1) Switch to the MediaTypes key below the service (NDIS) key
//
QueryTable[0].QueryRoutine = NULL;
QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY;
QueryTable[0].Name = L"MediaTypes";
//
// Setup to enumerate the values in the registry section (shown above).
// For each such value, we'll add it to the ndisMediumArray
//
QueryTable[1].QueryRoutine = ndisAddMediaTypeToArray;
QueryTable[1].DefaultType = REG_DWORD;
QueryTable[1].DefaultData = (PVOID)&DefaultZero;
QueryTable[1].DefaultLength = 0;
QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_NOEXPAND;
QueryTable[1].Name = NULL;
//
// Query terminator
//
QueryTable[2].QueryRoutine = NULL;
QueryTable[2].Flags = 0;
QueryTable[2].Name = NULL;
//
// The rest of the work is done in the callback routine ndisAddMediaTypeToArray.
//
RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
L"NDIS",
QueryTable,
(PVOID)NULL, // no context needed
NULL);
//
// Switch to the parameters key below the service (NDIS) key and
// read the parameters.
//
QueryTable[0].QueryRoutine = NULL;
QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY;
QueryTable[0].Name = L"Parameters";
//
// Read in the processor affinity mask.
//
QueryTable[1].QueryRoutine = ndisReadProcessorAffinityMask;
QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_NOEXPAND;
QueryTable[1].DefaultData = (PVOID)&DefaultZero;
QueryTable[1].DefaultLength = 0;
QueryTable[1].DefaultType = REG_DWORD;
QueryTable[1].Name = L"ProcessorAffinityMask";
QueryTable[2].QueryRoutine = ndisReadRegParameters;
QueryTable[2].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
QueryTable[2].DefaultData = (PVOID)&ndisFlags;
QueryTable[2].DefaultLength = 0;
QueryTable[2].DefaultType = REG_DWORD;
QueryTable[2].Name = L"Flags";
QueryTable[2].EntryContext = (PVOID)&ndisFlags;
QueryTable[3].QueryRoutine = ndisReadRegParameters;
QueryTable[3].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
QueryTable[3].DefaultData = (PVOID)&ndisPacketStackSize;
QueryTable[3].DefaultLength = 0;
QueryTable[3].DefaultType = REG_DWORD;
QueryTable[3].Name = L"PacketStackSize";
QueryTable[3].EntryContext = (PVOID)&ndisPacketStackSize;
//
// Query terminator
//
QueryTable[4].QueryRoutine = NULL;
QueryTable[4].Flags = 0;
QueryTable[4].Name = NULL;
#if DBG
#ifdef NDIS_TRACE
ndisDebugBreakPoint = 1;
ndisDebugLevel = 0;
ndisDebugSystems = 0x3003;
#else
QueryTable[4].QueryRoutine = ndisReadRegParameters;
QueryTable[4].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
QueryTable[4].Name = L"DebugBreakPoint";
QueryTable[4].DefaultData = (PVOID)&ndisDebugBreakPoint;
QueryTable[4].DefaultLength = 0;
QueryTable[4].EntryContext = (PVOID)&ndisDebugBreakPoint;
QueryTable[4].DefaultType = REG_DWORD;
QueryTable[5].QueryRoutine = ndisReadRegParameters;
QueryTable[5].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
QueryTable[5].Name = L"DebugLevel";
QueryTable[5].DefaultData = (PVOID)&ndisDebugLevel;
QueryTable[5].DefaultLength = 0;
QueryTable[5].EntryContext = (PVOID)&ndisDebugLevel;
QueryTable[5].DefaultType = REG_DWORD;
QueryTable[6].QueryRoutine = ndisReadRegParameters;
QueryTable[6].Flags = RTL_QUERY_REGISTRY_NOEXPAND;
QueryTable[6].Name = L"DebugSystems";
QueryTable[6].DefaultData = (PVOID)&ndisDebugSystems;
QueryTable[6].DefaultLength = 0;
QueryTable[6].EntryContext = (PVOID)&ndisDebugSystems;
QueryTable[6].DefaultType = REG_DWORD;
//
// Query terminator
//
QueryTable[7].QueryRoutine = NULL;
QueryTable[7].Flags = 0;
QueryTable[7].Name = NULL;
#endif
#endif
//
// The rest of the work is done in the callback routines
//
RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
L"NDIS",
QueryTable,
(PVOID)NULL, // no context needed
NULL);
//
// Make sure ndisPacketStackSize isn't zero
//
if (ndisPacketStackSize == 0)
ndisPacketStackSize = 1;
}
NTSTATUS
ndisReadRegParameters(
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
)
/*++
Arguments:
ValueName - The name of the value
ValueType - The type of the value (REG_MULTI_SZ -- ignored).
ValueData - The null-terminated data for the value.
ValueLength - The length of ValueData.
Context - Unused.
EntryContext - A pointer to the pointer that holds the copied data.
Return Value:
STATUS_SUCCESS
--*/
{
UNREFERENCED_PARAMETER(ValueName);
UNREFERENCED_PARAMETER(ValueLength);
UNREFERENCED_PARAMETER(Context);
if ((ValueType != REG_DWORD) || (ValueData == NULL))
return STATUS_UNSUCCESSFUL;
*((PULONG)EntryContext) = *((PULONG)ValueData);
return STATUS_SUCCESS;
}
NTSTATUS
ndisReadProcessorAffinityMask(
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
//
// If we have valid data then build our array of valid processors
// to use.... Treat the special case of 0 or default -1 to signify
// that DPC affinity will follow interrupt affinity
//
if ((REG_DWORD == ValueType) && (ValueData != NULL))
{
if ((*(PULONG)ValueData == 0) ||
(*(PULONG)ValueData == 0xFFFFFFFF))
{
ndisSkipProcessorAffinity = TRUE;
}
else
{
ULONG ProcessorAffinity;
UCHAR c1, c2;
//
// Save the processor affinity.
//
ProcessorAffinity = *(PULONG)ValueData;
//
// Fill in the valid processor array.
//
for (c1 = c2 = 0;
(c1 <= ndisMaximumProcessor) && (ProcessorAffinity != 0);
c1++)
{
if (ProcessorAffinity & 1)
{
ndisValidProcessors[c2++] = c1;
}
ProcessorAffinity >>= 1;
}
ndisCurrentProcessor = ndisMaximumProcessor = c2 - 1;
}
}
return STATUS_SUCCESS;
}
NTSTATUS
ndisAddMediaTypeToArray(
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
)
{
#if DBG
NDIS_STRING Str;
RtlInitUnicodeString(&Str, ValueName);
#endif
DBGPRINT(DBG_COMP_ALL, DBG_LEVEL_INFO,
("ExperimentalMediaType %Z - %x\n", &Str, *(PULONG)ValueData));
//
// Ignore all values that we already know about. These should not be in the
// registry anyway, but just in case somebody is messing with it.
//
if ((ValueType == REG_DWORD) && (ValueData != NULL) && (*(PULONG)ValueData > NdisMediumIrda))
{
NDIS_MEDIUM *pTemp;
ULONG size;
//
// See if we have enough space to add this value. If not allocate space for the
// new array, copy the old one into this (and free the old if not static).
//
ASSERT (ndisMediumArraySize <= ndisMediumArrayMaxSize);
//
// Check for duplicates. If so drop it
//
for (pTemp = ndisMediumArray, size = ndisMediumArraySize;
size > 0; pTemp ++, size -= sizeof(NDIS_MEDIUM))
{
if (*(NDIS_MEDIUM *)ValueData == *pTemp)
{
//
// Duplicate.
//
return STATUS_SUCCESS;
}
}
if (ndisMediumArraySize == ndisMediumArrayMaxSize)
{
//
// We do not have any space in the array. Need to re-alloc. Be generous.
//
pTemp = (NDIS_MEDIUM *)ALLOC_FROM_POOL(ndisMediumArraySize + EXPERIMENTAL_SIZE*sizeof(NDIS_MEDIUM),
NDIS_TAG_MEDIA_TYPE_ARRAY);
if (pTemp != NULL)
{
CopyMemory(pTemp, ndisMediumArray, ndisMediumArraySize);
if (ndisMediumArray != ndisMediumBuffer)
{
FREE_POOL(ndisMediumArray);
}
ndisMediumArray = pTemp;
}
}
if (ndisMediumArraySize < ndisMediumArrayMaxSize)
{
ndisMediumArray[ndisMediumArraySize/sizeof(NDIS_MEDIUM)] = *(NDIS_MEDIUM *)ValueData;
ndisMediumArraySize += sizeof(NDIS_MEDIUM);
}
}
return STATUS_SUCCESS;
}
WORK_QUEUE_ITEM LastWorkerThreadWI = {0} ;
VOID
ndisWorkerThread(
IN PVOID Context
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
BOOLEAN FirstThread = (Context == NULL);
PLIST_ENTRY pList;
HANDLE ThreadHandle;
PWORK_QUEUE_ITEM pWI;
NTSTATUS Status;
if (FirstThread)
{
ndisThreadObject = PsGetCurrentThread();
ObReferenceObject(ndisThreadObject);
do
{
//
// Block here waiting for work-items to do
//
pList = KeRemoveQueue(&ndisWorkerQueue, KernelMode, NULL);
DBGPRINT_RAW(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO,
("ndisWorkerThread: WorkItem %p\n", pList));
pWI = CONTAINING_RECORD(pList, WORK_QUEUE_ITEM, List);
#if NDIS_UNLOAD
//
// Unload asking us to quit, comply.
//
if (pWI == &ndisPoisonPill)
{
break;
}
#endif
Status = PsCreateSystemThread(&ThreadHandle,
THREAD_ALL_ACCESS,
NULL,
NtCurrentProcess(),
NULL,
ndisWorkerThread,
pWI);
if (NT_SUCCESS(Status))
{
NtClose(ThreadHandle);
}
else
{
DBGPRINT_RAW(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO,
("ndisWorkerThread: Failed to create a thread, using EX worker thread\n"));
XQUEUE_WORK_ITEM(pWI, CriticalWorkQueue);
ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
}
} while (TRUE);
}
else
{
//
// Not the main thread, just do the thing and die.
//
LastWorkerThreadWI = *((PWORK_QUEUE_ITEM)Context);
pWI = (PWORK_QUEUE_ITEM)Context;
(*pWI->WorkerRoutine)(pWI->Parameter);
ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
}
}
NTSTATUS
ndisDispatchRequest(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
/*++
Routine Description:
Dispatcher for Irps intended for the NDIS Device.
Arguments:
Return Value:
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PIO_STACK_LOCATION pIrpSp;
PNDIS_DEVICE_OBJECT_OPEN_CONTEXT OpenContext = NULL;
NTSTATUS SecurityStatus;
static LONG OpenCount = 0;
pDeviceObject; // prevent compiler warnings
PAGED_CODE( );
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pIrp->IoStatus.Status = STATUS_PENDING;
pIrp->IoStatus.Information = 0;
PnPReferencePackage();
switch (pIrpSp->MajorFunction)
{
case IRP_MJ_CREATE:
OpenContext = (PNDIS_DEVICE_OBJECT_OPEN_CONTEXT)ALLOC_FROM_POOL(sizeof(NDIS_DEVICE_OBJECT_OPEN_CONTEXT),
NDIS_TAG_OPEN_CONTEXT);
if (OpenContext == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
ZeroMemory(OpenContext, sizeof(NDIS_DEVICE_OBJECT_OPEN_CONTEXT));
OpenContext->AdminAccessAllowed = ndisCheckAccess(pIrp,
pIrpSp,
&SecurityStatus,
ndisSecurityDescriptor);
//
// save the caller's access right
//
pIrpSp->FileObject->FsContext = OpenContext;
Increment(&OpenCount, &Lock);
break;
case IRP_MJ_CLEANUP:
OpenContext = pIrpSp->FileObject->FsContext;
ASSERT(OpenContext != NULL);
pIrpSp->FileObject->FsContext = NULL;
FREE_POOL(OpenContext);
Decrement(&OpenCount, &Lock);
break;
case IRP_MJ_CLOSE:
break;
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
break;
case IRP_MJ_DEVICE_CONTROL:
Status = ndisHandlePnPRequest(pIrp);
break;
default:
Status = STATUS_NOT_IMPLEMENTED;
break;
}
ASSERT (CURRENT_IRQL < DISPATCH_LEVEL);
ASSERT (Status != STATUS_PENDING);
pIrp->IoStatus.Status = Status;
IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
PnPDereferencePackage();
return Status;
}
NTSTATUS
ndispConvOffsetToPointer(
IN PVOID MasterBuffer,
IN ULONG MasterLength,
IN OUT PULONG_PTR Offset,
IN ULONG Length,
IN ULONG Alignment
)
/*++
Routine Description:
This function validates a buffer within an IOCTL and converts a buffer
offset to a pointer.
Argumens:
MasterBuffer - Pointer to the start of the IOCTL buffer
MasterLength - Length of the IOCTL buffer
Offset - Offset of the data buffer within the IOCTL buffer
Length - Length of the data buffer within the IOCTL buffer
Alignment - Required alignment of the type within the data buffer
Return Value:
The function status is the final status of the operation.
--*/
{
ULONG_PTR masterStart;
ULONG_PTR masterEnd;
ULONG_PTR bufStart;
ULONG_PTR bufEnd;
if (Length == 0)
{
//
// Nothing to do.
//
return STATUS_SUCCESS;
}
masterStart = (ULONG_PTR)MasterBuffer;
masterEnd = masterStart + MasterLength;
bufStart = masterStart + *Offset;
bufEnd = bufStart + Length;
//
// Ensure that neither of the buffers wrap
//
if (masterEnd < masterStart || bufEnd < bufStart)
{
return STATUS_INVALID_PARAMETER;
}
//
// Ensure that buf is wholly contained within master
//
if (bufStart < masterStart || bufEnd > masterEnd)
{
return STATUS_INVALID_PARAMETER;
}
//
// Make sure that buf is properly aligned
//
if ((bufStart & (Alignment - 1)) != 0)
{
return STATUS_INVALID_PARAMETER;
}
//
// Everything looks good, perform the conversion
//
*Offset += masterStart;
return STATUS_SUCCESS;
}
NTSTATUS
ndispConvVar(
IN PVOID MasterBuffer,
IN ULONG MasterLength,
IN OUT PNDIS_VAR_DATA_DESC Var
)
/*++
Routine Description:
This function validates an NDIS_VAR_DATA_DESC buffer within an IOCTL
and converts its data offset to a pointer.
Argumens:
MasterBuffer - Pointer to the start of the IOCTL buffer
MasterLength - Length of the IOCTL buffer
Var - Pointer to an NDIS_VAR_DATA_DESC structure.
Return Value:
The function status is the final status of the operation.
--*/
{
return ndispConvOffsetToPointer( MasterBuffer,
MasterLength,
&Var->Offset,
Var->Length,
sizeof(WCHAR) );
}
NTSTATUS
FASTCALL
ndisHandlePnPRequest(
IN PIRP pIrp
)
/*++
Routine Description:
Handler for PnP ioctls.
Arguments:
Return Value:
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PNDIS_PNP_OPERATION PnPOp;
PNDIS_ENUM_INTF EnumIntf;
PIO_STACK_LOCATION pIrpSp;
UNICODE_STRING Device;
ULONG Method;
PVOID pBuf;
UINT iBufLen, oBufLen;
UINT AmtCopied;
#if defined(_WIN64)
PUCHAR pThunkBuf = NULL; // in case we thunk from 32-bit
#endif
PNDIS_DEVICE_OBJECT_OPEN_CONTEXT OpenContext;
BOOLEAN AdminAccessAllowed = FALSE;
PAGED_CODE( );
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
OpenContext = pIrpSp->FileObject->FsContext;
if (OpenContext == NULL)
{
return STATUS_NO_SUCH_FILE;
}
AdminAccessAllowed = OpenContext->AdminAccessAllowed;
Method = pIrpSp->Parameters.DeviceIoControl.IoControlCode & 3;
// Ensure that the method is buffered - we always use that.
if (Method == METHOD_BUFFERED)
{
// Get the output buffer and its length. Input and Output buffers are
// both pointed to by the SystemBuffer
iBufLen = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
oBufLen = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
pBuf = pIrp->AssociatedIrp.SystemBuffer;
}
else
{
return STATUS_INVALID_PARAMETER;
}
switch (pIrpSp->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_NDIS_ADD_TDI_DEVICE:
if (!AdminAccessAllowed)
{
return STATUS_ACCESS_DENIED;
}
//
// Validate the DeviceName
//
Status = STATUS_INVALID_PARAMETER;
if ((iBufLen > 0) && ((iBufLen % sizeof(WCHAR)) == 0))
{
((PWCHAR)pBuf)[iBufLen/sizeof(WCHAR) - 1] = 0;
RtlInitUnicodeString(&Device, pBuf);
Status = ndisHandleLegacyTransport(&Device);
}
break;
case IOCTL_NDIS_DO_PNP_OPERATION:
if (!AdminAccessAllowed)
{
return STATUS_ACCESS_DENIED;
}
Status = STATUS_BUFFER_TOO_SMALL;
PnPOp = (PNDIS_PNP_OPERATION)pBuf;
#if defined(_WIN64)
if (IoIs32bitProcess(pIrp))
{
PNDIS_PNP_OPERATION32 PnPOp32;
PUCHAR ThunkPtr;
PnPOp32 = (PNDIS_PNP_OPERATION32)pBuf;
//
// Validate structure based on its 32-bit definition
//
if ((iBufLen < sizeof(NDIS_PNP_OPERATION32)) ||
(iBufLen < sizeof(NDIS_PNP_OPERATION32) +
PnPOp32->LowerComponent.MaximumLength +
PnPOp32->UpperComponent.MaximumLength))
{
break;
}
pThunkBuf = ALLOC_FROM_POOL(sizeof(NDIS_PNP_OPERATION) +
PnPOp32->LowerComponent.MaximumLength +
PnPOp32->UpperComponent.MaximumLength, NDIS_TAG_DEFAULT);
if (pThunkBuf == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
PnPOp = (PNDIS_PNP_OPERATION)pThunkBuf;
PnPOp->Layer = PnPOp32->Layer;
PnPOp->Operation = PnPOp32->Operation;
ThunkPtr = (PUCHAR)PnPOp + sizeof(NDIS_PNP_OPERATION);
//
// LowerComponent:
//
PnPOp->LowerComponent.MaximumLength = PnPOp32->LowerComponent.MaximumLength;
PnPOp->LowerComponent.Length = PnPOp32->LowerComponent.Length;
PnPOp->LowerComponent.Offset = ThunkPtr - (PUCHAR)&PnPOp->LowerComponent;
NdisMoveMemory((PUCHAR)ThunkPtr, (PUCHAR)&PnPOp32->LowerComponent + PnPOp32->LowerComponent.Offset, PnPOp32->LowerComponent.MaximumLength);
ThunkPtr += PnPOp->LowerComponent.MaximumLength;
//
// UpperComponent:
//
PnPOp->UpperComponent.MaximumLength = PnPOp32->UpperComponent.MaximumLength;
PnPOp->UpperComponent.Length = PnPOp32->UpperComponent.Length;
PnPOp->UpperComponent.Offset = ThunkPtr - (PUCHAR)&PnPOp->UpperComponent;
NdisMoveMemory((PUCHAR)ThunkPtr, (PUCHAR)&PnPOp32->UpperComponent + PnPOp32->UpperComponent.Offset, PnPOp32->UpperComponent.MaximumLength);
ThunkPtr += PnPOp->UpperComponent.MaximumLength;
//
// BindList:
//
PnPOp->BindList.MaximumLength = PnPOp32->BindList.MaximumLength;
PnPOp->BindList.Length = PnPOp32->BindList.Length;
PnPOp->BindList.Offset = ThunkPtr - (PUCHAR)&PnPOp->BindList;
NdisMoveMemory((PUCHAR)ThunkPtr, (PUCHAR)&PnPOp32->BindList + PnPOp32->BindList.Offset, PnPOp32->BindList.MaximumLength);
ThunkPtr += PnPOp->BindList.MaximumLength;
}
else
{
//
// Not a 32-bit process on Win64
//
if ((iBufLen < sizeof(NDIS_PNP_OPERATION)) ||
(iBufLen < (sizeof(NDIS_PNP_OPERATION) +
PnPOp->LowerComponent.MaximumLength +
PnPOp->UpperComponent.MaximumLength)))
{
break;
}
}
#else
if ((iBufLen < sizeof(NDIS_PNP_OPERATION)) ||
(iBufLen < (sizeof(NDIS_PNP_OPERATION) +
PnPOp->LowerComponent.MaximumLength +
PnPOp->UpperComponent.MaximumLength)))
{
break;
}
#endif // _WIN64
//
// Convert the four buffer offsets within NDIS_PNP_OPERATION
// to pointers.
//
Status = ndispConvVar( PnPOp, iBufLen, &PnPOp->LowerComponent );
if (!NT_SUCCESS(Status))
{
break;
}
Status = ndispConvVar( PnPOp, iBufLen, &PnPOp->UpperComponent );
if (!NT_SUCCESS(Status))
{
break;
}
Status = ndispConvVar( PnPOp, iBufLen, &PnPOp->BindList );
if (!NT_SUCCESS(Status))
{
break;
}
Status = ndispConvOffsetToPointer( PnPOp,
iBufLen,
&PnPOp->ReConfigBufferOff,
PnPOp->ReConfigBufferSize,
sizeof(ULONG_PTR) );
if (!NT_SUCCESS(Status))
{
break;
}
Status = ndisHandleUModePnPOp(PnPOp);
break;
case IOCTL_NDIS_ENUMERATE_INTERFACES:
#if defined(_WIN64)
if (IoIs32bitProcess(pIrp))
{
if (oBufLen >= sizeof(NDIS_ENUM_INTF32))
{
Status = ndisEnumerateInterfaces32(pBuf, oBufLen);
pIrp->IoStatus.Information = oBufLen;
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
break;
}
#endif
if (oBufLen >= sizeof(NDIS_ENUM_INTF))
{
EnumIntf = (PNDIS_ENUM_INTF)pBuf;
Status = ndisEnumerateInterfaces(pBuf, oBufLen);
pIrp->IoStatus.Information = oBufLen;
}
else Status = STATUS_BUFFER_TOO_SMALL;
break;
case IOCTL_NDIS_GET_VERSION:
if (oBufLen < sizeof(UINT))
{
Status = STATUS_BUFFER_TOO_SMALL;
}
else
{
*((PUINT)pBuf) = NdisGetVersion();
if (oBufLen >= 2 * sizeof(UINT))
{
*((PUINT)pBuf + 1) = (UINT)ndisChecked;
}
Status = STATUS_SUCCESS;
}
break;
default:
break;
}
ASSERT (CURRENT_IRQL < DISPATCH_LEVEL);
#if defined(_WIN64)
if (pThunkBuf)
{
FREE_POOL(pThunkBuf);
}
#endif
return Status;
}
NTSTATUS
FASTCALL
ndisHandleUModePnPOp(
IN PNDIS_PNP_OPERATION PnPOp
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS Status;
PUNICODE_STRING Protocol, Device, BindList;
WAIT_FOR_OBJECT(&ndisPnPMutex, NULL);
ndisPnPMutexOwner = MODULE_NUMBER + __LINE__;
//
// Upcase the protocol and device names
//
Protocol = (PUNICODE_STRING)&PnPOp->UpperComponent;
Device = (PUNICODE_STRING)&PnPOp->LowerComponent;
BindList = (PUNICODE_STRING)&PnPOp->BindList;
if (PnPOp->Operation == BIND)
{
DBGPRINT(DBG_COMP_ALL, DBG_LEVEL_ERR,
("BIND (%s) %Z to %Z\n", (PnPOp->Layer == NDIS) ? "NDIS" : "TDI ", Protocol, Device));
}
else if (PnPOp->Operation == UNBIND)
{
DBGPRINT(DBG_COMP_ALL, DBG_LEVEL_ERR,
("UNBIND(%s) %Z to %Z\n", (PnPOp->Layer == NDIS) ? "NDIS" : "TDI ", Protocol, Device));
}
switch (PnPOp->Layer)
{
case TDI:
//
// Call into the TDI handler to do this
//
if (ndisTdiPnPHandler != NULL)
{
Status = (*ndisTdiPnPHandler)(Protocol,
Device,
BindList,
PnPOp->ReConfigBufferPtr,
PnPOp->ReConfigBufferSize,
PnPOp->Operation);
}
else
{
Status = STATUS_UNSUCCESSFUL;
}
break;
case NDIS:
switch (PnPOp->Operation)
{
case BIND:
Status = ndisHandleProtocolBindNotification(Device, Protocol);
break;
case UNBIND:
Status = ndisHandleProtocolUnbindNotification(Device, Protocol);
break;
case RECONFIGURE:
case BIND_LIST:
Status = ndisHandleProtocolReconfigNotification(Device,
Protocol,
PnPOp->ReConfigBufferPtr,
PnPOp->ReConfigBufferSize,
PnPOp->Operation);
break;
case UNLOAD:
Status = ndisHandleProtocolUnloadNotification(Protocol);
break;
case REMOVE_DEVICE:
Status = ndisHandleOrphanDevice(Device);
break;
default:
Status = STATUS_INVALID_PARAMETER;
break;
}
break;
default:
Status = STATUS_INVALID_PARAMETER;
break;
}
ndisPnPMutexOwner = 0;
RELEASE_MUTEX(&ndisPnPMutex);
return Status;
}
NTSTATUS
FASTCALL
ndisHandleProtocolBindNotification(
IN PUNICODE_STRING DeviceName,
IN PUNICODE_STRING ProtocolName
)
/*++
Routine Description:
Given a erotocol's name and an adapter's name, this routine creates a binding between
a protocol and an adapter (assuming protocol has a BindAdapterHandler)
Arguments:
DeviceName: Adapter device name i.e. \Device\{GUID}
ProtocolName Protocols name i.e. TCPIP
Return Value:
STATUS_SUCCESS if we could call BindAdapterHandler
STATUS_UNSUCCESSFUL otherwise
Note
This routine does not return the status of attempted bind, rather if it -could- attempt to bind!
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PNDIS_PROTOCOL_BLOCK Protocol = NULL;
PNDIS_MINIPORT_BLOCK Miniport = NULL;
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("==>ndisHandleProtocolBindNotification\n"));
do
{
ndisReferenceMiniportByName(DeviceName, &Miniport);
if (Miniport == NULL)
{
Status = STATUS_OBJECT_NAME_NOT_FOUND;
break;
}
//
// Map ProtocolName to the Protocol block
//
Status = ndisReferenceProtocolByName(ProtocolName, &Protocol, FALSE);
if (!NT_SUCCESS(Status))
{
Protocol = NULL;
Status = STATUS_SUCCESS;
break;
}
//
// Bind this protocols
//
ndisCheckAdapterBindings(Miniport, Protocol);
} while (FALSE);
if (Protocol != NULL)
{
ndisDereferenceProtocol(Protocol);
}
if (Miniport != NULL)
{
MINIPORT_DECREMENT_REF(Miniport);
}
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("<==ndisHandleProtocolBindNotification\n"));
return Status;
}
NTSTATUS
FASTCALL
ndisHandleProtocolUnbindNotification(
IN PUNICODE_STRING DeviceName,
IN PUNICODE_STRING ProtocolName
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS Status;
PNDIS_OPEN_BLOCK Open;
PNDIS_PROTOCOL_BLOCK Protocol = NULL;
PNDIS_MINIPORT_BLOCK Miniport = NULL;
BOOLEAN fPartial = FALSE;
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("==>ndisHandleProtocolUnbindNotification\n"));
do
{
//
// Map ProtocolName to the Protocol block
//
Status = ndisReferenceProtocolByName(ProtocolName, &Protocol, FALSE);
if (!NT_SUCCESS(Status))
{
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("ndisHandleProtocolUnbindNotification: ndisReferenceProtocolByName failed %lx\n", Status));
Status = STATUS_SUCCESS;
Protocol = NULL;
break;
}
do
{
Open = ndisMapOpenByName(DeviceName, Protocol, TRUE, TRUE);
if (Open == NULL)
{
//
// There is no -active- binding between this adapter and protocol.
// This would normally be an error but we need one special case for
// TCP/IP Arp modules. We can unbind notifications for TCP/IP which
// are actually destined for the ARP module.
// We also know that either TCP/IP or ONE and ONLY ONE arp module can be
// bound to an adapter. Make use of that knowledge.
//
ndisDereferenceProtocol(Protocol);
if (!fPartial)
{
fPartial = TRUE;
Protocol = NULL;
}
Status = ndisReferenceProtocolByName(ProtocolName, &Protocol, TRUE);
if (!NT_SUCCESS(Status))
{
break;
}
}
} while (Open == NULL);
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("ndisHandleProtocolUnbindNotification: Open %p\n", Open));
if (Open != NULL)
{
Miniport = Open->MiniportHandle;
Status = ndisUnbindProtocol(Open, TRUE);
if (Status != NDIS_STATUS_SUCCESS)
{
KIRQL OldIrql;
PNDIS_OPEN_BLOCK tmpOpen;
//
// check to see if the open is still there and if it is
// clear the UNBIND flag. Note that we were the one
// setting the flag, so we can clear it ourselves
//
ACQUIRE_SPIN_LOCK(&Protocol->Ref.SpinLock, &OldIrql);
for (tmpOpen = Protocol->OpenQueue;
tmpOpen != NULL;
tmpOpen = tmpOpen->ProtocolNextOpen)
{
if(tmpOpen == Open)
{
ACQUIRE_SPIN_LOCK_DPC(&Open->SpinLock);
MINIPORT_CLEAR_FLAG(Open, fMINIPORT_OPEN_UNBINDING |
fMINIPORT_OPEN_DONT_FREE |
fMINIPORT_OPEN_PROCESSING);
RELEASE_SPIN_LOCK_DPC(&Open->SpinLock);
break;
}
}
RELEASE_SPIN_LOCK(&Protocol->Ref.SpinLock, OldIrql);
}
}
} while (FALSE);
if (Miniport != NULL)
{
MINIPORT_DECREMENT_REF(Miniport);
}
if (Protocol != NULL)
{
ndisDereferenceProtocol(Protocol);
}
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("<==ndisHandleProtocolUnbindNotification: Protocol %p, Status %lx\n", Protocol, Status));
return Status;
}
NTSTATUS
ndisHandleProtocolReconfigNotification(
IN PUNICODE_STRING DeviceName,
IN PUNICODE_STRING ProtocolName,
IN PVOID ReConfigBuffer,
IN UINT ReConfigBufferSize,
IN UINT Operation
)
/*++
Routine Description:
This routine will notify protocols of a cahnge in their configuration -or-
their bind list
Arguments:
DeviceName: Adapter's name (if specified). if NULL, it means the change is global and not bind specific
ProtocolName: Protocol's name
ReConfigBuffer: information buffer
ReConfigBufferSize: Information buffer size
Operation: RECONFIGURE or BIND_LIST
Return Value:
--*/
{
NTSTATUS Status;
KIRQL OldIrql;
PNDIS_PROTOCOL_BLOCK Protocol = NULL;
PNDIS_OPEN_BLOCK Open = NULL;
NET_PNP_EVENT NetPnpEvent;
PNDIS_PNP_EVENT_RESERVED EventReserved;
KEVENT Event;
BOOLEAN fPartial = FALSE;
DBGPRINT_RAW(DBG_COMP_PROTOCOL, DBG_LEVEL_INFO,
("==>ndisHandleProtocolReconfigNotification\n"));
do
{
//
// Map ProtocolName to the Protocol block
//
Status = ndisReferenceProtocolByName(ProtocolName, &Protocol, FALSE);
if (!NT_SUCCESS(Status))
{
Protocol = NULL;
break;
}
//
// We can be passed a NULL device-name which implies global reconfig and we call
// the protocol's event handler with a NULL BindingContext
//
if (DeviceName->Length != 0)
{
ASSERT(Operation == RECONFIGURE);
do
{
WAIT_FOR_PROTO_MUTEX(Protocol);
Open = ndisMapOpenByName(DeviceName, Protocol, FALSE, FALSE);
if (Open == NULL)
{
RELEASE_PROT_MUTEX(Protocol);
//
// There is no -active- binding between this adapter and protocol.
// This would normally be an error but we need one special case for
// TCP/IP Arp modules. We can unbind notifications for TCP/IP which
// are actually destined for the ARP module.
// We also know that either TCP/IP or ONE and ONLY ONE arp module can be
// bound to an adapter. Make use of that knowledge.
//
ndisDereferenceProtocol(Protocol);
if (!fPartial)
{
fPartial = TRUE;
Protocol = NULL;
}
Status = ndisReferenceProtocolByName(ProtocolName, &Protocol, TRUE);
if (!NT_SUCCESS(Status))
{
break;
}
}
} while (Open == NULL);
if (Open == NULL)
{
//
// if Open == NULL we are not holding the protocol mutex
//
Status = STATUS_OBJECT_NAME_NOT_FOUND;
break;
}
else if (Protocol->ProtocolCharacteristics.PnPEventHandler == NULL)
{
//
// Open is not NULL, we -are- holding the protocol mutex. release
// it before breaking out
//
RELEASE_PROT_MUTEX(Protocol);
Status = STATUS_UNSUCCESSFUL;
break;
}
}
else
{
//
// the device is NULL, just grab the protocol mutex
//
if (Protocol->ProtocolCharacteristics.PnPEventHandler != NULL)
{
WAIT_FOR_PROTO_MUTEX(Protocol);
}
else
{
Status = STATUS_UNSUCCESSFUL;
break;
}
}
//
// Setup the PnPEvent buffer
//
NdisZeroMemory(&NetPnpEvent, sizeof(NetPnpEvent));
switch (Operation)
{
case RECONFIGURE:
NetPnpEvent.NetEvent = NetEventReconfigure;
break;
case BIND_LIST:
NetPnpEvent.NetEvent = NetEventBindList;
break;
default:
ASSERT(FALSE);
break;
}
NetPnpEvent.Buffer = ReConfigBuffer;
NetPnpEvent.BufferLength = ReConfigBufferSize;
//
// Get a pointer to the NDIS reserved are in the PnP event.
//
EventReserved = PNDIS_PNP_EVENT_RESERVED_FROM_NET_PNP_EVENT(&NetPnpEvent);
INITIALIZE_EVENT(&Event);
EventReserved->pEvent = &Event;
//
// Notify the protocol now
//
Status = (Protocol->ProtocolCharacteristics.PnPEventHandler)(
(Open != NULL) ? Open->ProtocolBindingContext : NULL,
&NetPnpEvent);
if (NDIS_STATUS_PENDING == Status)
{
//
// Wait for completion.
//
WAIT_FOR_PROTOCOL(Protocol, &Event);
//
// Get the completion status.
//
Status = EventReserved->Status;
}
if (Open)
{
ACQUIRE_SPIN_LOCK(&Open->SpinLock, &OldIrql);
MINIPORT_CLEAR_FLAG(Open, fMINIPORT_OPEN_PROCESSING);
RELEASE_SPIN_LOCK(&Open->SpinLock, OldIrql);
}
RELEASE_PROT_MUTEX(Protocol);
} while (FALSE);
if (Protocol != NULL)
{
ndisDereferenceProtocol(Protocol);
}
DBGPRINT_RAW(DBG_COMP_PROTOCOL, DBG_LEVEL_INFO,
("<==ndisHandleProtocolReconfigNotification\n"));
return Status;
}
NTSTATUS
FASTCALL
ndisHandleProtocolUnloadNotification(
IN PUNICODE_STRING ProtocolName
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS Status;
PNDIS_PROTOCOL_BLOCK Protocol = NULL;
DBGPRINT_RAW(DBG_COMP_PROTOCOL, DBG_LEVEL_INFO,
("==>ndisHandleProtocolUnloadNotification\n"));
//
// Map ProtocolName to the Protocol block
//
Status = ndisReferenceProtocolByName(ProtocolName, &Protocol, FALSE);
if (NT_SUCCESS(Status))
{
ndisDereferenceProtocol(Protocol);
if (Protocol->ProtocolCharacteristics.UnloadHandler != NULL)
{
(Protocol->ProtocolCharacteristics.UnloadHandler)();
}
else
{
Status = STATUS_UNSUCCESSFUL;
}
}
DBGPRINT_RAW(DBG_COMP_PROTOCOL, DBG_LEVEL_INFO,
("<==ndisHandleProtocolUnloadNotification\n"));
return Status;
}
NTSTATUS
FASTCALL
ndisHandleOrphanDevice(
IN PUNICODE_STRING pDevice
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS Status;
KIRQL OldIrql;
BOOLEAN fFound = FALSE;
PNDIS_M_DRIVER_BLOCK MiniBlock;
PNDIS_MINIPORT_BLOCK Miniport = NULL;
UNICODE_STRING UpcaseDevice;
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO,
("==>ndisHandleOrphanDevice\n"));
UpcaseDevice.Length = pDevice->Length;
UpcaseDevice.MaximumLength = pDevice->Length + sizeof(WCHAR);
UpcaseDevice.Buffer = ALLOC_FROM_POOL(UpcaseDevice.MaximumLength, NDIS_TAG_STRING);
if (UpcaseDevice.Buffer == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = RtlUpcaseUnicodeString(&UpcaseDevice, pDevice, FALSE);
ASSERT (NT_SUCCESS(Status));
ACQUIRE_SPIN_LOCK(&ndisMiniDriverListLock, &OldIrql);
for (MiniBlock = ndisMiniDriverList;
(MiniBlock != NULL) && !fFound;
MiniBlock = MiniBlock->NextDriver)
{
ACQUIRE_SPIN_LOCK_DPC(&MiniBlock->Ref.SpinLock);
for (Miniport = MiniBlock->MiniportQueue;
Miniport != NULL;
Miniport = Miniport->NextMiniport)
{
if (NDIS_EQUAL_UNICODE_STRING(&UpcaseDevice, &Miniport->MiniportName))
{
fFound = TRUE;
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport);
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_ORPHANED);
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
break;
}
}
RELEASE_SPIN_LOCK_DPC(&MiniBlock->Ref.SpinLock);
}
RELEASE_SPIN_LOCK(&ndisMiniDriverListLock, OldIrql);
FREE_POOL(UpcaseDevice.Buffer);
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO,
("<==ndisHandleOrphanDevice\n"));
return STATUS_SUCCESS;
}
NTSTATUS
FASTCALL
ndisEnumerateInterfaces(
IN PNDIS_ENUM_INTF EnumIntf,
IN UINT BufferLength
)
{
PNDIS_MINIPORT_BLOCK Miniport;
PNDIS_M_DRIVER_BLOCK MiniBlock;
PNDIS_INTERFACE Interface;
UINT SpaceLeft = BufferLength - sizeof(NDIS_ENUM_INTF);
UINT SpaceNeeded;
PUCHAR pBuf;
KIRQL OldIrql;
NTSTATUS Status = STATUS_SUCCESS;
NdisZeroMemory(EnumIntf, BufferLength);
Interface = &EnumIntf->Interface[0];
pBuf = (PUCHAR)EnumIntf + BufferLength;
ACQUIRE_SPIN_LOCK(&ndisMiniDriverListLock, &OldIrql);
for (MiniBlock = ndisMiniDriverList;
MiniBlock != NULL;
MiniBlock = MiniBlock->NextDriver)
{
ACQUIRE_SPIN_LOCK_DPC(&MiniBlock->Ref.SpinLock);
for (Miniport = MiniBlock->MiniportQueue;
Miniport != NULL;
Miniport = Miniport->NextMiniport)
{
if (Miniport->PnPDeviceState != NdisPnPDeviceStarted)
{
continue;
}
EnumIntf->AvailableInterfaces ++;
SpaceNeeded = sizeof(NDIS_INTERFACE) +
Miniport->MiniportName.Length +
Miniport->pAdapterInstanceName->Length;
EnumIntf->BytesNeeded += SpaceNeeded;
if (SpaceLeft >= SpaceNeeded)
{
EnumIntf->TotalInterfaces ++;
SpaceLeft -= SpaceNeeded;
pBuf -= Miniport->MiniportName.Length;
Interface->DeviceName.Buffer = (PWSTR)pBuf;
Interface->DeviceName.MaximumLength =
Interface->DeviceName.Length = Miniport->MiniportName.Length;
CopyMemory(pBuf, Miniport->MiniportName.Buffer, Interface->DeviceName.Length);
POINTER_TO_OFFSET(Interface->DeviceName.Buffer, EnumIntf);
pBuf -= Miniport->pAdapterInstanceName->Length;
Interface->DeviceDescription.Buffer = (PWSTR)pBuf;
Interface->DeviceDescription.MaximumLength =
Interface->DeviceDescription.Length = Miniport->pAdapterInstanceName->Length;
CopyMemory(pBuf, Miniport->pAdapterInstanceName->Buffer, Interface->DeviceDescription.Length);
POINTER_TO_OFFSET(Interface->DeviceDescription.Buffer, EnumIntf);
Interface ++;
}
}
RELEASE_SPIN_LOCK_DPC(&MiniBlock->Ref.SpinLock);
}
RELEASE_SPIN_LOCK(&ndisMiniDriverListLock, OldIrql);
return Status;
}
#if defined(_WIN64)
//
// Version of ndisEnumerateInterfaces that operates on 32-bit ioctl structures.
// This is to support 32-bit apps running on Win64.
//
NTSTATUS
FASTCALL
ndisEnumerateInterfaces32(
IN PNDIS_ENUM_INTF32 EnumIntf,
IN UINT BufferLength
)
{
PNDIS_MINIPORT_BLOCK Miniport;
PNDIS_M_DRIVER_BLOCK MiniBlock;
PNDIS_INTERFACE32 Interface;
UINT SpaceLeft = BufferLength - sizeof(NDIS_ENUM_INTF32);
UINT SpaceNeeded;
PUCHAR pBuf;
KIRQL OldIrql;
NTSTATUS Status = STATUS_SUCCESS;
NdisZeroMemory(EnumIntf, BufferLength);
Interface = &EnumIntf->Interface[0];
pBuf = (PUCHAR)EnumIntf + BufferLength;
ACQUIRE_SPIN_LOCK(&ndisMiniDriverListLock, &OldIrql);
for (MiniBlock = ndisMiniDriverList;
MiniBlock != NULL;
MiniBlock = MiniBlock->NextDriver)
{
ACQUIRE_SPIN_LOCK_DPC(&MiniBlock->Ref.SpinLock);
for (Miniport = MiniBlock->MiniportQueue;
Miniport != NULL;
Miniport = Miniport->NextMiniport)
{
if (Miniport->PnPDeviceState != NdisPnPDeviceStarted)
{
continue;
}
EnumIntf->AvailableInterfaces ++;
SpaceNeeded = sizeof(NDIS_INTERFACE32) +
Miniport->MiniportName.Length +
Miniport->pAdapterInstanceName->Length;
if (SpaceLeft >= SpaceNeeded)
{
EnumIntf->TotalInterfaces ++;
SpaceLeft -= SpaceNeeded;
pBuf -= Miniport->MiniportName.Length;
Interface->DeviceName.MaximumLength =
Interface->DeviceName.Length = Miniport->MiniportName.Length;
CopyMemory(pBuf, Miniport->MiniportName.Buffer, Interface->DeviceName.Length);
Interface->DeviceName.Buffer = (ULONG)((ULONG_PTR)pBuf - (ULONG_PTR)EnumIntf);
pBuf -= Miniport->pAdapterInstanceName->Length;
Interface->DeviceDescription.MaximumLength =
Interface->DeviceDescription.Length = Miniport->pAdapterInstanceName->Length;
CopyMemory(pBuf, Miniport->pAdapterInstanceName->Buffer, Interface->DeviceDescription.Length);
Interface->DeviceDescription.Buffer = (ULONG)((ULONG_PTR)pBuf - (ULONG_PTR)EnumIntf);
Interface ++;
}
}
RELEASE_SPIN_LOCK_DPC(&MiniBlock->Ref.SpinLock);
}
RELEASE_SPIN_LOCK(&ndisMiniDriverListLock, OldIrql);
return Status;
}
#endif // _WIN64
NTSTATUS
ndisUnbindProtocol(
IN PNDIS_OPEN_BLOCK Open,
IN BOOLEAN Notify
)
/*+++
Routine Description:
Arguments:
Return Value:
None
---*/
{
NDIS_STATUS Status = STATUS_SUCCESS;
NDIS_BIND_CONTEXT UnbindContext;
PKMUTEX pMutex;
PKEVENT CloseCompleteEvent = NULL;
PNDIS_PROTOCOL_BLOCK Protocol = Open->ProtocolHandle;
PNDIS_OPEN_BLOCK TmpOpen;
PNDIS_MINIPORT_BLOCK Miniport = Open->MiniportHandle;
KIRQL OldIrql;
BOOLEAN fDerefProtocol = FALSE;
BOOLEAN FreeOpen;
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("==>ndisUnbindProtocol: Open %p, Notify %d\n", Open, Notify));
PnPReferencePackage();
//
// if this is called outside the context of the protocol deregistering, increment
// the ref count to make sure the protocol deregisteration does not go through
// otherwise make note of the fact that we could not increment the ref count and avoid
// deref at the end
//
if (ndisReferenceProtocol(Protocol))
{
fDerefProtocol = TRUE;
}
ASSERT(MINIPORT_TEST_FLAG(Open, fMINIPORT_OPEN_UNBINDING));
CloseCompleteEvent = Open->CloseCompleteEvent;
do
{
NTSTATUS WaitStatus;
pMutex = &Protocol->Mutex;
WAIT_FOR_PROTO_MUTEX(Protocol);
//
// make sure the open didn't go away while we were waiting for
// protocol mutex.
//
ACQUIRE_SPIN_LOCK(&Protocol->Ref.SpinLock, &OldIrql);
for (TmpOpen = Protocol->OpenQueue;
TmpOpen != NULL;
TmpOpen = TmpOpen->ProtocolNextOpen)
{
if (TmpOpen == Open)
break;
}
RELEASE_SPIN_LOCK(&Protocol->Ref.SpinLock, OldIrql);
if (TmpOpen == NULL)
{
//
// open went away while we were trying to get the protocol mutex
// return right away
//
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("ndisUnbindProtocol: Open %p, Flags %lx was closed while we were waiting for the protocol mutex.\n", Open, Open->Flags));
break;
}
//
// wait for all AF notifications to go through
//
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IS_CO))
{
KEVENT AfNotifyCompleteEvent;
INITIALIZE_EVENT(&AfNotifyCompleteEvent);
Open->AfNotifyCompleteEvent = &AfNotifyCompleteEvent;
if (Open->PendingAfNotifications != 0)
{
WAIT_FOR_OBJECT(Open->AfNotifyCompleteEvent , 0);
}
Open->AfNotifyCompleteEvent = NULL;
}
//
// Do a query-remove here first
//
if (Notify && (Protocol->ProtocolCharacteristics.PnPEventHandler != NULL))
{
NET_PNP_EVENT NetPnpEvent;
PNDIS_PNP_EVENT_RESERVED EventReserved;
KEVENT Event;
RtlZeroMemory(&NetPnpEvent, sizeof(NET_PNP_EVENT));
INITIALIZE_EVENT(&Event);
EventReserved = PNDIS_PNP_EVENT_RESERVED_FROM_NET_PNP_EVENT(&NetPnpEvent);
NetPnpEvent.NetEvent = NetEventQueryRemoveDevice;
NetPnpEvent.Buffer = NULL;
NetPnpEvent.BufferLength = 0;
EventReserved->pEvent = &Event;
//
// Indicate the event to the protocol.
//
Status = (Protocol->ProtocolCharacteristics.PnPEventHandler)(
Open->ProtocolBindingContext,
&NetPnpEvent);
if (NDIS_STATUS_PENDING == Status)
{
//
// Wait for completion.
//
WAIT_FOR_PROTOCOL(Protocol, &Event);
//
// Get the completion status.
//
Status = EventReserved->Status;
}
//
// Is the status OK?
//
if (Status != NDIS_STATUS_SUCCESS)
{
break;
}
}
if (CloseCompleteEvent != NULL)
{
INITIALIZE_EVENT(CloseCompleteEvent);
}
//
// Protocol ok with remove so now do it.
//
INITIALIZE_EVENT(&UnbindContext.Event);
Status = NDIS_STATUS_SUCCESS;
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
(*Protocol->ProtocolCharacteristics.UnbindAdapterHandler)(
&Status,
Open->ProtocolBindingContext,
&UnbindContext);
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
if (Status == NDIS_STATUS_PENDING)
{
WAIT_FOR_PROTOCOL(Protocol, &UnbindContext.Event);
Status = UnbindContext.BindStatus;
}
ASSERT(Status == NDIS_STATUS_SUCCESS);
ndisNotifyWmiBindUnbind(Miniport, Protocol, FALSE);
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
if (CloseCompleteEvent != NULL)
{
//
// make sure the open is gone
//
WAIT_FOR_PROTOCOL(Protocol, CloseCompleteEvent);
}
} while (FALSE);
RELEASE_MUTEX(pMutex);
ACQUIRE_SPIN_LOCK(&Open->SpinLock, &OldIrql);
//
// did the close routine get our message not to free the open structure?
//
if (MINIPORT_TEST_FLAG(Open, fMINIPORT_OPEN_CLOSE_COMPLETE))
{
//
// we have to get rid of open ourselves
//
FreeOpen = TRUE;
}
else
{
//
// for some reason, unbind did not go through or close is
// still in progress
//
MINIPORT_CLEAR_FLAG(Open, fMINIPORT_OPEN_UNBINDING |
fMINIPORT_OPEN_DONT_FREE |
fMINIPORT_OPEN_PROCESSING);
FreeOpen = FALSE;
}
RELEASE_SPIN_LOCK(&Open->SpinLock, OldIrql);
PnPDereferencePackage();
if (FreeOpen)
{
ndisRemoveOpenFromGlobalList(Open);
FREE_POOL(Open);
}
if (fDerefProtocol)
{
ndisDereferenceProtocol(Protocol);
}
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("<==ndisUnbindProtocol: Open %p, Notify %d, Status %lx\n", Open, Notify, Status));
return(Status);
}
VOID
ndisReferenceMiniportByName(
IN PUNICODE_STRING DeviceName,
OUT PNDIS_MINIPORT_BLOCK * pMiniport
)
{
KIRQL OldIrql;
PNDIS_M_DRIVER_BLOCK MiniBlock;
PNDIS_MINIPORT_BLOCK Miniport = NULL;
UNICODE_STRING UpcaseDevice;
DBGPRINT_RAW(DBG_COMP_REF, DBG_LEVEL_INFO,
("==>ndisReferenceMiniportByName\n"));
*pMiniport = NULL;
UpcaseDevice.Length = DeviceName->Length;
UpcaseDevice.MaximumLength = DeviceName->Length + sizeof(WCHAR);
UpcaseDevice.Buffer = ALLOC_FROM_POOL(UpcaseDevice.MaximumLength, NDIS_TAG_STRING);
if (UpcaseDevice.Buffer == NULL)
{
return;
}
RtlUpcaseUnicodeString(&UpcaseDevice, DeviceName, FALSE);
do
{
UINT Depth = 1;
ACQUIRE_SPIN_LOCK(&ndisMiniDriverListLock, &OldIrql);
for (MiniBlock = ndisMiniDriverList;
MiniBlock != NULL;
MiniBlock = MiniBlock->NextDriver)
{
ACQUIRE_SPIN_LOCK_DPC(&MiniBlock->Ref.SpinLock);
for (Miniport = MiniBlock->MiniportQueue;
Miniport != NULL;
Miniport = Miniport->NextMiniport)
{
if (!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_ORPHANED) &&
(Miniport->BindPaths != NULL) &&
(Miniport->BindPaths->Number >= Depth) &&
NDIS_EQUAL_UNICODE_STRING(&UpcaseDevice, &Miniport->BindPaths->Paths[0]))
{
if (*pMiniport != NULL)
{
MINIPORT_DECREMENT_REF(*pMiniport);
*pMiniport = NULL;
}
Depth = Miniport->BindPaths->Number;
if (MINIPORT_INCREMENT_REF(Miniport))
{
*pMiniport = Miniport;
}
break;
}
}
RELEASE_SPIN_LOCK_DPC(&MiniBlock->Ref.SpinLock);
}
} while (FALSE);
RELEASE_SPIN_LOCK(&ndisMiniDriverListLock, OldIrql);
FREE_POOL(UpcaseDevice.Buffer);
DBGPRINT_RAW(DBG_COMP_REF, DBG_LEVEL_INFO,
("<==ndisReferenceMiniportByName\n"));
}
PNDIS_OPEN_BLOCK
FASTCALL
ndisMapOpenByName(
IN PUNICODE_STRING DeviceName,
IN PNDIS_PROTOCOL_BLOCK Protocol,
IN BOOLEAN Reference,
IN BOOLEAN fUnbinding
)
{
UNICODE_STRING UpcaseDevice;
PNDIS_OPEN_BLOCK Open, tmpOpen;
KIRQL OldIrql;
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("==>ndisReferenceOpenByName, DeviceName %p, Protocol %p, fUnbinding %d\n",
DeviceName, Protocol, fUnbinding));
Open = NULL;
UpcaseDevice.Length = DeviceName->Length;
UpcaseDevice.MaximumLength = DeviceName->Length + sizeof(WCHAR);
UpcaseDevice.Buffer = ALLOC_FROM_POOL(UpcaseDevice.MaximumLength, NDIS_TAG_STRING);
if (UpcaseDevice.Buffer == NULL)
{
DBGPRINT_RAW(DBG_COMP_ALL, DBG_LEVEL_ERR,
("<==ndisReferenceOpenByName: failed to allocate memory.\n"));
return NULL;
}
RtlUpcaseUnicodeString(&UpcaseDevice, DeviceName, FALSE);
ACQUIRE_SPIN_LOCK(&Protocol->Ref.SpinLock, &OldIrql);
//
// Now walk the open list and get to the open representing the DeviceName
//
for (Open = Protocol->OpenQueue;
Open != NULL;
Open = Open->ProtocolNextOpen)
{
if (NDIS_EQUAL_UNICODE_STRING(&UpcaseDevice, Open->RootDeviceName))
{
tmpOpen = Open;
ACQUIRE_SPIN_LOCK_DPC(&tmpOpen->SpinLock);
if (fUnbinding)
{
if (MINIPORT_TEST_FLAG(Open, fMINIPORT_OPEN_UNBINDING |
fMINIPORT_OPEN_CLOSING |
fMINIPORT_OPEN_PROCESSING))
{
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("ndisReferenceOpenByName: Open %p is already getting unbind\n", Open));
Open = NULL;
}
else
{
MINIPORT_SET_FLAG(Open, fMINIPORT_OPEN_UNBINDING |
fMINIPORT_OPEN_DONT_FREE |
fMINIPORT_OPEN_PROCESSING);
}
}
else
{
if (MINIPORT_TEST_FLAG(Open, fMINIPORT_OPEN_PROCESSING))
{
Open = NULL;
}
else
{
MINIPORT_SET_FLAG(Open, fMINIPORT_OPEN_PROCESSING);
}
}
RELEASE_SPIN_LOCK_DPC(&tmpOpen->SpinLock);
break;
}
}
RELEASE_SPIN_LOCK(&Protocol->Ref.SpinLock, OldIrql);
if ((Open != NULL) && Reference)
{
PNDIS_MINIPORT_BLOCK Miniport;
Miniport = Open->MiniportHandle;
if (!MINIPORT_INCREMENT_REF(Miniport))
{
if (fUnbinding)
{
MINIPORT_CLEAR_FLAG(Open, fMINIPORT_OPEN_UNBINDING |
fMINIPORT_OPEN_PROCESSING |
fMINIPORT_OPEN_DONT_FREE);
}
else
{
MINIPORT_CLEAR_FLAG(Open, fMINIPORT_OPEN_PROCESSING);
}
Open = NULL;
}
}
FREE_POOL(UpcaseDevice.Buffer);
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("<==ndisReferenceOpenByName: Open %p\n", Open ));
return(Open);
}
NTSTATUS
FASTCALL
ndisHandleLegacyTransport(
IN PUNICODE_STRING pDevice
)
{
NTSTATUS Status = STATUS_SUCCESS;
RTL_QUERY_REGISTRY_TABLE LinkQueryTable[3];
PWSTR Export = NULL;
HANDLE TdiHandle;
DBGPRINT_RAW(DBG_COMP_PROTOCOL, DBG_LEVEL_INFO,
("==>ndisHandleLegacyTransport\n"));
if (ndisTdiRegisterCallback == NULL)
{
DBGPRINT_RAW(DBG_COMP_CONFIG, DBG_LEVEL_INFO,
("<==ndisHandleLegacyTransport\n"));
return STATUS_UNSUCCESSFUL;
}
//
// Set up LinkQueryTable to do the following:
//
//
// 1) Switch to the Linkage key below the xports registry key
//
LinkQueryTable[0].QueryRoutine = NULL;
LinkQueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY;
LinkQueryTable[0].Name = L"Linkage";
//
// 2) Call ndisReadParameter for "Export" (as a single multi-string),
// which will allocate storage and save the data in Export.
//
LinkQueryTable[1].QueryRoutine = ndisReadParameter;
LinkQueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_NOEXPAND;
LinkQueryTable[1].Name = L"Export";
LinkQueryTable[1].EntryContext = (PVOID)&Export;
LinkQueryTable[1].DefaultType = REG_NONE;
//
// 3) Stop
//
LinkQueryTable[2].QueryRoutine = NULL;
LinkQueryTable[2].Flags = 0;
LinkQueryTable[2].Name = NULL;
do
{
UNICODE_STRING Us;
PWSTR CurExport;
Status = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
pDevice->Buffer,
LinkQueryTable,
(PVOID)NULL, // no context needed
NULL);
if (!NT_SUCCESS(Status))
{
//
// Do not complain about TDI drivers which do not
// have any linkages
//
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
{
Status = STATUS_SUCCESS;
}
break;
}
//
// Walk the list of exports and call TdiRegisterDevice for each
//
for (CurExport = Export;
*CurExport != 0;
CurExport = (PWCHAR)((PUCHAR)CurExport + Us.MaximumLength))
{
RtlInitUnicodeString (&Us, CurExport);
Status = (*ndisTdiRegisterCallback)(&Us, &TdiHandle);
if (!NT_SUCCESS(Status))
{
break;
}
}
} while (FALSE);
if (Export != NULL)
FREE_POOL(Export);
DBGPRINT_RAW(DBG_COMP_PROTOCOL, DBG_LEVEL_INFO,
("<==ndisHandleLegacyTransport\n"));
return(Status);
}
VOID
FASTCALL
ndisInitializeBinding(
IN PNDIS_MINIPORT_BLOCK Miniport,
IN PNDIS_PROTOCOL_BLOCK Protocol
)
{
PUNICODE_STRING ExportName;
NDIS_BIND_CONTEXT BindContext;
PDEVICE_OBJECT PhysicalDeviceObject;
NDIS_STATUS BindStatus;
UNICODE_STRING ProtocolSection;
UNICODE_STRING DerivedBaseName, Parms;
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("==>ndisInitializeBinding\n"));
//
// Call the protocol to bind to the Miniport
//
WAIT_FOR_PROTO_MUTEX(Protocol);
do
{
//
// once we grabbed the protocol mutex, check again to see if
// the adapter is still there
//
if (!ndisIsMiniportStarted(Miniport) ||
((Miniport->PnPDeviceState != NdisPnPDeviceStarted) &&
(Miniport->PnPDeviceState != NdisPnPDeviceQueryStopped) &&
(Miniport->PnPDeviceState != NdisPnPDeviceQueryRemoved)))
{
break;
}
if (TRUE == ndisProtocolAlreadyBound(Protocol, Miniport))
{
//
// these two are already bound. just return
//
break;
}
ExportName = &Miniport->BindPaths->Paths[0];
Protocol->BindDeviceName = &Miniport->MiniportName;
Protocol->RootDeviceName = ExportName;
PhysicalDeviceObject = Miniport->PhysicalDeviceObject;
if (ndisReferenceProtocol(Protocol) == FALSE)
{
break;
}
RtlInitUnicodeString(&Parms, L"\\Parameters\\Adapters\\");
DerivedBaseName = *ExportName;
DerivedBaseName.Length -= ndisDeviceStr.Length;
DerivedBaseName.MaximumLength -= ndisDeviceStr.Length;
(PUCHAR)(DerivedBaseName.Buffer) += ndisDeviceStr.Length;
ProtocolSection.MaximumLength = Protocol->ProtocolCharacteristics.Name.Length + // "tcpip"
Parms.Length + // "\Parameters\Adapters\"
ExportName->Length - ndisDeviceStr.Length + // "{GUID}"
sizeof(WCHAR);
ProtocolSection.Length = 0;
ProtocolSection.Buffer = (PWSTR)ALLOC_FROM_POOL(ProtocolSection.MaximumLength,
NDIS_TAG_DEFAULT);
if (ProtocolSection.Buffer != NULL)
{
ZeroMemory(ProtocolSection.Buffer, ProtocolSection.MaximumLength);
RtlCopyUnicodeString(&ProtocolSection,
&Protocol->ProtocolCharacteristics.Name);
RtlAppendUnicodeStringToString(&ProtocolSection,
&Parms);
RtlAppendUnicodeStringToString(&ProtocolSection,
&DerivedBaseName);
}
else
{
ndisDereferenceProtocol(Protocol);
break;
}
BindContext.Next = NULL;
BindContext.Protocol = Protocol;
BindContext.Miniport = Miniport;
BindContext.ProtocolSection = ProtocolSection;
BindContext.DeviceName = ExportName;
INITIALIZE_EVENT(&BindContext.Event);
if (!Protocol->Ref.Closing)
{
BindStatus = NDIS_STATUS_SUCCESS;
Protocol->BindingAdapter = Miniport;
(*Protocol->ProtocolCharacteristics.BindAdapterHandler)(&BindStatus,
&BindContext,
ExportName,
&ProtocolSection,
(PVOID)PhysicalDeviceObject);
if (BindStatus == NDIS_STATUS_PENDING)
{
WAIT_FOR_PROTOCOL(Protocol, &BindContext.Event);
BindStatus = BindContext.BindStatus;
}
Protocol->BindingAdapter = NULL;
if (BindStatus == NDIS_STATUS_SUCCESS)
{
ndisNotifyWmiBindUnbind(Miniport, Protocol, TRUE);
}
#if DBG
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
(" ndisInitializeBinding\n"));
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
(" Protocol: "));
DBGPRINT_UNICODE(DBG_COMP_BIND, DBG_LEVEL_INFO,
&Protocol->ProtocolCharacteristics.Name);
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("\n Adapter: "));
if (Miniport->pAdapterInstanceName)
{
DBGPRINT_UNICODE(DBG_COMP_BIND, DBG_LEVEL_INFO,
Miniport->pAdapterInstanceName);
}
else
{
DBGPRINT_UNICODE(DBG_COMP_INIT, DBG_LEVEL_INFO,
&Miniport->BaseName);
}
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("\n Result: %lx\n", BindStatus));
#endif
}
FREE_POOL(ProtocolSection.Buffer);
Protocol->BindDeviceName = NULL;
ndisDereferenceProtocol(Protocol);
} while (FALSE);
RELEASE_PROT_MUTEX(Protocol);
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("<==ndisInitializeBinding\n"));
}
VOID
NdisCompleteBindAdapter(
IN NDIS_HANDLE BindAdapterContext,
IN NDIS_STATUS Status,
IN NDIS_STATUS OpenStatus
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_BIND_CONTEXT pContext = (PNDIS_BIND_CONTEXT)BindAdapterContext;
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("==>NdisCompleteBindAdapter\n"));
pContext->BindStatus = Status;
SET_EVENT(&pContext->Event);
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("<==NdisCompleteBindAdapter\n"));
}
VOID
NdisCompleteUnbindAdapter(
IN NDIS_HANDLE UnbindAdapterContext,
IN NDIS_STATUS Status
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_BIND_CONTEXT pContext = (PNDIS_BIND_CONTEXT)UnbindAdapterContext;
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("==>NdisCompleteUnbindAdapter\n"));
pContext->BindStatus = Status;
SET_EVENT(&pContext->Event);
DBGPRINT_RAW(DBG_COMP_BIND, DBG_LEVEL_INFO,
("<==NdisCompleteUnbindAdapter\n"));
}
VOID
NdisRegisterTdiCallBack(
IN TDI_REGISTER_CALLBACK RegisterCallback,
IN TDI_PNP_HANDLER PnPHandler
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
DBGPRINT_RAW(DBG_COMP_PNP, DBG_LEVEL_INFO,
("==>NdisRegisterTdiCallBack\n"));
if (ndisTdiRegisterCallback == NULL)
{
ndisTdiRegisterCallback = RegisterCallback;
}
if (ndisTdiPnPHandler == NULL)
{
ndisTdiPnPHandler = PnPHandler;
}
DBGPRINT_RAW(DBG_COMP_PNP, DBG_LEVEL_INFO,
("<==NdisRegisterTdiCallBack\n"));
}
VOID
ndisFindRootDevice(
IN PNDIS_STRING DeviceName,
IN BOOLEAN fTester,
OUT PNDIS_STRING * pBindDevice,
OUT PNDIS_STRING * pRootDevice,
OUT PNDIS_MINIPORT_BLOCK * pAdapter
)
/*++
Routine Description:
Find the Miniport which is the highest level filter given the target root name.
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
PNDIS_M_DRIVER_BLOCK MiniBlock;
PNDIS_MINIPORT_BLOCK Miniport;
PNDIS_STRING RootDevice, BindDevice;
NDIS_STRING UpcaseDevice;
PWSTR pwch;
UINT Depth = 1;
BOOLEAN Found = FALSE;
DBGPRINT(DBG_COMP_PNP, DBG_LEVEL_INFO,
("==>ndisFindRootDevice\n"));
*pBindDevice = NULL;
*pRootDevice = NULL;
*pAdapter = NULL;
//
// First we need to upcase the device-name before checking
//
UpcaseDevice.Length = DeviceName->Length;
UpcaseDevice.MaximumLength = DeviceName->Length + sizeof(WCHAR);
UpcaseDevice.Buffer = ALLOC_FROM_POOL(UpcaseDevice.MaximumLength, NDIS_TAG_STRING);
if ((pwch = UpcaseDevice.Buffer) == NULL)
{
return;
}
RtlUpcaseUnicodeString(&UpcaseDevice, DeviceName, FALSE);
BindDevice = &UpcaseDevice;
ASSERT(ndisPkgs[NPNP_PKG].ReferenceCount > 0);
PnPReferencePackage();
ACQUIRE_SPIN_LOCK(&ndisMiniDriverListLock, &OldIrql);
for (MiniBlock = ndisMiniDriverList;
MiniBlock != NULL;
MiniBlock = MiniBlock->NextDriver)
{
ACQUIRE_SPIN_LOCK_DPC(&MiniBlock->Ref.SpinLock);
for (Miniport = MiniBlock->MiniportQueue;
Miniport != NULL;
Miniport = Miniport->NextMiniport)
{
if (fTester)
{
if (NDIS_EQUAL_UNICODE_STRING(BindDevice, &Miniport->MiniportName))
{
BindDevice = &Miniport->MiniportName;
RootDevice = &Miniport->MiniportName;
*pAdapter = Miniport;
Found = TRUE;
break;
}
}
else if ((Miniport->BindPaths->Number >= Depth) &&
NDIS_EQUAL_UNICODE_STRING(BindDevice, &Miniport->BindPaths->Paths[0]))
{
RootDevice = &Miniport->BindPaths->Paths[0];
BindDevice = &Miniport->MiniportName;
*pAdapter = Miniport;
Depth = Miniport->BindPaths->Number;
Found = TRUE;
}
}
RELEASE_SPIN_LOCK_DPC(&MiniBlock->Ref.SpinLock);
if (fTester && Found)
{
break;
}
}
RELEASE_SPIN_LOCK(&ndisMiniDriverListLock, OldIrql);
PnPDereferencePackage();
FREE_POOL(pwch);
if (Found)
{
*pBindDevice = BindDevice;
*pRootDevice = RootDevice;
}
DBGPRINT(DBG_COMP_PNP, DBG_LEVEL_INFO,
("<==ndisFindRootDevice\n"));
}
VOID
ndisNotifyWmiBindUnbind(
PNDIS_MINIPORT_BLOCK Miniport,
PNDIS_PROTOCOL_BLOCK Protocol,
BOOLEAN fBind
)
/*++
Routine Description:
Notify WMI that either a bind or an unbind has occured.
Arguments:
Return Value:
--*/
{
PWNODE_SINGLE_INSTANCE wnode;
PUCHAR ptmp;
NTSTATUS NtStatus;
DBGPRINT(DBG_COMP_PNP, DBG_LEVEL_INFO,
("==>ndisNotifyWmiBindUnbind: Miniport %p, Protocol %p, fBind %lx\n", Miniport, Protocol, fBind));
ndisSetupWmiNode(Miniport,
Miniport->pAdapterInstanceName,
Miniport->BindPaths->Paths[0].Length + sizeof(WCHAR) +
Protocol->ProtocolCharacteristics.Name.Length + sizeof(WCHAR),
fBind ? (PVOID)&GUID_NDIS_NOTIFY_BIND : (PVOID)&GUID_NDIS_NOTIFY_UNBIND,
&wnode);
if (wnode != NULL)
{
//
// Save the number of elements in the first ULONG.
//
ptmp = (PUCHAR)wnode + wnode->DataBlockOffset;
//
// Copy the data which is the protocol name + the miniport name in the data field
// Protocol<NULL>MiniportName<NULL>
//
RtlCopyMemory(ptmp,
Protocol->ProtocolCharacteristics.Name.Buffer,
Protocol->ProtocolCharacteristics.Name.Length);
RtlCopyMemory(ptmp + Protocol->ProtocolCharacteristics.Name.Length + sizeof(WCHAR),
Miniport->BindPaths->Paths[0].Buffer,
Miniport->BindPaths->Paths[0].Length);
//
// notify kernel mode components who have registered for Ndis BindUnbind event
//
if (ndisBindUnbindCallbackObject != NULL)
{
ExNotifyCallback(ndisBindUnbindCallbackObject,
(PVOID)wnode,
NULL);
}
//
// Indicate the event to WMI. WMI will take care of freeing
// the WMI struct back to pool.
//
NtStatus = IoWMIWriteEvent(wnode);
if (!NT_SUCCESS(NtStatus))
{
DBGPRINT(DBG_COMP_WMI, DBG_LEVEL_ERR,
("IoWMIWriteEvent failed %lx\n", NtStatus));
FREE_POOL(wnode);
}
}
DBGPRINT(DBG_COMP_PNP, DBG_LEVEL_INFO,
("<==ndisNotifyWmiBindUnbind: Miniport %p, Protocol %p, fBind %lx\n", Miniport, Protocol, fBind));
return;
}
VOID
ndisNotifyDevicePowerStateChange(
PNDIS_MINIPORT_BLOCK Miniport,
NDIS_DEVICE_POWER_STATE PowerState
)
/*++
Routine Description:
Notify WMI that that the power state of a NIC is changed.
Arguments:
Return Value:
--*/
{
PWNODE_SINGLE_INSTANCE wnode;
PUCHAR ptmp;
NTSTATUS NtStatus;
DBGPRINT(DBG_COMP_PNP, DBG_LEVEL_INFO,
("==>ndisNotifyDevicePowerStateChange: Miniport %p, PowerState %lx\n", Miniport, PowerState));
ndisSetupWmiNode(Miniport,
Miniport->pAdapterInstanceName,
Miniport->MiniportName.Length + sizeof(WCHAR),
(PowerState == NdisDeviceStateD0) ? (PVOID)&GUID_NDIS_NOTIFY_DEVICE_POWER_ON : (PVOID)&GUID_NDIS_NOTIFY_DEVICE_POWER_OFF,
&wnode);
if (wnode != NULL)
{
//
// Save the number of elements in the first ULONG.
//
ptmp = (PUCHAR)wnode + wnode->DataBlockOffset;
RtlCopyMemory(ptmp,
Miniport->MiniportName.Buffer,
Miniport->MiniportName.Length);
//
// Indicate the event to WMI. WMI will take care of freeing
// the WMI struct back to pool.
//
NtStatus = IoWMIWriteEvent(wnode);
if (!NT_SUCCESS(NtStatus))
{
DBGPRINT(DBG_COMP_WMI, DBG_LEVEL_ERR,
("IoWMIWriteEvent failed %lx\n", NtStatus));
FREE_POOL(wnode);
}
}
DBGPRINT(DBG_COMP_PNP, DBG_LEVEL_INFO,
("<==ndisNotifyDevicePowerStateChange: Miniport %p, PowerState %lx\n", Miniport, PowerState));
return;
}
BOOLEAN
NdisMatchPdoWithPacket(
IN PNDIS_PACKET Packet,
IN PVOID Pdo
)
{
PNDIS_STACK_RESERVED NSR;
PNDIS_MINIPORT_BLOCK Miniport;
NDIS_STACK_RESERVED_FROM_PACKET(Packet, &NSR);
Miniport = NSR->Miniport;
return (Pdo == Miniport->PhysicalDeviceObject);
}
VOID
ndisPowerStateCallback(
PVOID CallBackContext,
PVOID Argument1,
PVOID Argument2
)
{
ULONG Action = (ULONG)((ULONG_PTR)Argument1);
ULONG State = (ULONG)((ULONG_PTR)Argument2);
NDIS_POWER_PROFILE PowerProfile;
DBGPRINT(DBG_COMP_PNP, DBG_LEVEL_INFO,
("==>ndisPowerStateCallback: Action %lx, State %lx\n", Action, State));
if (Action == PO_CB_AC_STATUS)
{
ndisAcOnLine = State;
PowerProfile = ((BOOLEAN)ndisAcOnLine == TRUE) ? NdisPowerProfileAcOnLine : NdisPowerProfileBattery;
ndisNotifyMiniports((PNDIS_MINIPORT_BLOCK)NULL,
NdisDevicePnPEventPowerProfileChanged,
&PowerProfile,
sizeof(NDIS_POWER_PROFILE));
}
DBGPRINT(DBG_COMP_PNP, DBG_LEVEL_INFO,
("<==ndisPowerStateCallback: Action %lx, State %lx\n", Action, State));
}
VOID
ndisNotifyMiniports(
IN PNDIS_MINIPORT_BLOCK Miniport OPTIONAL,
IN NDIS_DEVICE_PNP_EVENT DevicePnPEvent,
IN PVOID Buffer,
IN ULONG Length
)
{
PNDIS_M_DRIVER_BLOCK MiniBlock, NextMiniBlock;
PNDIS_MINIPORT_BLOCK CurMiniport, NM;
KIRQL OldIrql;
DBGPRINT(DBG_COMP_PNP, DBG_LEVEL_INFO,
("==>ndisNotifyMiniportsPowerProfileChange: Miniport %p, Event %lx, Buffer %p\n",
Miniport,
DevicePnPEvent,
Buffer));
PnPReferencePackage();
do
{
if (Miniport)
{
if(Miniport->DriverHandle->MiniportCharacteristics.PnPEventNotifyHandler != NULL)
{
//
// if Miniport has been specified, the caller is responsible to make sure it is valid and appropriate
// to call the miniport
//
Miniport->DriverHandle->MiniportCharacteristics.PnPEventNotifyHandler(Miniport->MiniportAdapterContext,
DevicePnPEvent,
Buffer,
Length);
}
break;
}
//
// notification is for all the miniports
//
ACQUIRE_SPIN_LOCK(&ndisMiniDriverListLock, &OldIrql);
for (MiniBlock = ndisMiniDriverList;
MiniBlock != NULL;
MiniBlock = NextMiniBlock)
{
if (ndisReferenceDriver(MiniBlock))
{
RELEASE_SPIN_LOCK(&ndisMiniDriverListLock, OldIrql);
while ((CurMiniport = ndisReferenceNextUnprocessedMiniport(MiniBlock)) != NULL)
{
if (CurMiniport->DriverHandle->MiniportCharacteristics.PnPEventNotifyHandler != NULL)
{
CurMiniport->DriverHandle->MiniportCharacteristics.PnPEventNotifyHandler(CurMiniport->MiniportAdapterContext,
NdisDevicePnPEventPowerProfileChanged,
Buffer,
Length);
}
}
ndisUnprocessAllMiniports(MiniBlock);
ACQUIRE_SPIN_LOCK(&ndisMiniDriverListLock, &OldIrql);
NextMiniBlock = MiniBlock->NextDriver;
ndisDereferenceDriver(MiniBlock, TRUE);
}
}
RELEASE_SPIN_LOCK(&ndisMiniDriverListLock, OldIrql);
} while (FALSE);
PnPDereferencePackage();
DBGPRINT(DBG_COMP_PNP, DBG_LEVEL_INFO,
("<==>ndisNotifyMiniportsPowerProfileChange: Miniport %p\n", Miniport));
return;
}
PNDIS_MINIPORT_BLOCK
ndisReferenceNextUnprocessedMiniport(
IN PNDIS_M_DRIVER_BLOCK MiniBlock
)
{
PNDIS_MINIPORT_BLOCK Miniport;
KIRQL OldIrql;
DBGPRINT_RAW(DBG_COMP_PNP, DBG_LEVEL_INFO,
("==>ndisReferenceNextUnprocessedMiniport: MiniBlock %p\n", MiniBlock));
ACQUIRE_SPIN_LOCK(&MiniBlock->Ref.SpinLock, &OldIrql);
for (Miniport = MiniBlock->MiniportQueue;
Miniport != NULL;
Miniport = Miniport->NextMiniport)
{
if (!MINIPORT_TEST_FLAG(Miniport, (fMINIPORT_DEREGISTERED_INTERRUPT |
fMINIPORT_RESET_IN_PROGRESS |
fMINIPORT_PM_HALTING)) &&
!MINIPORT_PNP_TEST_FLAG(Miniport, (fMINIPORT_REMOVE_IN_PROGRESS |
fMINIPORT_DEVICE_FAILED |
fMINIPORT_PM_HALTED |
fMINIPORT_HALTING |
fMINIPORT_SHUTTING_DOWN |
fMINIPORT_PROCESSING)) &&
(Miniport->PnPDeviceState == NdisPnPDeviceStarted) &&
(Miniport->CurrentDevicePowerState == PowerDeviceD0) &&
MINIPORT_INCREMENT_REF(Miniport))
{
MINIPORT_PNP_SET_FLAG(Miniport, fMINIPORT_PROCESSING);
break;
}
}
RELEASE_SPIN_LOCK(&MiniBlock->Ref.SpinLock, OldIrql);
DBGPRINT_RAW(DBG_COMP_PNP, DBG_LEVEL_INFO,
("<==ndisReferenceNextUnprocessedMiniport: MiniBlock %p\n", MiniBlock));
return(Miniport);
}
VOID
ndisUnprocessAllMiniports(
IN PNDIS_M_DRIVER_BLOCK MiniBlock
)
{
PNDIS_MINIPORT_BLOCK Miniport;
KIRQL OldIrql;
DBGPRINT_RAW(DBG_COMP_PNP, DBG_LEVEL_INFO,
("==>ndisUnprocessAllMiniports: MiniBlock %p\n", MiniBlock));
while (TRUE)
{
ACQUIRE_SPIN_LOCK(&MiniBlock->Ref.SpinLock, &OldIrql);
//
// find the first miniport that is being proccessed. clear the flag, dereference the
// miniport and go through the whole process again.
//
for (Miniport = MiniBlock->MiniportQueue;
Miniport != NULL;
Miniport = Miniport->NextMiniport)
{
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_PROCESSING))
{
MINIPORT_PNP_CLEAR_FLAG(Miniport, fMINIPORT_PROCESSING);
break;
}
}
RELEASE_SPIN_LOCK(&MiniBlock->Ref.SpinLock, OldIrql);
if (Miniport == NULL)
break;
//
// dereferencing the miniport could make it to go away
//
MINIPORT_DECREMENT_REF(Miniport);
}
DBGPRINT_RAW(DBG_COMP_PNP, DBG_LEVEL_INFO,
("<==ndisUnprocessAllMiniports: MiniBlock %p\n", MiniBlock));
}
PVOID
NdisGetRoutineAddress(
IN PUNICODE_STRING NdisRoutineName
)
{
PVOID Address;
ANSI_STRING AnsiString;
NTSTATUS Status;
Status = RtlUnicodeStringToAnsiString(&AnsiString,
NdisRoutineName,
TRUE);
if (!NT_SUCCESS(Status))
{
return NULL;
}
Address = FindExportedRoutineByName(ndisDriverObject->DriverStart, &AnsiString);
RtlFreeAnsiString (&AnsiString);
return Address;
}
PVOID
FindExportedRoutineByName (
IN PVOID DllBase,
IN PANSI_STRING AnsiImageRoutineName
)
/*++
Routine Description:
This function searches the argument module looking for the requested
exported function name.
Arguments:
DllBase - Supplies the base address of the requested module.
AnsiImageRoutineName - Supplies the ANSI routine name being searched for.
Return Value:
The virtual address of the requested routine or NULL if not found.
--*/
{
USHORT OrdinalNumber;
PULONG NameTableBase;
PUSHORT NameOrdinalTableBase;
PULONG Addr;
ULONG High;
ULONG Low;
ULONG Middle;
LONG Result;
ULONG ExportSize;
PVOID FunctionAddress;
PIMAGE_EXPORT_DIRECTORY ExportDirectory;
PAGED_CODE();
FunctionAddress = NULL;
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(
DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&ExportSize
);
if (ExportDirectory == NULL) {
return NULL;
}
//
// Initialize the pointer to the array of RVA-based ansi export strings.
//
NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);
//
// Initialize the pointer to the array of USHORT ordinal numbers.
//
NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);
//
// Lookup the desired name in the name table using a binary search.
//
Low = 0;
High = ExportDirectory->NumberOfNames - 1;
//
// Initializing Middle is not needed for correctness, but without it
// the compiler cannot compile this code W4 to check for use of
// uninitialized variables.
//
Middle = 0;
while (High >= Low && (LONG)High >= 0) {
//
// Compute the next probe index and compare the import name
// with the export name entry.
//
Middle = (Low + High) >> 1;
Result = strcmp (AnsiImageRoutineName->Buffer,
(PCHAR)DllBase + NameTableBase[Middle]);
if (Result < 0) {
High = Middle - 1;
}
else if (Result > 0) {
Low = Middle + 1;
}
else {
break;
}
}
//
// If the high index is less than the low index, then a matching
// table entry was not found. Otherwise, get the ordinal number
// from the ordinal table.
//
if ((LONG)High < (LONG)Low) {
return NULL;
}
OrdinalNumber = NameOrdinalTableBase[Middle];
//
// If the OrdinalNumber is not within the Export Address Table,
// then this image does not implement the function. Return not found.
//
if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {
return NULL;
}
//
// Index into the array of RVA export addresses by ordinal number.
//
Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
FunctionAddress = (PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]);
//
// Forwarders are not used by the kernel and HAL to each other.
//
if ((ULONG_PTR)FunctionAddress > (ULONG_PTR)ExportDirectory &&
(ULONG_PTR)FunctionAddress < ((ULONG_PTR)ExportDirectory + ExportSize)) {
FunctionAddress = NULL;
}
return FunctionAddress;
}
UINT
NdisGetVersion(
VOID
)
{
return ((NDIS_MAJOR_VERSION << 16) | NDIS_MINOR_VERSION);
}
#if 0
VOID
ndisBindUnbindCallback(
PVOID CallBackContext,
PVOID Argument1,
PVOID Argument2
)
{
PWNODE_SINGLE_INSTANCE wnode = (PWNODE_SINGLE_INSTANCE)Argument1;
PUCHAR ptmp;
UNICODE_STRING ProtocolName, MiniportName;
DBGPRINT_RAW(DBG_COMP_PNP, DBG_LEVEL_INFO,
("==>ndisBindUnbindCallback\n"));
if (wnode != NULL)
{
ptmp = (PUCHAR)wnode + wnode->DataBlockOffset;
RtlInitUnicodeString(&ProtocolName, (PWCHAR)ptmp);
ptmp += ProtocolName.Length + sizeof(WCHAR);
RtlInitUnicodeString(&MiniportName, (PWCHAR)ptmp);
ndisDbgPrintUnicodeString(&ProtocolName);
DbgPrint("\n");
ndisDbgPrintUnicodeString(&MiniportName);
DbgPrint("\n");
DBGPRINT_RAW(DBG_COMP_PNP, DBG_LEVEL_INFO,
("<==ndisBindUnbindCallback\n"));
}
}
#endif