You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1256 lines
37 KiB
1256 lines
37 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
|
PURPOSE.
|
|
|
|
Module Name:
|
|
|
|
Miniport.C
|
|
|
|
Abstract:
|
|
|
|
The purpose of this sample is to illustrate functionality of a deserialized
|
|
NDIS miniport driver without requiring a physical network adapter. This
|
|
sample is based on E100BEX sample present in the DDK. It is basically a
|
|
simplified version of E100bex driver. The driver can be installed either
|
|
manually using Add Hardware wizard as a root enumerated virtual miniport
|
|
driver or on a virtual bus (like toaster bus). Since the driver does not
|
|
interact with any hardware, it makes it very easy to understand the miniport
|
|
interface and the usage of various NDIS functions without the clutter of
|
|
hardware specific code normally found in a fully functional driver.
|
|
|
|
This sample provides an example of minimal driver intended for education
|
|
purposes. Neither the driver or it's sample test programs are intended
|
|
for use in a production environment.
|
|
|
|
Author: Eliyas Yakub (Nov 20th 2002)
|
|
|
|
Revision History:
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
#include "miniport.h"
|
|
|
|
#pragma NDIS_INIT_FUNCTION(DriverEntry)
|
|
#pragma NDIS_PAGEABLE_FUNCTION(MPInitialize)
|
|
#pragma NDIS_PAGEABLE_FUNCTION(MPHalt)
|
|
#pragma NDIS_PAGEABLE_FUNCTION(MPUnload)
|
|
|
|
#ifdef NDIS51_MINIPORT
|
|
#pragma NDIS_PAGEABLE_FUNCTION(MPPnPEventNotify)
|
|
#endif
|
|
|
|
MP_GLOBAL_DATA GlobalData;
|
|
INT MPDebugLevel = MP_INFO;
|
|
NDIS_HANDLE NdisWrapperHandle;
|
|
|
|
NDIS_STATUS
|
|
DriverEntry(
|
|
PVOID DriverObject,
|
|
PVOID RegistryPath)
|
|
/*++
|
|
Routine Description:
|
|
|
|
In the context of its DriverEntry function, a miniport driver associates
|
|
itself with NDIS, specifies the NDIS version that it is using, and
|
|
registers its entry points.
|
|
|
|
|
|
Arguments:
|
|
PVOID DriverObject - pointer to the driver object.
|
|
PVOID RegistryPath - pointer to the driver registry path.
|
|
|
|
Return Value:
|
|
|
|
NDIS_STATUS_xxx code
|
|
|
|
--*/
|
|
{
|
|
|
|
NDIS_STATUS Status;
|
|
NDIS_MINIPORT_CHARACTERISTICS MPChar;
|
|
|
|
DEBUGP(MP_TRACE, ("---> DriverEntry built on "__DATE__" at "__TIME__ "\n"));
|
|
|
|
//
|
|
// Associate the miniport driver with NDIS by calling the
|
|
// NdisMInitializeWrapper. This function allocates a structure
|
|
// to represent this association, stores the miniport driver-
|
|
// specific information that the NDIS Library needs in this
|
|
// structure, and returns NdisWrapperHandle. The driver must retain and
|
|
// pass this handle to NdisMRegisterMiniport when it registers its entry
|
|
// points. NDIS will use NdisWrapperHandle to identify the miniport driver.
|
|
// The miniport driver must retain this handle but it should never attempt
|
|
// to access or interpret this handle.
|
|
//
|
|
NdisMInitializeWrapper(
|
|
&NdisWrapperHandle,
|
|
DriverObject,
|
|
RegistryPath,
|
|
NULL
|
|
);
|
|
//
|
|
// Fill in the Miniport characteristics structure with the version numbers
|
|
// and the entry points for driver-supplied MiniportXxx
|
|
//
|
|
NdisZeroMemory(&MPChar, sizeof(MPChar));
|
|
|
|
//
|
|
// The NDIS version number, in addition to being included in
|
|
// NDIS_MINIPORT_CHARACTERISTICS, must also be specified when the
|
|
// miniport driver source code is compiled.
|
|
//
|
|
MPChar.MajorNdisVersion = MP_NDIS_MAJOR_VERSION;
|
|
MPChar.MinorNdisVersion = MP_NDIS_MINOR_VERSION;
|
|
|
|
MPChar.InitializeHandler = MPInitialize;
|
|
MPChar.HaltHandler = MPHalt;
|
|
|
|
MPChar.SetInformationHandler = MPSetInformation;
|
|
MPChar.QueryInformationHandler = MPQueryInformation;
|
|
|
|
MPChar.SendPacketsHandler = MPSendPackets;
|
|
MPChar.ReturnPacketHandler = MPReturnPacket;
|
|
|
|
MPChar.ResetHandler = MPReset;
|
|
MPChar.CheckForHangHandler = MPCheckForHang; //optional
|
|
|
|
MPChar.AllocateCompleteHandler = MPAllocateComplete;//optional
|
|
|
|
MPChar.DisableInterruptHandler = MPDisableInterrupt; //optional
|
|
MPChar.EnableInterruptHandler = MPEnableInterrupt; //optional
|
|
MPChar.HandleInterruptHandler = MPHandleInterrupt;
|
|
MPChar.ISRHandler = MPIsr;
|
|
|
|
#ifdef NDIS51_MINIPORT
|
|
MPChar.CancelSendPacketsHandler = MPCancelSendPackets;
|
|
MPChar.PnPEventNotifyHandler = MPPnPEventNotify;
|
|
MPChar.AdapterShutdownHandler = MPShutdown;
|
|
#endif
|
|
|
|
|
|
DEBUGP(MP_LOUD, ("Calling NdisMRegisterMiniport...\n"));
|
|
|
|
//
|
|
// Registers miniport's entry points with the NDIS library as the first
|
|
// step in NIC driver initialization. The NDIS will call the
|
|
// MiniportInitialize when the device is actually started by the PNP
|
|
// manager.
|
|
//
|
|
Status = NdisMRegisterMiniport(
|
|
NdisWrapperHandle,
|
|
&MPChar,
|
|
sizeof(NDIS_MINIPORT_CHARACTERISTICS));
|
|
if (Status != NDIS_STATUS_SUCCESS) {
|
|
|
|
DEBUGP(MP_ERROR, ("Status = 0x%08x\n", Status));
|
|
NdisTerminateWrapper(NdisWrapperHandle, NULL);
|
|
|
|
} else {
|
|
//
|
|
// Initialize the global variables. The ApaterList in the
|
|
// GloablData structure is used to track the multiple instances
|
|
// of the same adapter. Make sure you do that before registering
|
|
// the unload handler.
|
|
//
|
|
NdisAllocateSpinLock(&GlobalData.Lock);
|
|
NdisInitializeListHead(&GlobalData.AdapterList);
|
|
//
|
|
// Register an Unload handler for global data cleanup. The unload handler
|
|
// has a more global scope, whereas the scope of the MiniportHalt function
|
|
// is restricted to a particular miniport instance.
|
|
//
|
|
NdisMRegisterUnloadHandler(NdisWrapperHandle, MPUnload);
|
|
|
|
}
|
|
|
|
|
|
DEBUGP(MP_TRACE, ("<--- DriverEntry\n"));
|
|
return Status;
|
|
|
|
}
|
|
|
|
NDIS_STATUS
|
|
MPInitialize(
|
|
OUT PNDIS_STATUS OpenErrorStatus,
|
|
OUT PUINT SelectedMediumIndex,
|
|
IN PNDIS_MEDIUM MediumArray,
|
|
IN UINT MediumArraySize,
|
|
IN NDIS_HANDLE MiniportAdapterHandle,
|
|
IN NDIS_HANDLE WrapperConfigurationContext)
|
|
/*++
|
|
Routine Description:
|
|
|
|
The MiniportInitialize function is a required function that sets up a
|
|
NIC (or virtual NIC) for network I/O operations, claims all hardware
|
|
resources necessary to the NIC in the registry, and allocates resources
|
|
the driver needs to carry out network I/O operations.
|
|
|
|
MiniportInitialize runs at IRQL = PASSIVE_LEVEL.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
NDIS_STATUS_xxx code
|
|
|
|
--*/
|
|
{
|
|
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
|
|
PMP_ADAPTER Adapter=NULL;
|
|
NDIS_HANDLE ConfigurationHandle;
|
|
UINT index;
|
|
|
|
DEBUGP(MP_TRACE, ("---> MPInitialize\n"));
|
|
|
|
do {
|
|
//
|
|
// Check to see if our media type exists in an array of supported
|
|
// media types provided by NDIS.
|
|
//
|
|
for(index = 0; index < MediumArraySize; ++index)
|
|
{
|
|
if(MediumArray[index] == NIC_MEDIA_TYPE) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(index == MediumArraySize)
|
|
{
|
|
DEBUGP(MP_ERROR, ("Expected media is not in MediumArray.\n"));
|
|
Status = NDIS_STATUS_UNSUPPORTED_MEDIA;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set the index value as the selected medium for our device.
|
|
//
|
|
*SelectedMediumIndex = index;
|
|
|
|
//
|
|
// Allocate adapter context structure and initialize all the
|
|
// memory resources for sending and receiving packets.
|
|
//
|
|
Status = NICAllocAdapter(&Adapter);
|
|
if(Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
MP_INC_REF(Adapter);
|
|
|
|
//
|
|
// NdisMGetDeviceProperty function enables us to get the:
|
|
// PDO - created by the bus driver to represent our device.
|
|
// FDO - created by NDIS to represent our miniport as a function driver.
|
|
// NextDeviceObject - deviceobject of another driver (filter)
|
|
// attached to us at the bottom.
|
|
// In a pure NDIS miniport driver, there is no use for this
|
|
// information, but a NDISWDM driver would need to know this so that it
|
|
// can transfer packets to the lower WDM stack using IRPs.
|
|
//
|
|
NdisMGetDeviceProperty(MiniportAdapterHandle,
|
|
&Adapter->Pdo,
|
|
&Adapter->Fdo,
|
|
&Adapter->NextDeviceObject,
|
|
NULL,
|
|
NULL);
|
|
|
|
Adapter->AdapterHandle = MiniportAdapterHandle;
|
|
|
|
//
|
|
// Read Advanced configuration information from the registry
|
|
//
|
|
|
|
Status = NICReadRegParameters(Adapter, WrapperConfigurationContext);
|
|
if(Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Inform NDIS about significant features of the NIC. A
|
|
// MiniportInitialize function must call NdisMSetAttributesEx
|
|
// (or NdisMSetAttributes) before calling any other NdisMRegisterXxx
|
|
// or NdisXxx function that claims hardware resources. If your
|
|
// hardware supports busmaster DMA, you must specify NDIS_ATTRIBUTE_BUS_MASTER.
|
|
// If this is NDIS51 miniport, it should use safe APIs. But if this
|
|
// is NDIS 5.0, the driver claim to use safe APIs by setting
|
|
// NDIS_ATTRIBUTE_USES_SAFE_BUFFER_APIS
|
|
//
|
|
NdisMSetAttributesEx(
|
|
MiniportAdapterHandle,
|
|
(NDIS_HANDLE) Adapter,
|
|
0,
|
|
#ifdef NDIS51_MINIPORT
|
|
NDIS_ATTRIBUTE_DESERIALIZE, // NDIS does not maintain a send-packet queue
|
|
#else
|
|
NDIS_ATTRIBUTE_DESERIALIZE|
|
|
NDIS_ATTRIBUTE_USES_SAFE_BUFFER_APIS,
|
|
#endif
|
|
NIC_INTERFACE_TYPE);
|
|
|
|
//
|
|
// Get the Adapter Resources & Initialize the hardware.
|
|
//
|
|
|
|
Status = NICInitializeAdapter(Adapter, WrapperConfigurationContext);
|
|
if(Status != NDIS_STATUS_SUCCESS) {
|
|
Status = NDIS_STATUS_FAILURE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Setup a timer function for Receive Indication
|
|
//
|
|
NdisInitializeTimer(
|
|
&Adapter->RecvTimer,
|
|
(PNDIS_TIMER_FUNCTION)NICIndicateReceiveTimerDpc,
|
|
(PVOID)Adapter);
|
|
|
|
//
|
|
// Setup a timer function for use with our MPReset routine.
|
|
//
|
|
NdisInitializeTimer(
|
|
&Adapter->ResetTimer,
|
|
(PNDIS_TIMER_FUNCTION) NICResetCompleteTimerDpc,
|
|
(PVOID) Adapter);
|
|
|
|
NdisInitializeEvent(&Adapter->RemoveEvent);
|
|
|
|
|
|
} while(FALSE);
|
|
|
|
if(Status == NDIS_STATUS_SUCCESS) {
|
|
//
|
|
// Attach this Adapter to the global list of adapters managed by
|
|
// this driver.
|
|
//
|
|
NICAttachAdapter(Adapter);
|
|
|
|
//
|
|
// Create an IOCTL interface
|
|
//
|
|
NICRegisterDevice();
|
|
}
|
|
else {
|
|
if(Adapter){
|
|
MP_DEC_REF(Adapter);
|
|
NICFreeAdapter(Adapter);
|
|
}
|
|
}
|
|
|
|
DEBUGP(MP_TRACE, ("<--- MPInitialize Status = 0x%08x%\n", Status));
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
MPHalt(
|
|
IN NDIS_HANDLE MiniportAdapterContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Halt handler is called when NDIS receives IRP_MN_STOP_DEVICE,
|
|
IRP_MN_SUPRISE_REMOVE or IRP_MN_REMOVE_DEVICE requests from the
|
|
PNP manager. Here, the driver should free all the resources acquired
|
|
in MiniportInitialize and stop access to the hardware. NDIS will
|
|
not submit any further request once this handler is invoked.
|
|
|
|
1) Free and unmap all I/O resources.
|
|
2) Disable interrupt and deregister interrupt handler.
|
|
3) Deregister shutdown handler regsitered by
|
|
NdisMRegisterAdapterShutdownHandler .
|
|
4) Cancel all queued up timer callbacks.
|
|
5) Finally wait indefinitely for all the outstanding receive
|
|
packets indicated to the protocol to return.
|
|
|
|
MiniportHalt runs at IRQL = PASSIVE_LEVEL.
|
|
|
|
|
|
Arguments:
|
|
|
|
MiniportAdapterContext Pointer to the Adapter
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PMP_ADAPTER Adapter = (PMP_ADAPTER) MiniportAdapterContext;
|
|
BOOLEAN bDone=TRUE;
|
|
BOOLEAN bCancelled;
|
|
LONG nHaltCount = 0, Count;
|
|
|
|
MP_SET_FLAG(Adapter, fMP_ADAPTER_HALT_IN_PROGRESS);
|
|
|
|
DEBUGP(MP_TRACE, ("---> MPHalt\n"));
|
|
|
|
//
|
|
// Call Shutdown handler to disable interrupt and turn the hardware off
|
|
// by issuing a full reset
|
|
//
|
|
#if defined(NDIS50_MINIPORT)
|
|
MPShutdown(MiniportAdapterContext);
|
|
#elif defined(NDIS51_MINIPORT)
|
|
//
|
|
// On XP and later, NDIS notifies our PNP event handler the
|
|
// reason for calling Halt. So before accessing the device, check to see
|
|
// if the device is surprise removed, if so don't bother calling
|
|
// the shutdown handler to stop the hardware because it doesn't exist.
|
|
//
|
|
if(!MP_TEST_FLAG(Adapter, fMP_ADAPTER_SURPRISE_REMOVED)) {
|
|
MPShutdown(MiniportAdapterContext);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Free the packets on SendWaitList
|
|
//
|
|
NICFreeQueuedSendPackets(Adapter);
|
|
|
|
//
|
|
// Cancel the ResetTimer.
|
|
//
|
|
NdisCancelTimer(&Adapter->ResetTimer, &bCancelled);
|
|
|
|
//
|
|
// Cancel the ReceiveIndication Timer.
|
|
//
|
|
NdisCancelTimer(&Adapter->RecvTimer, &bCancelled);
|
|
if(bCancelled) {
|
|
//
|
|
// We are able to cancel a queued Timer. So there is a
|
|
// possibility for the packets to be waiting in the
|
|
// RecvWaitList. So let us free them by calling..
|
|
//
|
|
NICFreeQueuedRecvPackets(Adapter);
|
|
}
|
|
|
|
//
|
|
// Decrement the ref count which was incremented in MPInitialize
|
|
//
|
|
|
|
MP_DEC_REF(Adapter);
|
|
|
|
//
|
|
// Possible non-zero ref counts mean one or more of the following conditions:
|
|
// 1) Reset DPC is still running.
|
|
// 2) Receive Indication DPC is still running.
|
|
//
|
|
|
|
DEBUGP(MP_INFO, ("RefCount=%d --- waiting!\n", MP_GET_REF(Adapter)));
|
|
|
|
NdisWaitEvent(&Adapter->RemoveEvent, 0);
|
|
|
|
while(TRUE)
|
|
{
|
|
bDone = TRUE;
|
|
|
|
//
|
|
// Are all the packets indicated up returned?
|
|
//
|
|
if(Adapter->nBusyRecv)
|
|
{
|
|
DEBUGP(MP_INFO, ("nBusyRecv = %d\n", Adapter->nBusyRecv));
|
|
bDone = FALSE;
|
|
}
|
|
|
|
//
|
|
// Are there any outstanding send packets?
|
|
//
|
|
if(Adapter->nBusySend)
|
|
{
|
|
DEBUGP(MP_INFO, ("nBusySend = %d\n", Adapter->nBusySend));
|
|
bDone = FALSE;
|
|
}
|
|
|
|
if(bDone)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if(++nHaltCount % 100)
|
|
{
|
|
DEBUGP(MP_ERROR, ("Halt timed out!!!\n"));
|
|
DEBUGP(MP_ERROR, ("RecvWaitList = %p\n", &Adapter->RecvWaitList));
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
DEBUGP(MP_INFO, ("MPHalt - waiting ...\n"));
|
|
NdisMSleep(1000);
|
|
}
|
|
|
|
ASSERT(bDone);
|
|
|
|
#ifdef NDIS50_MINIPORT
|
|
//
|
|
// Deregister shutdown handler as it's being halted
|
|
//
|
|
NdisMDeregisterAdapterShutdownHandler(Adapter->AdapterHandle);
|
|
#endif
|
|
|
|
//
|
|
// Unregister the ioctl interface.
|
|
//
|
|
NICDeregisterDevice();
|
|
|
|
NICDetachAdapter(Adapter);
|
|
|
|
NICFreeAdapter(Adapter);
|
|
|
|
DEBUGP(MP_TRACE, ("<--- MPHalt\n"));
|
|
}
|
|
|
|
NDIS_STATUS
|
|
MPReset(
|
|
OUT PBOOLEAN AddressingReset,
|
|
IN NDIS_HANDLE MiniportAdapterContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MiniportReset is a required to issue a hardware reset to the NIC
|
|
and/or to reset the driver's software state.
|
|
|
|
1) The miniport driver can optionally complete any pending
|
|
OID requests. NDIS will submit no further OID requests
|
|
to the miniport driver for the NIC being reset until
|
|
the reset operation has finished. After the reset,
|
|
NDIS will resubmit to the miniport driver any OID requests
|
|
that were pending but not completed by the miniport driver
|
|
before the reset.
|
|
2) A deserialized miniport driver must complete any pending send
|
|
operations. NDIS will not requeue pending send packets for
|
|
a deserialized driver since NDIS does not maintain the send
|
|
queue for such a driver.
|
|
|
|
3) If MiniportReset returns NDIS_STATUS_PENDING, the driver must
|
|
complete the original request subsequently with a call to
|
|
NdisMResetComplete.
|
|
|
|
MiniportReset runs at IRQL = DISPATCH_LEVEL.
|
|
|
|
Arguments:
|
|
|
|
AddressingReset - If multicast or functional addressing information
|
|
or the lookahead size, is changed by a reset,
|
|
MiniportReset must set the variable at AddressingReset
|
|
to TRUE before it returns control. This causes NDIS to
|
|
call the MiniportSetInformation function to restore
|
|
the information.
|
|
|
|
MiniportAdapterContext - Pointer to our adapter
|
|
|
|
Return Value:
|
|
|
|
NDIS_STATUS
|
|
|
|
--*/
|
|
{
|
|
NDIS_STATUS Status;
|
|
PMP_ADAPTER Adapter = (PMP_ADAPTER) MiniportAdapterContext;
|
|
|
|
BOOLEAN bDone = TRUE;
|
|
|
|
DEBUGP(MP_TRACE, ("---> MPReset\n"));
|
|
|
|
do
|
|
{
|
|
ASSERT(!MP_TEST_FLAG(Adapter, fMP_ADAPTER_HALT_IN_PROGRESS));
|
|
|
|
if(MP_TEST_FLAG(Adapter, fMP_RESET_IN_PROGRESS))
|
|
{
|
|
Status = NDIS_STATUS_RESET_IN_PROGRESS;
|
|
break;
|
|
}
|
|
|
|
MP_SET_FLAG(Adapter, fMP_RESET_IN_PROGRESS);
|
|
|
|
//
|
|
// Complete all the queued up send packets
|
|
//
|
|
NICFreeQueuedSendPackets(Adapter);
|
|
|
|
//
|
|
// Check to see if all the packets indicated up are returned.
|
|
//
|
|
if(Adapter->nBusyRecv)
|
|
{
|
|
DEBUGP(MP_INFO, ("nBusyRecv = %d\n", Adapter->nBusyRecv));
|
|
bDone = FALSE;
|
|
}
|
|
|
|
//
|
|
// Are there any send packets in the processes of being
|
|
// transmitted?
|
|
//
|
|
if(Adapter->nBusySend)
|
|
{
|
|
DEBUGP(MP_INFO, ("nBusySend = %d\n", Adapter->nBusySend));
|
|
bDone = FALSE;
|
|
}
|
|
|
|
if(!bDone)
|
|
{
|
|
Adapter->nResetTimerCount = 0;
|
|
//
|
|
// We can't complete the reset request now. So let us queue
|
|
// a timer callback for 500ms and check again whether we can
|
|
// successfully reset the hardware.
|
|
//
|
|
NdisSetTimer(&Adapter->ResetTimer, 500);
|
|
|
|
//
|
|
// By returning NDIS_STATUS_PENDING, we are promising NDIS that
|
|
// we will complete the reset request by calling NdisMResetComplete.
|
|
//
|
|
Status = NDIS_STATUS_PENDING;
|
|
break;
|
|
}
|
|
|
|
*AddressingReset = FALSE;
|
|
MP_CLEAR_FLAG(Adapter, fMP_RESET_IN_PROGRESS);
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
|
|
} while(FALSE);
|
|
|
|
DEBUGP(MP_TRACE, ("<--- MPReset Status = 0x%08x\n", Status));
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
VOID
|
|
NICResetCompleteTimerDpc(
|
|
IN PVOID SystemSpecific1,
|
|
IN PVOID FunctionContext,
|
|
IN PVOID SystemSpecific2,
|
|
IN PVOID SystemSpecific3)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Timer callback function for Reset operation.
|
|
|
|
Arguments:
|
|
|
|
FunctionContext - Pointer to our adapter
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
PMP_ADAPTER Adapter = (PMP_ADAPTER)FunctionContext;
|
|
BOOLEAN bDone = TRUE;
|
|
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
|
|
|
|
DEBUGP(MP_TRACE, ("--> NICResetCompleteTimerDpc\n"));
|
|
|
|
//
|
|
// Increment the ref count on the adapter to prevent the driver from
|
|
// unloding while the DPC is running. The Halt handler waits for the
|
|
// ref count to drop to zero before returning.
|
|
//
|
|
MP_INC_REF(Adapter);
|
|
|
|
//
|
|
// Check to see if all the packets indicated up are returned.
|
|
//
|
|
if(Adapter->nBusyRecv)
|
|
{
|
|
DEBUGP(MP_INFO, ("nBusyRecv = %d\n", Adapter->nBusyRecv));
|
|
bDone = FALSE;
|
|
}
|
|
|
|
//
|
|
// Are there any send packets in the processes of being
|
|
// transmitted?
|
|
//
|
|
if(Adapter->nBusySend)
|
|
{
|
|
DEBUGP(MP_INFO, ("nBusySend = %d\n", Adapter->nBusySend));
|
|
bDone = FALSE;
|
|
}
|
|
|
|
if(!bDone && ++Adapter->nResetTimerCount <= 20)
|
|
{
|
|
//
|
|
// Let us try one more time.
|
|
//
|
|
NdisSetTimer(&Adapter->ResetTimer, 500);
|
|
}
|
|
else
|
|
{
|
|
if(!bDone)
|
|
{
|
|
|
|
//
|
|
// We have tried enough. Something is wrong. Let us
|
|
// just complete the reset request with failure.
|
|
//
|
|
DEBUGP(MP_ERROR, ("Reset timed out!!!\n"));
|
|
DEBUGP(MP_ERROR, ("nBusySend = %d\n", Adapter->nBusySend));
|
|
DEBUGP(MP_ERROR, ("RecvWaitList = %p\n", &Adapter->RecvWaitList));
|
|
DEBUGP(MP_ERROR, ("nBusyRecv = %d\n", Adapter->nBusyRecv));
|
|
|
|
ASSERT(FALSE);
|
|
|
|
Status = NDIS_STATUS_FAILURE;
|
|
}
|
|
|
|
DEBUGP(MP_INFO, ("Done - NdisMResetComplete\n"));
|
|
|
|
MP_CLEAR_FLAG(Adapter, fMP_RESET_IN_PROGRESS);
|
|
NdisMResetComplete(
|
|
Adapter->AdapterHandle,
|
|
Status,
|
|
FALSE);
|
|
|
|
}
|
|
|
|
MP_DEC_REF(Adapter);
|
|
|
|
DEBUGP(MP_TRACE, ("<-- NICResetCompleteTimerDpc Status = 0x%08x\n", Status));
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
MPUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The unload handler is called during driver unload to free up resources
|
|
acquired in DriverEntry. This handler is registered through
|
|
NdisMRegisterUnloadHandler. Note that an unload handler differs from
|
|
a MiniportHalt function in that the unload handler has a more global
|
|
scope, whereas the scope of the MiniportHalt function is restricted
|
|
to a particular miniport driver instance.
|
|
|
|
Runs at IRQL = PASSIVE_LEVEL.
|
|
|
|
Arguments:
|
|
|
|
DriverObject Not used
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DEBUGP(MP_TRACE, ("--> MPUnload\n"));
|
|
|
|
ASSERT(IsListEmpty(&GlobalData.AdapterList));
|
|
NdisFreeSpinLock(&GlobalData.Lock);
|
|
|
|
DEBUGP(MP_TRACE, ("<--- MPUnload\n"));
|
|
}
|
|
|
|
VOID
|
|
MPShutdown(
|
|
IN NDIS_HANDLE MiniportAdapterContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The MiniportShutdown handler restores a NIC to its initial state when
|
|
the system is shut down, whether by the user or because an unrecoverable
|
|
system error occurred. This is to ensure that the NIC is in a known
|
|
state and ready to be reinitialized when the machine is rebooted after
|
|
a system shutdown occurs for any reason, including a crash dump.
|
|
|
|
Here just disable the interrupt and stop the DMA engine.
|
|
Do not free memory resources or wait for any packet transfers
|
|
to complete.
|
|
|
|
|
|
Runs at an arbitrary IRQL <= DIRQL. So do not call any passive-level
|
|
function.
|
|
|
|
Arguments:
|
|
|
|
MiniportAdapterContext Pointer to our adapter
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PMP_ADAPTER Adapter = (PMP_ADAPTER) MiniportAdapterContext;
|
|
|
|
DEBUGP(MP_TRACE, ("---> MPShutdown\n"));
|
|
|
|
DEBUGP(MP_TRACE, ("<--- MPShutdown\n"));
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
MPCheckForHang(
|
|
IN NDIS_HANDLE MiniportAdapterContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The MiniportCheckForHang handler is called to report the state of the
|
|
NIC, or to monitor the responsiveness of an underlying device driver.
|
|
This is an optional function. If this handler is not specified, NDIS
|
|
judges the driver unresponsive when the driver holds
|
|
MiniportQueryInformation or MiniportSetInformation requests for a
|
|
time-out interval (deafult 4 sec), and then calls the driver's
|
|
MiniportReset function. A NIC driver's MiniportInitialize function can
|
|
extend NDIS's time-out interval by calling NdisMSetAttributesEx to
|
|
avoid unnecessary resets.
|
|
|
|
Always runs at IRQL = DISPATCH_LEVEL.
|
|
|
|
Arguments:
|
|
|
|
MiniportAdapterContext Pointer to our adapter
|
|
|
|
Return Value:
|
|
|
|
TRUE NDIS calls the driver's MiniportReset function.
|
|
FALSE Everything is fine
|
|
|
|
Note:
|
|
CheckForHang handler is called in the context of a timer DPC.
|
|
take advantage of this fact when acquiring/releasing spinlocks
|
|
|
|
--*/
|
|
{
|
|
DEBUGP(MP_LOUD, ("---> MPCheckForHang\n"));
|
|
DEBUGP(MP_LOUD, ("<--- MPCheckForHang\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
VOID
|
|
MPHandleInterrupt(
|
|
IN NDIS_HANDLE MiniportAdapterContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MiniportHandleInterrupt is a DPC function called to do deferred
|
|
processing of all outstanding interrupt operations. When a NIC
|
|
generates an interrupt, a miniport's MiniportISR or
|
|
MiniportDisableInterrupt function dismisses the interrupt on the
|
|
NIC, saves any necessary state about the operation, and returns
|
|
control as quickly as possible, thereby deferring most
|
|
interrupt-driven I/O operations to MiniportHandleInterrupt. This
|
|
handler is called only if the MiniportISR function returned
|
|
QueueMiniportHandleInterrupt set to TRUE.
|
|
|
|
MiniportHandleInterrupt then re-enables interrupts on the NIC,
|
|
either by letting NDIS call the miniport driver's
|
|
MiniportEnableInterrupt function after MiniportHandleInterrupt
|
|
returns control or by enabling the interrupt from within
|
|
MiniportHandleInterrupt, which is faster.
|
|
|
|
Note that more than one instance of this function can execute
|
|
concurrently in SMP machines.
|
|
|
|
Runs at IRQL = DISPATCH_LEVEL
|
|
|
|
Arguments:
|
|
|
|
MiniportAdapterContext Pointer to our adapter
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DEBUGP(MP_TRACE, ("---> MPHandleInterrupt\n"));
|
|
DEBUGP(MP_TRACE, ("<--- MPHandleInterrupt\n"));
|
|
}
|
|
|
|
VOID
|
|
MPIsr(
|
|
OUT PBOOLEAN InterruptRecognized,
|
|
OUT PBOOLEAN QueueMiniportHandleInterrupt,
|
|
IN NDIS_HANDLE MiniportAdapterContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MiniportIsr handler is called to when the device asserts an interrupt.
|
|
MiniportISR dismisses the interrupt on the NIC, saves whatever state
|
|
it must about the interrupt, and defers as much of the I/O processing
|
|
for each interrupt as possible to the MiniportHandleInterrupt function.
|
|
|
|
MiniportISR is not re-entrant, although two instantiations of this
|
|
function can execute concurrently in SMP machines, particularly if
|
|
the miniport driver supports full-duplex sends and receives. A driver
|
|
writer should not rely on a one-to-one correspondence between the
|
|
execution of MiniportISR and MiniportHandleInterrupt.
|
|
|
|
If the NIC shares an IRQ with other devices (check NdisMRegisterInterrupt),
|
|
this function should determine whether the NIC generated the interrupt.
|
|
If the NIC did not generated the interrupt, MiniportISR should return FALSE
|
|
immediately so that the driver of the device that generated the interrupt
|
|
is called quickly.
|
|
|
|
Runs at IRQL = DIRQL assigned when the NIC driver's MiniportInitialize
|
|
function called NdisMRegisterInterrupt.
|
|
|
|
Arguments:
|
|
|
|
InterruptRecognized TRUE on return if the interrupt comes
|
|
from this NIC
|
|
QueueMiniportHandleInterrupt TRUE on return if MiniportHandleInterrupt
|
|
should be called
|
|
MiniportAdapterContext Pointer to our adapter
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
|
|
DEBUGP(MP_TRACE, ("---> MPIsr\n"));
|
|
DEBUGP(MP_TRACE, ("<--- MPIsr\n"));
|
|
}
|
|
|
|
VOID
|
|
MPDisableInterrupt(
|
|
IN PVOID MiniportAdapterContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MiniportDisableInterrupt typically disables interrupts by writing
|
|
a mask to the NIC. If a driver does not have this function, typically
|
|
its MiniportISR disables interrupts on the NIC.
|
|
|
|
This handler is required by drivers of NICs that support dynamic
|
|
enabling and disabling of interrupts but do not share an IRQ.
|
|
|
|
Runs at IRQL = DIRQL
|
|
|
|
Arguments:
|
|
|
|
MiniportAdapterContext Pointer to our adapter
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DEBUGP(MP_TRACE, ("---> MPDisableInterrupt\n"));
|
|
DEBUGP(MP_TRACE, ("<--- MPDisableInterrupt\n"));
|
|
|
|
}
|
|
|
|
VOID
|
|
MPEnableInterrupt(
|
|
IN PVOID MiniportAdapterContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MiniportEnableInterrupt typically enables interrupts by writing a mask
|
|
to the NIC. A NIC driver that exports a MiniportDisableInterrupt function
|
|
need not have a reciprocal MiniportEnableInterrupt function.
|
|
Such a driver's MiniportHandleInterrupt function is responsible for
|
|
re-enabling interrupts on the NIC.
|
|
|
|
Runs at IRQL = DIRQL
|
|
|
|
Arguments:
|
|
|
|
MiniportAdapterContext Pointer to our adapter
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DEBUGP(MP_TRACE, ("---> MPEnableInterrupt\n"));
|
|
DEBUGP(MP_TRACE, ("<--- MPEnableInterrupt\n"));
|
|
|
|
}
|
|
|
|
VOID
|
|
MPAllocateComplete(
|
|
NDIS_HANDLE MiniportAdapterContext,
|
|
IN PVOID VirtualAddress,
|
|
IN PNDIS_PHYSICAL_ADDRESS PhysicalAddress,
|
|
IN ULONG Length,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This handler is needed if the driver makes calls to
|
|
NdisMAllocateSharedMemoryAsync. Drivers of bus-master DMA NICs call
|
|
NdisMAllocateSharedMemoryAsync to dynamically allocate shared memory
|
|
for transfer operations when high network traffic places excessive
|
|
demands on the shared memory space that the driver allocated during
|
|
initialization.
|
|
|
|
Runs at IRQL = DISPATCH_LEVEL.
|
|
|
|
Arguments:
|
|
|
|
MiniportAdapterContext Pointer to our adapter
|
|
VirtualAddress Pointer to the allocated memory block
|
|
PhysicalAddress Physical address of the memory block
|
|
Length Length of the memory block
|
|
Context Context in NdisMAllocateSharedMemoryAsync
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DEBUGP(MP_TRACE, ("---> MPAllocateComplete\n"));
|
|
}
|
|
|
|
#ifdef NDIS51_MINIPORT
|
|
VOID
|
|
MPCancelSendPackets(
|
|
IN NDIS_HANDLE MiniportAdapterContext,
|
|
IN PVOID CancelId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MiniportCancelSendPackets cancels the transmission of all packets that
|
|
are marked with a specified cancellation identifier. Miniport drivers
|
|
that queue send packets for more than one second should export this
|
|
handler. When a protocol driver or intermediate driver calls the
|
|
NdisCancelSendPackets function, NDIS calls the MiniportCancelSendPackets
|
|
function of the appropriate lower-level driver (miniport driver or
|
|
intermediate driver) on the binding.
|
|
|
|
Runs at IRQL <= DISPATCH_LEVEL.
|
|
|
|
Available - NDIS5.1 (WinXP) and later.
|
|
|
|
Arguments:
|
|
|
|
MiniportAdapterContext Pointer to our adapter
|
|
CancelId All the packets with this Id should be cancelled
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PNDIS_PACKET Packet;
|
|
PVOID PacketId;
|
|
PLIST_ENTRY thisEntry, nextEntry, listHead;
|
|
SINGLE_LIST_ENTRY SendCancelList;
|
|
PSINGLE_LIST_ENTRY entry;
|
|
|
|
PMP_ADAPTER Adapter = (PMP_ADAPTER)MiniportAdapterContext;
|
|
|
|
#define MP_GET_PACKET_MR(_p) (PSINGLE_LIST_ENTRY)(&(_p)->MiniportReserved[0])
|
|
|
|
DEBUGP(MP_TRACE, ("---> MPCancelSendPackets\n"));
|
|
|
|
SendCancelList.Next = NULL;
|
|
|
|
NdisAcquireSpinLock(&Adapter->SendLock);
|
|
|
|
//
|
|
// Walk through the send wait queue and complete the sends with matching Id
|
|
//
|
|
listHead = &Adapter->SendWaitList;
|
|
|
|
for(thisEntry = listHead->Flink,nextEntry = thisEntry->Flink;
|
|
thisEntry != listHead;
|
|
thisEntry = nextEntry,nextEntry = thisEntry->Flink) {
|
|
Packet = CONTAINING_RECORD(thisEntry, NDIS_PACKET, MiniportReserved);
|
|
|
|
PacketId = NdisGetPacketCancelId(Packet);
|
|
if (PacketId == CancelId)
|
|
{
|
|
//
|
|
// This packet has the right CancelId
|
|
//
|
|
RemoveEntryList(thisEntry);
|
|
//
|
|
// Put this packet on SendCancelList
|
|
//
|
|
PushEntryList(&SendCancelList, MP_GET_PACKET_MR(Packet));
|
|
}
|
|
}
|
|
|
|
NdisReleaseSpinLock(&Adapter->SendLock);
|
|
|
|
//
|
|
// Get the packets from SendCancelList and complete them if any
|
|
//
|
|
|
|
entry = PopEntryList(&SendCancelList);
|
|
|
|
while (entry)
|
|
{
|
|
Packet = CONTAINING_RECORD(entry, NDIS_PACKET, MiniportReserved);
|
|
|
|
NdisMSendComplete(
|
|
Adapter->AdapterHandle,
|
|
Packet,
|
|
NDIS_STATUS_REQUEST_ABORTED);
|
|
|
|
entry = PopEntryList(&SendCancelList);
|
|
}
|
|
|
|
DEBUGP(MP_TRACE, ("<--- MPCancelSendPackets\n"));
|
|
|
|
}
|
|
|
|
VOID MPPnPEventNotify(
|
|
IN NDIS_HANDLE MiniportAdapterContext,
|
|
IN NDIS_DEVICE_PNP_EVENT PnPEvent,
|
|
IN PVOID InformationBuffer,
|
|
IN ULONG InformationBufferLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MiniportPnPEventNotify is to handle PnP notification messages.
|
|
All NDIS 5.1 miniport drivers must export a MiniportPnPEventNotify
|
|
function. Miniport drivers that have a WDM lower edge should export
|
|
a MiniportPnPEventNotify function.
|
|
|
|
Runs at IRQL = PASSIVE_LEVEL in the context of system thread.
|
|
|
|
Available - NDIS5.1 (WinXP) and later.
|
|
|
|
Arguments:
|
|
|
|
MiniportAdapterContext Pointer to our adapter
|
|
PnPEvent Self-explanatory
|
|
InformationBuffer Self-explanatory
|
|
InformationBufferLength Self-explanatory
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PMP_ADAPTER Adapter = (PMP_ADAPTER)MiniportAdapterContext;
|
|
PNDIS_POWER_PROFILE NdisPowerProfile;
|
|
|
|
//
|
|
// Turn off the warings.
|
|
//
|
|
UNREFERENCED_PARAMETER(Adapter);
|
|
|
|
DEBUGP(MP_TRACE, ("---> MPPnPEventNotify\n"));
|
|
|
|
switch (PnPEvent)
|
|
{
|
|
case NdisDevicePnPEventQueryRemoved:
|
|
//
|
|
// Called when NDIS receives IRP_MN_QUERY_REMOVE_DEVICE.
|
|
//
|
|
DEBUGP(MP_INFO, ("MPPnPEventNotify: NdisDevicePnPEventQueryRemoved\n"));
|
|
break;
|
|
|
|
case NdisDevicePnPEventRemoved:
|
|
//
|
|
// Called when NDIS receives IRP_MN_REMOVE_DEVICE.
|
|
// NDIS calls MiniportHalt function after this call returns.
|
|
//
|
|
DEBUGP(MP_INFO, ("MPPnPEventNotify: NdisDevicePnPEventRemoved\n"));
|
|
break;
|
|
|
|
case NdisDevicePnPEventSurpriseRemoved:
|
|
//
|
|
// Called when NDIS receives IRP_MN_SUPRISE_REMOVAL.
|
|
// NDIS calls MiniportHalt function after this call returns.
|
|
//
|
|
MP_SET_FLAG(Adapter, fMP_ADAPTER_SURPRISE_REMOVED);
|
|
DEBUGP(MP_INFO, ("MPPnPEventNotify: NdisDevicePnPEventSurpriseRemoved\n"));
|
|
break;
|
|
|
|
case NdisDevicePnPEventQueryStopped:
|
|
//
|
|
// Called when NDIS receives IRP_MN_QUERY_STOP_DEVICE. ??
|
|
//
|
|
DEBUGP(MP_INFO, ("MPPnPEventNotify: NdisDevicePnPEventQueryStopped\n"));
|
|
break;
|
|
|
|
case NdisDevicePnPEventStopped:
|
|
//
|
|
// Called when NDIS receives IRP_MN_STOP_DEVICE.
|
|
// NDIS calls MiniportHalt function after this call returns.
|
|
//
|
|
//
|
|
DEBUGP(MP_INFO, ("MPPnPEventNotify: NdisDevicePnPEventStopped\n"));
|
|
break;
|
|
|
|
case NdisDevicePnPEventPowerProfileChanged:
|
|
//
|
|
// After initializing a miniport driver and after miniport driver
|
|
// receives an OID_PNP_SET_POWER notification that specifies
|
|
// a device power state of NdisDeviceStateD0 (the powered-on state),
|
|
// NDIS calls the miniport's MiniportPnPEventNotify function with
|
|
// PnPEvent set to NdisDevicePnPEventPowerProfileChanged.
|
|
//
|
|
DEBUGP(MP_INFO, ("MPPnPEventNotify: NdisDevicePnPEventPowerProfileChanged\n"));
|
|
|
|
if(InformationBufferLength == sizeof(NDIS_POWER_PROFILE)) {
|
|
NdisPowerProfile = (PNDIS_POWER_PROFILE)InformationBuffer;
|
|
if(*NdisPowerProfile == NdisPowerProfileBattery) {
|
|
DEBUGP(MP_INFO,
|
|
("The host system is running on battery power\n"));
|
|
}
|
|
if(*NdisPowerProfile == NdisPowerProfileAcOnLine) {
|
|
DEBUGP(MP_INFO,
|
|
("The host system is running on AC power\n"));
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
DEBUGP(MP_ERROR, ("MPPnPEventNotify: unknown PnP event %x \n", PnPEvent));
|
|
break;
|
|
}
|
|
|
|
DEBUGP(MP_TRACE, ("<--- MPPnPEventNotify\n"));
|
|
|
|
}
|
|
|
|
#endif
|
|
|