📄 wdmvnicadap.cpp
字号:
// WdmVNICAdap.cpp: implementation of the WdmVNICAdapter class.
//
//=============================================================================
//
// Compuware Corporation
// NuMega Lab
// 9 Townsend West
// Nashua, NH 03060 USA
//
// Copyright (c) 2001 Compuware Corporation. All Rights Reserved.
// Unpublished - rights reserved under the Copyright laws of the
// United States.
//
//=============================================================================
//////////////////////////////////////////////////////////////////////
#include <kndis.h>
#include <kndisvdw.h> // DriverWorks
#include <KIrpPool.h>
#include "Characteristics.h"
#include "WdmVNICAdap.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
WdmVNICAdapter::WdmVNICAdapter() :
KNdisMiniAdapter(),
m_HaltFlag(FALSE),
m_RxIdleEvent(),
m_RxIrpsOutstanding(),
#if KNDIS_W9X_COMPATIBLE
m_RxIndicateQueue((ULONG)&((PHW_RFD)0)->m_LE),
#endif // KNDIS_W9X_COMPATIBLE
m_RxIrpList()
{
// TODO: initialize your private data members
// Do NOT call any NDIS functions in here. Defer to Initialize()
}
WdmVNICAdapter::~WdmVNICAdapter()
{
// TODO: deallocate all the resources allocated in constructor/Initialize()
}
////////////////////////////////////////////////////////////////////
// NDIS callback handlers
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// WdmVNICAdapter::Initialize
//
// MiniportInitialize 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
//
// Parameters:
// Medium
// Reference to KNdisMedium object that we have to set the medium
// type our NIC supports
// Config
// Reference to KNdisConfig object that we use to query various
// configuration parameters
// IRQL:
// PASSIVE_LEVEL
// Return Mode:
// Synchronous
//
// TODO:
// 1) Select supported medium in Medium object
// 2) Read config parameters if any using Config object
// 3) Allocate h/w resources, pools, etc.
// 4) Register with NDIS using SetAttributes()
// 5) Register Shutdown Handler
// 6) Initialize and enable the NIC.
// NOTE: Be ready to process INTs even before the function exits
//
NDIS_STATUS WdmVNICAdapter::Initialize
(IN OUT KNdisMedium& Medium, IN KNdisConfig& Config)
{
TRACE("WdmVNICAdapter::Initialize() Entered\n");
// Select our the Medium:
if (! Medium.Select(WDMVNIC_MEDIUM_TYPE) )
KNDIS_RETURN_ERROR (NDIS_STATUS_UNSUPPORTED_MEDIA);
// Get network address (if any) from the Registry. If specified
// it will be used instead of burned permanent address:
if (Config.ReadNetworkAddress(m_CurrentAddress) != NDIS_STATUS_SUCCESS)
m_CurrentAddress.Invalidate();
// Read some other config stuff from the Registry, e.g.
ULONG uMode;
Config.Read(KNDIS_STRING_CONST("Mode"), &uMode);
// Retrieve some initialization parameters from the Registry. Use defaults if none.
UINT TxIrpPoolSize = Config.Read(KNDIS_STRING_CONST("TxIrpPoolSize"), 8);
TRACE("WdmVNICAdapter::Initialize() TxIrpPoolSize %d\n", TxIrpPoolSize);
UINT RxIrpPoolSize = Config.Read(KNDIS_STRING_CONST("RxIrpPoolSize"), 1);
TRACE("WdmVNICAdapter::Initialize() RxIrpPoolSize %d\n", RxIrpPoolSize);
UINT BusTimerIntervalMSecs = Config.Read(KNDIS_STRING_CONST("BusTimerIntervalMSecs"), 5000);
TRACE("WdmVNICAdapter::Initialize() BusTimerIntervalMSecs %d\n", BusTimerIntervalMSecs);
UINT RxPacketLength = Config.Read(KNDIS_STRING_CONST("RxPacketLength"), 1000);
TRACE("WdmVNICAdapter::Initialize() RxPacketLength %d\n", RxPacketLength);
UINT RxRandomize = Config.Read(KNDIS_STRING_CONST("RxRandomize"), (UINT)0);
TRACE("WdmVNICAdapter::Initialize() RxRandomize %d\n", RxRandomize);
UINT MaxRxBurstCount = Config.Read(KNDIS_STRING_CONST("MaxRxBurstCount"), 20);
TRACE("WdmVNICAdapter::Initialize() MaxRxBurstCount %d\n", MaxRxBurstCount);
// Now let NDIS know about the BUS the NIC is on. Here's where the NDIS/Adapter-instance
// handshake takes place. This should happen *before* allocating the h/w resources:
SetAttributesEx(NdisInterfaceInternal,
NDIS_ATTRIBUTE_DESERIALIZE);
// Get the physical and "TopOfStack" device objects from NDIS for our WDM
// device. The "TopOfStack" device object is the device to which we will
// submit IRPs to talk to the bus driver for our device. We will use the PDO, along
// with the "TopOfStack" device object to initialize our lower device object using
// the DriverWorks class KPnpLowerDevice (or in this case KUsbLowerDevice, which is
// derived from KPnpLowerDevice).
GetDeviceProperty(&m_PhysicalDeviceObject, NULL, &m_TopOfStackDeviceObject);
// Initialize the IRP pools used to service the bus device for Send and Receive
m_TxIrpPool.Initialize(m_TopOfStackDeviceObject, TxIrpPoolSize);
// Initialize the context block heap. We'll need one block per irp. We only
// need the heap for Tx context blocks, since the Rx context blocks will be
// allocated in the HW_RFD structures used by the receive area initialized below.
m_ContextHeap.Initialize(static_cast<USHORT>(TxIrpPoolSize));
#if KNDIS_W9X_COMPATIBLE
// Initialize a timer that will be used for indicating Rx packets in a Win9x compatible
// manner.
m_Timer.Initialize(this, KNDIS_MEMBER_CALLBACK(IndicateCallback));
#endif // KNDIS_W9X_COMPATIBLE
m_RxBufPool.Initialize(RxIrpPoolSize);
// This sets up the Receive Frame Area including:
// Allocating internal software Receive Frame Descriptors
// Allocating packet and buffer pools
// Initializing and chaining packets, software RFD, buffers
// Maintains list of software RFD descriptors
//
// This driver's requirement is to further initialize the hardware RFD's
// see global template specialization DescriptorInitialize(..) which
// is called for each descriptor
m_pRxArea = new KNdisSystemReceiveArea< WdmVNICAdapter, HW_RFD >(
*this,
RxIrpPoolSize
);
// Initialize the bus based lower device object. Since NDIS owns this device object, we
// use the form of Initialize() on our KPnpLowerDevice derived class that takes the
// "TopOfStackDevice" as the device to which calls to the lower device should be directed
// in lieu of actually attaching to the device stack.
m_BusDevice.Initialize(m_TopOfStackDeviceObject, m_PhysicalDeviceObject);
m_BusDevice.SetAdapter(this);
// Some initialization calls to our bus simulation device.
m_BusDevice.SetTimerInterval(BusTimerIntervalMSecs);
m_BusDevice.SetRxPacketLength(RxPacketLength);
m_BusDevice.SetRxRandomize((RxRandomize != 0));
m_BusDevice.SetMaxRxBurstCount(MaxRxBurstCount);
// TODO: Read perm address from the card instead
m_PermanentAddress = m_CurrentAddress;
// Set default filter and MAC options. In principle, we should also
// relay that to our card...
m_uPacketFilter = NDIS_PACKET_TYPE_DIRECTED |
NDIS_PACKET_TYPE_MULTICAST |
NDIS_PACKET_TYPE_BROADCAST;
m_uMacOptions = NDIS_MAC_OPTION_TRANSFERS_NOT_PEND |
NDIS_MAC_OPTION_RECEIVE_SERIALIZED |
NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA |
NDIS_MAC_OPTION_NO_LOOPBACK;
// Get the Receive IRPs going
StartReceiveUnit();
return NDIS_STATUS_SUCCESS;
}
////////////////////////////////////////////////////////////////////
// WdmVNICAdapter::Halt
//
// MiniportHalt is a required function that deallocates resources when
// the NIC is removed and halts the NIC.
//
// Parameters:
// None
// IRQL:
// PASSIVE_LEVEL
// Return Mode:
// Synchronous
//
// NOTE: Miniports has no "unload" for the driver. The Halt() is the last
// function called into a miniport, so everything should be stopped
// and freed right here. The KNDIS framework takes care of destroying
// the adapter object itself, so this member function is merery required
// to undo things done by the Initialize() above - in the inversed order.
//
// Alternatevely, the driver writer can move all the deallocations to the
// destructor: the destructor will be called immediatelly after Halt()
// returns. Our sample follows this practice.
//
VOID WdmVNICAdapter::Halt(VOID)
{
TRACE("WdmVNICAdapter::Halt() Entered\n");
// Set the halt flag
m_HaltFlag = TRUE;
// Cancel all of the IRPs in the Rx Irp list.
PHW_RFD pHwRfd = m_RxIrpList.Head();
while (pHwRfd)
{
IoCancelIrp(pHwRfd->m_pIrp);
pHwRfd = m_RxIrpList.Next(pHwRfd);
}
// Wait for all of the Rx IRPs to be completed
LARGE_INTEGER DueTime;
DueTime.QuadPart = -(10000 * 20000); // 20 seconds
if ( m_RxIdleEvent.Wait() == FALSE )
{
ASSERT(!"All Rx IRPs were not accounted for");
}
// Wait for all of the Tx IRPs to be completed
if ( m_TxIrpPool.Wait(1000) == STATUS_TIMEOUT ) // 20 seconds
{
ASSERT(!"All Tx IRPs were not accounted for");
}
// Deallocate our receive area. This will cleanup all of the HW_RFD descriptors
// and call the DescriptorInvalidate method, which will deallocate the IRP associated with
// the descriptor in the DescriptorInitialize method.
TRACE("WdmVNICAdapter::Halt() Deleting Receive Area\n");
delete m_pRxArea;
m_pRxArea = NULL;
}
////////////////////////////////////////////////////////////////////
// WdmVNICAdapter::Reset
//
// MiniportReset is a required function that issues a hardware reset
// to the NIC and/or resets the driver's software state.
//
// Parameters:
// AddressingReset
// Points to a variable that MiniportReset sets to TRUE if the
// NDIS library should call MiniportSetInformation to restore
// addressing information to the current values.
// IRQL:
// DISPATCH_LEVEL
// Return Mode:
// Asynchronous
NDIS_STATUS WdmVNICAdapter::Reset
(OUT PBOOLEAN AddressingReset)
{
TRACE("WdmVNICAdapter::Reset() Entered\n");
// TODO: Reset the card
return NDIS_STATUS_SUCCESS;
}
////////////////////////////////////////////////////////////////////
// WdmVNICAdapter::Shutdown
//
// MiniportShutdown does nothing more than restore the NIC to its
// initial state (before the miniport's DriverEntry function runs)
//
// Parameters:
// None
// IRQL:
// If MiniportShutdown is called due to a user-initiated system shutdown,
// it runs at IRQL PASSIVE_LEVEL in a system-thread context. If it is called
// due to an unrecoverable error, MiniportShutdown runs at an arbitrary IRQL
// and in the context of whatever component raised the error.
// Return Mode:
// Synchronous
VOID WdmVNICAdapter::Shutdown(VOID)
{
TRACE("WdmVNICAdapter::Shutdown() Entered\n");
// TODO: Reset the card for good
}
////////////////////////////////////////////////////////////////////
// WdmVNICAdapter::Send
//
// Transfers a protocol-supplied packet over the network
//
// Parameters:
// Packet
// Points to a packet descriptor specifying the data to be transmitted.
// Flags
// Specifies the packet flags, if any, set by the protocol.
// IRQL:
// DISPATCH_LEVEL
// Return Mode:
// Asynchronous
// NOTE:
// The return code determines the ownership of the packet and further
// interaction between NDIS and our driver. Specifically,
//
// NDIS_STATUS_SUCCESS - Done with.
// NDIS_STATUS_PENDING - We keep it until further notice.
// NDIS_STATUS_RESOURCES - We didn't have the resouces *now*, so, NDIS,
// please keep it until further notice.
//
// NOTE: Deserialized Miniports should never return NDIS_STATUS_RESOURCES to NDIS,
// and must instead queue it internally.
//
NDIS_STATUS WdmVNICAdapter::Send(
IN PNDIS_PACKET Packet,
IN UINT Flags
)
{
TRACE("WdmVNICAdapter::Send() Entered\n");
if (TxPacket(Packet) == NDIS_STATUS_RESOURCES)
{
// We can never return NDIS_STATUS_RESOURCES to NDIS, and must
// instead queue This packet for the Deserialized Miniport
m_TxQueue.InsertTail(Packet);
TRACE("!!!Queueing packet\n");
}
// Deserialized drivers always return pending.
return NDIS_STATUS_PENDING;
}
///////////////////////////////////////////////////////////////////////
// WdmVNICAdapter::TxPacket
//
// Parameters:
// PNDIS_PACKET - Packet to be transmitted
// IRQL:
// IRQL DISPATCH_LEVEL
// Returns:
// NDIS_STATUS
NDIS_STATUS WdmVNICAdapter::TxPacket(
IN PNDIS_PACKET Packet
)
{
NDIS_STATUS Status = NDIS_STATUS_PENDING;
KIrp I = m_TxIrpPool.Allocate();
if (I.IsNull())
{
Status = NDIS_STATUS_RESOURCES;
}
else
{
// allocate a new context structure
IRP_CONTEXT* pContext = new (&m_ContextHeap) IRP_CONTEXT (this, Packet);
// make sure it succeeded
if ( pContext == NULL )
{
m_TxIrpPool.Free(I);
Status = NDIS_STATUS_RESOURCES;
}
else
{
// TODO should transfer the packet information to the IRP. This is highly dependent
// on how the IRP should look to the lower device.
I.SetCompletionRoutine(WdmCompletionTxRoutine(this), pContext);
m_BusDevice.Call(I);
}
}
return Status;
}
///////////////////////////////////////////////////////////////////////
// WdmVNICAdapter::CompletionTxRoutine
//
// Parameters:
// pIrp - irp being completed
// Context - context passed when the irp had been submitted
// IRQL:
// IRQL DISPATCH_LEVEL
// Returns:
// Usually, STATUS_MORE_PROCESSING_REQUIRED
// NOTE:
// Processes asynchronous "send" IRP completions: this is a "HandleInterrupt()"
// thing for NDIS WDM drivers.So, the logic is similar to HandleInterrupt():
// If a pended Tx packet gets completed, return the packet to NDIS.
// The important point is that if the the irps are be recycled back to the pools they
// came from, STATUS_MORE_PROCESSING_REQUIRED should be returned to the system.
NTSTATUS WdmVNICAdapter::CompletionTxRoutine(PIRP pIrp, WdmVNICAdapter::IRP_CONTEXT* pContext)
{
TRACE("WdmVNICAdapter::TxPacketComplete() Entered\n");
// Recycle the irp and the packet
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -