3794 lines
107 KiB
C
3794 lines
107 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
|
|
|
|
// #include "ndis.tmh"
|
|
|
|
//
|
|
// 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"
|
|
#define GET_TEXT_1(_T) #_T
|
|
#define GET_TEXT(_T) GET_TEXT_1(_T)
|
|
|
|
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;
|
|
|
|
UNREFERENCED_PARAMETER(RegistryPath);
|
|
|
|
|
|
// WPP_INIT_TRACING(DriverObject, RegistryPath);
|
|
|
|
// LOG_INFO("==>Ndis: DriverEntry");
|
|
|
|
NdisInitializeString(&ndisBuildDate, (PUCHAR)__DATE__);
|
|
NdisInitializeString(&ndisBuildTime, (PUCHAR)__TIME__);
|
|
NdisInitializeString(&ndisBuiltBy, (PUCHAR)GET_TEXT(BUILT_BY));
|
|
|
|
ndisDriverObject = DriverObject;
|
|
|
|
//
|
|
// Create the device object.
|
|
//
|
|
RtlInitUnicodeString(&DeviceName, NDIS_DEVICE_NAME);
|
|
|
|
//1 in Longhorn, number of processors may vary during a session
|
|
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,
|
|
TRUE);
|
|
|
|
//1 check for status
|
|
Status = CreateDeviceDriverSecurityDescriptor(DriverObject, TRUE, NULL);
|
|
Status = CreateDeviceDriverSecurityDescriptor(DriverObject->DeviceObject, TRUE, NULL);
|
|
Status = CreateDeviceDriverSecurityDescriptor(ndisDeviceObject, TRUE, NULL);
|
|
|
|
|
|
|
|
//
|
|
// 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))
|
|
{
|
|
//1 do more error processing here
|
|
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
|
|
|
|
#ifdef NDIS_LOG_ABORTED_REQUESTS
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
NdisZeroMemory (&ndisAbortedRequests[i], sizeof (NDIS_REQUEST));
|
|
}
|
|
#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 | OBJ_KERNEL_HANDLE,
|
|
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 | OBJ_KERNEL_HANDLE,
|
|
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);
|
|
|
|
|
|
//
|
|
// create an ACL for all users
|
|
//
|
|
AllUsersAclRead = ndisCreateAcl(TRUE, // Admins
|
|
TRUE, //LocalSystem
|
|
TRUE, //LocalService
|
|
TRUE, //NetworkService
|
|
TRUE, //NetConfigOps
|
|
TRUE, //Users
|
|
GENERIC_READ | WMIGUID_QUERY
|
|
);
|
|
|
|
ASSERT(AllUsersAclRead != NULL);
|
|
|
|
AllUsersAclWrite = ndisCreateAcl(TRUE, // Admins
|
|
TRUE, //LocalSystem
|
|
TRUE, //LocalService
|
|
TRUE, //NetworkService
|
|
TRUE, //NetConfigOps
|
|
TRUE, //Users
|
|
GENERIC_WRITE | WMIGUID_SET
|
|
);
|
|
|
|
ASSERT(AllUsersAclWrite != NULL);
|
|
|
|
AllUsersAclReadWrite = ndisCreateAcl(TRUE, // Admins
|
|
TRUE, //LocalSystem
|
|
TRUE, //LocalService
|
|
TRUE, //NetworkService
|
|
TRUE, //NetConfigOps
|
|
TRUE, //Users
|
|
GENERIC_READ | GENERIC_WRITE | WMIGUID_QUERY | WMIGUID_SET
|
|
);
|
|
ASSERT(AllUsersAclReadWrite != NULL);
|
|
|
|
AllUsersAclNotification = ndisCreateAcl(TRUE, // Admins
|
|
TRUE, //LocalSystem
|
|
TRUE, //LocalService
|
|
TRUE, //NetworkService
|
|
TRUE, //NetConfigOps
|
|
TRUE, //Users
|
|
SYNCHRONIZE | WMIGUID_NOTIFICATION
|
|
);
|
|
|
|
|
|
ASSERT(AllUsersAclNotification != NULL);
|
|
|
|
|
|
//
|
|
// create an ACL for admin types
|
|
//
|
|
AdminsAcl = ndisCreateAcl(TRUE, // Admins
|
|
TRUE, //LocalSystem
|
|
TRUE, //LocalService
|
|
TRUE, //NetworkService
|
|
TRUE, //NetConfigOps
|
|
FALSE, //Users
|
|
GENERIC_READ | GENERIC_WRITE | WMIGUID_QUERY | WMIGUID_SET
|
|
);
|
|
|
|
ASSERT(AdminsAcl != NULL);
|
|
|
|
//
|
|
// Create an SD for All Users
|
|
//
|
|
Status = ndisCreateGenericSD(AllUsersAclRead, AllUsersReadSecurityDescriptor);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Status = ndisCreateGenericSD(AllUsersAclWrite, AllUsersWriteSecurityDescriptor);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Status = ndisCreateGenericSD(AllUsersAclReadWrite, AllUsersReadWriteSecurityDescriptor);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Status = ndisCreateGenericSD(AllUsersAclNotification, AllUsersNotificationSecurityDescriptor);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Status = ndisCreateGenericSD(AdminsAcl, AdminsSecurityDescriptor);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
/*
|
|
|
|
//
|
|
// set the security descriptor for all known guids
|
|
//
|
|
for (i = 0; i < sizeof(ndisSupportedGuids)/sizeof(NDIS_GUID); i++)
|
|
{
|
|
Status = ndisSetWmiSecurity(&ndisSupportedGuids[i]);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DbgPrint("Successfully added ndisSupportedGuids[%ld].\n", i);
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("setting security failed for %ld\n.", i);
|
|
}
|
|
}
|
|
for (i = 0; i < sizeof(ndisCoSupportedGuids)/sizeof(NDIS_GUID); i++)
|
|
{
|
|
Status = ndisSetWmiSecurity(&ndisCoSupportedGuids[i]);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DbgPrint("Successfully added ndisCoSupportedGuids[%ld].\n", i);
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("setting security failed for ndisCoSupportedGuids[%ld].\n", i);
|
|
}
|
|
}
|
|
for (i = 0; i < sizeof(ndisMediaSupportedGuids)/sizeof(NDIS_GUID); i++)
|
|
{
|
|
Status = ndisSetWmiSecurity(&ndisMediaSupportedGuids[i]);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DbgPrint("Successfully added ndisMediaSupportedGuids[%ld].\n", i);
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("setting security failed for ndisMediaSupportedGuids[%ld].\n", i);
|
|
}
|
|
}
|
|
for (i = 0; i < sizeof(ndisStatusSupportedGuids)/sizeof(NDIS_GUID); i++)
|
|
{
|
|
Status = ndisSetWmiSecurity(&ndisStatusSupportedGuids[i]);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DbgPrint("Successfully added ndisStatusSupportedGuids[%ld].\n", i);
|
|
}
|
|
else
|
|
{
|
|
DbgPrint("setting security failed for ndisStatusSupportedGuids[%ld].\n", i);
|
|
}
|
|
}
|
|
|
|
*/
|
|
|
|
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 < NDIS_MAX_CPU_COUNT) && (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;
|
|
|
|
|
|
//1 check for an upper bound on # of stack locations.
|
|
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:
|
|
|
|
--*/
|
|
{
|
|
UNREFERENCED_PARAMETER(EntryContext);
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(ValueLength);
|
|
UNREFERENCED_PARAMETER(ValueName);
|
|
|
|
//
|
|
// 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);
|
|
#else
|
|
UNREFERENCED_PARAMETER(ValueName);
|
|
#endif
|
|
UNREFERENCED_PARAMETER(EntryContext);
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(ValueLength);
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
UNREFERENCED_PARAMETER(pDeviceObject);
|
|
|
|
PAGED_CODE( );
|
|
|
|
#if defined(_WIN64)
|
|
if (IoIs32bitProcess(pIrp))
|
|
{
|
|
pIrp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
#endif
|
|
|
|
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_DEVICE_OBJECT_OPEN_CONTEXT OpenContext;
|
|
PNDIS_PNP_OPERATION PnPOp;
|
|
PNDIS_ENUM_INTF EnumIntf;
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
UNICODE_STRING Device;
|
|
ULONG Method;
|
|
PVOID pBuf;
|
|
UINT iBufLen, oBufLen;
|
|
UINT OutputLength = 0;
|
|
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;
|
|
|
|
//
|
|
// check to make sure the input buffer is big enough to
|
|
// have all the information that NDIS_PNP_OPERATION structure
|
|
// claims to contain.
|
|
//
|
|
if ((iBufLen < sizeof(NDIS_PNP_OPERATION)) ||
|
|
(iBufLen < (sizeof(NDIS_PNP_OPERATION) +
|
|
PnPOp->LowerComponent.MaximumLength +
|
|
PnPOp->UpperComponent.MaximumLength +
|
|
PnPOp->ReConfigBufferSize +
|
|
PnPOp->BindList.MaximumLength
|
|
)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// 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 (oBufLen >= sizeof(NDIS_ENUM_INTF))
|
|
{
|
|
EnumIntf = (PNDIS_ENUM_INTF)pBuf;
|
|
Status = ndisEnumerateInterfaces(pBuf, oBufLen, &OutputLength);
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
pIrp->IoStatus.Information = OutputLength;
|
|
|
|
break;
|
|
|
|
case IOCTL_NDIS_GET_VERSION:
|
|
if (oBufLen < sizeof(UINT))
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
*((PUINT)pBuf) = NdisGetVersion();
|
|
OutputLength = sizeof(UINT);
|
|
if (oBufLen >= 2 * sizeof(UINT))
|
|
{
|
|
*((PUINT)pBuf + 1) = (UINT)ndisChecked;
|
|
OutputLength += sizeof(UINT);
|
|
}
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
pIrp->IoStatus.Information = OutputLength;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ASSERT (CURRENT_IRQL < DISPATCH_LEVEL);
|
|
|
|
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:
|
|
//1 for BIND_LIST, validate the buffer to have the correct format
|
|
Status = ndisHandleProtocolReconfigNotification(Device,
|
|
Protocol,
|
|
PnPOp->ReConfigBufferPtr,
|
|
PnPOp->ReConfigBufferSize,
|
|
PnPOp->Operation);
|
|
break;
|
|
|
|
case UNLOAD:
|
|
Status = ndisHandleProtocolUnloadNotification(Protocol);
|
|
break;
|
|
|
|
//1 check to see when this is called
|
|
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!
|
|
|
|
--*/
|
|
{
|
|
//1 check to see if this routine should return back the status of attempted 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, FALSE);
|
|
}
|
|
|
|
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;
|
|
//1 change the name of fPartial variable. what this does is to keep track
|
|
//1 of whether or not we are running our partial search for the first
|
|
//1 time or not.
|
|
BOOLEAN fPartial = FALSE;
|
|
KIRQL OldIrql;
|
|
|
|
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;
|
|
}
|
|
|
|
//1 add comments what this loop does
|
|
do
|
|
{
|
|
//1 add comments what this call does
|
|
Open = ndisMapOpenByName(DeviceName, Protocol, 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, FALSE);
|
|
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;
|
|
|
|
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
|
|
ndisMDereferenceOpen(Open);
|
|
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
|
|
|
|
Status = ndisUnbindProtocol(Open, Protocol, Miniport, TRUE);
|
|
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
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);
|
|
OPEN_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, FALSE);
|
|
}
|
|
|
|
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 change in their configuration -or-
|
|
their bind list.
|
|
This routine can also be called to notify protocols of a change in 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_MINIPORT_BLOCK Miniport = NULL;
|
|
PNDIS_OPEN_BLOCK Open = NULL;
|
|
NET_PNP_EVENT NetPnpEvent;
|
|
PNDIS_PNP_EVENT_RESERVED EventReserved;
|
|
KEVENT Event;
|
|
BOOLEAN fPartial = FALSE;
|
|
BOOLEAN DeRefOpen = FALSE;
|
|
BOOLEAN DeRefMiniport = 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);
|
|
|
|
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, FALSE);
|
|
if (!fPartial)
|
|
{
|
|
fPartial = TRUE;
|
|
Protocol = NULL;
|
|
}
|
|
Status = ndisReferenceProtocolByName(ProtocolName, &Protocol, TRUE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DeRefOpen = TRUE;
|
|
DeRefMiniport = TRUE;
|
|
Miniport = Open->MiniportHandle;
|
|
}
|
|
} 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));
|
|
Status = STATUS_SUCCESS;
|
|
switch (Operation)
|
|
{
|
|
case RECONFIGURE:
|
|
NetPnpEvent.NetEvent = NetEventReconfigure;
|
|
break;
|
|
|
|
case BIND_LIST:
|
|
NetPnpEvent.NetEvent = NetEventBindList;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
if (Status == STATUS_INVALID_DEVICE_REQUEST)
|
|
{
|
|
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 (Status == NDIS_STATUS_PENDING)
|
|
{
|
|
//
|
|
// Wait for completion.
|
|
//
|
|
WAIT_FOR_PROTOCOL(Protocol, &Event);
|
|
|
|
//
|
|
// Get the completion status.
|
|
//
|
|
Status = EventReserved->Status;
|
|
}
|
|
|
|
//
|
|
// IPX may return NDIS_STATUS_NOT_ACCEPTED to ask NDIS to
|
|
// Unbind/Bind the adapter. In this case, Open cannot be NULL.
|
|
//
|
|
if (Status != NDIS_STATUS_NOT_ACCEPTED)
|
|
{
|
|
if (Open)
|
|
{
|
|
ACQUIRE_SPIN_LOCK(&Open->SpinLock, &OldIrql);
|
|
OPEN_CLEAR_FLAG(Open, fMINIPORT_OPEN_PROCESSING);
|
|
RELEASE_SPIN_LOCK(&Open->SpinLock, OldIrql);
|
|
}
|
|
RELEASE_PROT_MUTEX(Protocol);
|
|
break;
|
|
|
|
}
|
|
|
|
RELEASE_PROT_MUTEX(Protocol);
|
|
|
|
ASSERT(DeRefOpen);
|
|
//
|
|
// Ndis need to Unbind/Bind the adapter for the protocol(Especially for IPX)
|
|
//
|
|
ACQUIRE_SPIN_LOCK(&Open->SpinLock, &OldIrql);
|
|
|
|
//
|
|
// If the open has gotten a unbind anyway, just return success and don't
|
|
// need to unbind/bind.
|
|
//
|
|
if (OPEN_TEST_FLAG(Open, fMINIPORT_OPEN_UNBINDING | fMINIPORT_OPEN_CLOSING))
|
|
{
|
|
RELEASE_SPIN_LOCK(&Open->SpinLock, OldIrql);
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
//
|
|
// Going to Unbind the adaper
|
|
//
|
|
OPEN_SET_FLAG(Open, fMINIPORT_OPEN_UNBINDING | fMINIPORT_OPEN_DONT_FREE);
|
|
|
|
RELEASE_SPIN_LOCK(&Open->SpinLock, OldIrql);
|
|
|
|
|
|
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
|
|
ndisMDereferenceOpen(Open);
|
|
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
|
|
|
|
DeRefOpen = FALSE;
|
|
|
|
Status = ndisUnbindProtocol(Open, Protocol, Miniport, FALSE);
|
|
|
|
//
|
|
// If cannot unbind the adapter
|
|
//
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
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);
|
|
OPEN_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);
|
|
break;
|
|
}
|
|
//
|
|
// Then bind the adapter again.
|
|
//
|
|
ndisCheckAdapterBindings(Miniport, Protocol);
|
|
|
|
} while (FALSE);
|
|
|
|
|
|
if (DeRefOpen)
|
|
{
|
|
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
|
|
ndisMDereferenceOpen(Open);
|
|
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
|
|
}
|
|
|
|
if (Protocol != NULL)
|
|
{
|
|
ndisDereferenceProtocol(Protocol, FALSE);
|
|
}
|
|
|
|
if (DeRefMiniport)
|
|
{
|
|
MINIPORT_DECREMENT_REF(Miniport);
|
|
}
|
|
|
|
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, FALSE);
|
|
|
|
if (Protocol->ProtocolCharacteristics.UnloadHandler != NULL)
|
|
{
|
|
//1 investigate if this can be called with open bindings
|
|
(Protocol->ProtocolCharacteristics.UnloadHandler)();
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
DBGPRINT_RAW(DBG_COMP_PROTOCOL, DBG_LEVEL_INFO,
|
|
("<==ndisHandleProtocolUnloadNotification\n"));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//1 when do we hit this?
|
|
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,
|
|
IN PUINT OutputLength
|
|
)
|
|
{
|
|
PNDIS_MINIPORT_BLOCK Miniport;
|
|
PNDIS_M_DRIVER_BLOCK MiniBlock;
|
|
PNDIS_INTERFACE Interface;
|
|
UINT SpaceLeft;
|
|
UINT SpaceNeeded;
|
|
PUCHAR pBuf;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
KIRQL OldIrql;
|
|
|
|
do
|
|
{
|
|
*OutputLength = 0;
|
|
if (BufferLength < sizeof(NDIS_ENUM_INTF))
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
SpaceLeft = BufferLength - sizeof(NDIS_ENUM_INTF);
|
|
|
|
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 ++;
|
|
}
|
|
#if 0
|
|
else
|
|
{
|
|
//
|
|
// we should report the cases that buffer was too small
|
|
//
|
|
//1 for .NET leave the behavior as before (return success)
|
|
//1 because some apps do not handle this properly
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_DPC(&MiniBlock->Ref.SpinLock);
|
|
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// if we failed, get out
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&ndisMiniDriverListLock, OldIrql);
|
|
|
|
//
|
|
// since we zero'ed out the entire output buffer and started
|
|
// writing to it from the end, this should be the original output buffer
|
|
// length
|
|
//
|
|
*OutputLength = BufferLength;
|
|
}while (FALSE);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ndisUnbindProtocol(
|
|
IN PNDIS_OPEN_BLOCK Open,
|
|
IN PNDIS_PROTOCOL_BLOCK Protocol,
|
|
IN PNDIS_MINIPORT_BLOCK Miniport,
|
|
IN BOOLEAN Notify
|
|
)
|
|
/*+++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
None
|
|
|
|
---*/
|
|
{
|
|
NDIS_STATUS Status = STATUS_SUCCESS;
|
|
NDIS_BIND_CONTEXT UnbindContext;
|
|
PKEVENT CloseCompleteEvent = NULL;
|
|
PNDIS_OPEN_BLOCK TmpOpen = NULL;
|
|
KIRQL OldIrql;
|
|
BOOLEAN fDerefProtocol = FALSE;
|
|
BOOLEAN FreeOpen = FALSE;
|
|
|
|
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;
|
|
}
|
|
|
|
WAIT_FOR_PROTO_MUTEX(Protocol);
|
|
|
|
do
|
|
{
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
ASSERT(OPEN_TEST_FLAG(Open, fMINIPORT_OPEN_UNBINDING));
|
|
CloseCompleteEvent = Open->CloseCompleteEvent;
|
|
|
|
//
|
|
// wait for all AF notifications to go through
|
|
//
|
|
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IS_CO))
|
|
{
|
|
KEVENT AfNotifyCompleteEvent;
|
|
|
|
INITIALIZE_EVENT(&AfNotifyCompleteEvent);
|
|
|
|
//1 check to see if we need to get the Open Spinlock here
|
|
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_PROT_MUTEX(Protocol);
|
|
|
|
if (TmpOpen != NULL)
|
|
{
|
|
ACQUIRE_SPIN_LOCK(&Open->SpinLock, &OldIrql);
|
|
//
|
|
// did the close routine get our message not to free the open structure?
|
|
//
|
|
if (OPEN_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
|
|
//
|
|
OPEN_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, FALSE);
|
|
}
|
|
|
|
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;
|
|
UINT Depth = 1;
|
|
|
|
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
|
|
{
|
|
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 fUnbinding
|
|
)
|
|
/*
|
|
Routine Description:
|
|
ndisMapOpenByName searches a protocol's open queue and tries to find an
|
|
open block that its RootDevice name matches Devicename passed to this function.
|
|
if the Open is found, miniport for that open is referenced. if we are -not-
|
|
trying to unbind the open, we will reference it.
|
|
|
|
Arguments:
|
|
DeviceName: RootDevice name of the open.
|
|
Protocol: protocol block to search.
|
|
fUnbinding: whether we are searching for the open so we can close it. if that is
|
|
the case, then some additional checks will be perfomred and the some flags on open
|
|
will be set.
|
|
|
|
Return Value:
|
|
Open block or NULL.
|
|
|
|
*/
|
|
{
|
|
UNICODE_STRING UpcaseDevice;
|
|
PNDIS_OPEN_BLOCK Open, tmpOpen;
|
|
PNDIS_MINIPORT_BLOCK Miniport;
|
|
KIRQL OldIrql;
|
|
BOOLEAN DeRefOpen = FALSE;
|
|
|
|
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 (OPEN_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
|
|
{
|
|
|
|
M_OPEN_INCREMENT_REF_INTERLOCKED(Open);
|
|
OPEN_SET_FLAG(Open, fMINIPORT_OPEN_UNBINDING |
|
|
fMINIPORT_OPEN_DONT_FREE |
|
|
fMINIPORT_OPEN_PROCESSING);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (OPEN_TEST_FLAG(Open, fMINIPORT_OPEN_PROCESSING))
|
|
{
|
|
Open = NULL;
|
|
}
|
|
else
|
|
{
|
|
M_OPEN_INCREMENT_REF_INTERLOCKED(Open);
|
|
OPEN_SET_FLAG(Open, fMINIPORT_OPEN_PROCESSING);
|
|
}
|
|
}
|
|
RELEASE_SPIN_LOCK_DPC(&tmpOpen->SpinLock);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&Protocol->Ref.SpinLock, OldIrql);
|
|
|
|
if (Open != NULL)
|
|
{
|
|
|
|
Miniport = Open->MiniportHandle;
|
|
if (!MINIPORT_INCREMENT_REF(Miniport))
|
|
{
|
|
if (fUnbinding)
|
|
{
|
|
OPEN_CLEAR_FLAG(Open, fMINIPORT_OPEN_UNBINDING |
|
|
fMINIPORT_OPEN_PROCESSING |
|
|
fMINIPORT_OPEN_DONT_FREE);
|
|
}
|
|
else
|
|
{
|
|
OPEN_CLEAR_FLAG(Open, fMINIPORT_OPEN_PROCESSING);
|
|
}
|
|
|
|
DeRefOpen = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DeRefOpen = FALSE;
|
|
}
|
|
}
|
|
|
|
FREE_POOL(UpcaseDevice.Buffer);
|
|
|
|
if (DeRefOpen)
|
|
{
|
|
Miniport = Open->MiniportHandle;
|
|
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
|
|
ndisMDereferenceOpen(Open);
|
|
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
|
|
Open = NULL;
|
|
}
|
|
|
|
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;
|
|
|
|
//1 Context parameter probably should be use to verify data type
|
|
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, FALSE);
|
|
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, FALSE);
|
|
|
|
} 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;
|
|
UNREFERENCED_PARAMETER(OpenStatus);
|
|
|
|
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
|
|
NdisDeregisterTdiCallBack(
|
|
VOID
|
|
)
|
|
|
|
{
|
|
ndisTdiRegisterCallback = NULL;
|
|
ndisTdiPnPHandler = NULL;
|
|
}
|
|
|
|
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 = NULL, BindDevice = NULL;
|
|
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;
|
|
|
|
UNREFERENCED_PARAMETER(CallBackContext);
|
|
|
|
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;
|
|
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);
|
|
|
|
}
|
|
else
|
|
{
|
|
NextMiniBlock = MiniBlock->NextDriver;
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
//1 add function header
|
|
|
|
PVOID
|
|
NdisGetRoutineAddress(
|
|
IN PNDIS_STRING NdisRoutineName
|
|
)
|
|
{
|
|
PVOID Address;
|
|
ANSI_STRING AnsiString;
|
|
NTSTATUS Status;
|
|
|
|
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|
|
|
Status = RtlUnicodeStringToAnsiString(&AnsiString,
|
|
(PUNICODE_STRING)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;
|
|
|
|
//1 investigate using strncmp
|
|
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
|
|
|