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.
1716 lines
53 KiB
1716 lines
53 KiB
/* |
|
* TAP-Windows -- A kernel driver to provide virtual tap |
|
* device functionality on Windows. |
|
* |
|
* This code was inspired by the CIPE-Win32 driver by Damion K. Wilson. |
|
* |
|
* This source code is Copyright (C) 2002-2014 OpenVPN Technologies, Inc., |
|
* and is released under the GPL version 2 (see below). |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License version 2 |
|
* as published by the Free Software Foundation. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program (see the file COPYING included with this |
|
* distribution); if not, write to the Free Software Foundation, Inc., |
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
*/ |
|
|
|
// |
|
// Include files. |
|
// |
|
|
|
#include "tap.h" |
|
|
|
NDIS_OID TAPSupportedOids[] = |
|
{ |
|
OID_GEN_HARDWARE_STATUS, |
|
OID_GEN_TRANSMIT_BUFFER_SPACE, |
|
OID_GEN_RECEIVE_BUFFER_SPACE, |
|
OID_GEN_TRANSMIT_BLOCK_SIZE, |
|
OID_GEN_RECEIVE_BLOCK_SIZE, |
|
OID_GEN_VENDOR_ID, |
|
OID_GEN_VENDOR_DESCRIPTION, |
|
OID_GEN_VENDOR_DRIVER_VERSION, |
|
OID_GEN_CURRENT_PACKET_FILTER, |
|
OID_GEN_CURRENT_LOOKAHEAD, |
|
OID_GEN_DRIVER_VERSION, |
|
OID_GEN_MAXIMUM_TOTAL_SIZE, |
|
OID_GEN_XMIT_OK, |
|
OID_GEN_RCV_OK, |
|
OID_GEN_STATISTICS, |
|
#ifdef IMPLEMENT_OPTIONAL_OIDS |
|
OID_GEN_TRANSMIT_QUEUE_LENGTH, // Optional |
|
#endif // IMPLEMENT_OPTIONAL_OIDS |
|
OID_GEN_LINK_PARAMETERS, |
|
OID_GEN_INTERRUPT_MODERATION, |
|
OID_GEN_MEDIA_SUPPORTED, |
|
OID_GEN_MEDIA_IN_USE, |
|
OID_GEN_MAXIMUM_SEND_PACKETS, |
|
OID_GEN_XMIT_ERROR, |
|
OID_GEN_RCV_ERROR, |
|
OID_GEN_RCV_NO_BUFFER, |
|
OID_802_3_PERMANENT_ADDRESS, |
|
OID_802_3_CURRENT_ADDRESS, |
|
OID_802_3_MULTICAST_LIST, |
|
OID_802_3_MAXIMUM_LIST_SIZE, |
|
OID_802_3_RCV_ERROR_ALIGNMENT, |
|
OID_802_3_XMIT_ONE_COLLISION, |
|
OID_802_3_XMIT_MORE_COLLISIONS, |
|
#ifdef IMPLEMENT_OPTIONAL_OIDS |
|
OID_802_3_XMIT_DEFERRED, // Optional |
|
OID_802_3_XMIT_MAX_COLLISIONS, // Optional |
|
OID_802_3_RCV_OVERRUN, // Optional |
|
OID_802_3_XMIT_UNDERRUN, // Optional |
|
OID_802_3_XMIT_HEARTBEAT_FAILURE, // Optional |
|
OID_802_3_XMIT_TIMES_CRS_LOST, // Optional |
|
OID_802_3_XMIT_LATE_COLLISIONS, // Optional |
|
OID_PNP_CAPABILITIES, // Optional |
|
#endif // IMPLEMENT_OPTIONAL_OIDS |
|
}; |
|
|
|
//====================================================================== |
|
// TAP NDIS 6 Miniport Callbacks |
|
//====================================================================== |
|
|
|
// Returns with reference count initialized to one. |
|
PTAP_ADAPTER_CONTEXT |
|
tapAdapterContextAllocate( |
|
__in NDIS_HANDLE MiniportAdapterHandle |
|
) |
|
{ |
|
PTAP_ADAPTER_CONTEXT adapter = NULL; |
|
|
|
adapter = (PTAP_ADAPTER_CONTEXT )NdisAllocateMemoryWithTagPriority( |
|
GlobalData.NdisDriverHandle, |
|
sizeof(TAP_ADAPTER_CONTEXT), |
|
TAP_ADAPTER_TAG, |
|
NormalPoolPriority |
|
); |
|
|
|
if(adapter) |
|
{ |
|
NET_BUFFER_LIST_POOL_PARAMETERS nblPoolParameters = {0}; |
|
|
|
NdisZeroMemory(adapter,sizeof(TAP_ADAPTER_CONTEXT)); |
|
|
|
adapter->MiniportAdapterHandle = MiniportAdapterHandle; |
|
|
|
// Initialize cancel-safe IRP queue |
|
tapIrpCsqInitialize(&adapter->PendingReadIrpQueue); |
|
|
|
// Initialize TAP send packet queue. |
|
tapPacketQueueInitialize(&adapter->SendPacketQueue); |
|
|
|
// Allocate the adapter lock. |
|
NdisAllocateSpinLock(&adapter->AdapterLock); |
|
|
|
// NBL pool for making TAP receive indications. |
|
NdisZeroMemory(&nblPoolParameters, sizeof(NET_BUFFER_LIST_POOL_PARAMETERS)); |
|
|
|
// Initialize event used to determine when all receive NBLs have been returned. |
|
NdisInitializeEvent(&adapter->ReceiveNblInFlightCountZeroEvent); |
|
|
|
nblPoolParameters.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; |
|
nblPoolParameters.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; |
|
nblPoolParameters.Header.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; |
|
nblPoolParameters.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT; |
|
nblPoolParameters.ContextSize = 0; |
|
//nblPoolParameters.ContextSize = sizeof(RX_NETBUFLIST_RSVD); |
|
nblPoolParameters.fAllocateNetBuffer = TRUE; |
|
nblPoolParameters.PoolTag = TAP_RX_NBL_TAG; |
|
|
|
#pragma warning( suppress : 28197 ) |
|
adapter->ReceiveNblPool = NdisAllocateNetBufferListPool( |
|
adapter->MiniportAdapterHandle, |
|
&nblPoolParameters); |
|
|
|
if (adapter->ReceiveNblPool == NULL) |
|
{ |
|
DEBUGP (("[TAP] Couldn't allocate adapter receive NBL pool\n")); |
|
NdisFreeMemory(adapter,0,0); |
|
} |
|
|
|
// Add initial reference. Normally removed in AdapterHalt. |
|
adapter->RefCount = 1; |
|
|
|
// Safe for multiple removes. |
|
NdisInitializeListHead(&adapter->AdapterListLink); |
|
|
|
// |
|
// The miniport adapter is initially powered up |
|
// |
|
adapter->CurrentPowerState = NdisDeviceStateD0; |
|
} |
|
|
|
return adapter; |
|
} |
|
|
|
VOID |
|
tapReadPermanentAddress( |
|
__in PTAP_ADAPTER_CONTEXT Adapter, |
|
__in NDIS_HANDLE ConfigurationHandle, |
|
__out MACADDR PermanentAddress |
|
) |
|
{ |
|
NDIS_STATUS status; |
|
NDIS_CONFIGURATION_PARAMETER *configParameter; |
|
NDIS_STRING macKey = NDIS_STRING_CONST("MAC"); |
|
ANSI_STRING macString; |
|
BOOLEAN macFromRegistry = FALSE; |
|
|
|
// Read MAC parameter from registry. |
|
NdisReadConfiguration( |
|
&status, |
|
&configParameter, |
|
ConfigurationHandle, |
|
&macKey, |
|
NdisParameterString |
|
); |
|
|
|
if (status == NDIS_STATUS_SUCCESS) |
|
{ |
|
if( (configParameter->ParameterType == NdisParameterString) |
|
&& (configParameter->ParameterData.StringData.Length >= 12) |
|
) |
|
{ |
|
if (RtlUnicodeStringToAnsiString( |
|
&macString, |
|
&configParameter->ParameterData.StringData, |
|
TRUE) == STATUS_SUCCESS |
|
) |
|
{ |
|
macFromRegistry = ParseMAC (PermanentAddress, macString.Buffer); |
|
RtlFreeAnsiString (&macString); |
|
} |
|
} |
|
} |
|
|
|
if(!macFromRegistry) |
|
{ |
|
// |
|
// There is no (valid) address stashed in the registry parameter. |
|
// |
|
// Make up a dummy mac address based on the ANSI representation of the |
|
// NetCfgInstanceId GUID. |
|
// |
|
GenerateRandomMac(PermanentAddress, MINIPORT_INSTANCE_ID(Adapter)); |
|
} |
|
} |
|
|
|
NDIS_STATUS |
|
tapReadConfiguration( |
|
__in PTAP_ADAPTER_CONTEXT Adapter |
|
) |
|
{ |
|
NDIS_STATUS status = NDIS_STATUS_SUCCESS; |
|
NDIS_CONFIGURATION_OBJECT configObject; |
|
NDIS_HANDLE configHandle; |
|
|
|
DEBUGP (("[TAP] --> tapReadConfiguration\n")); |
|
|
|
// |
|
// Setup defaults in case configuration cannot be opened. |
|
// |
|
Adapter->MtuSize = ETHERNET_MTU; |
|
Adapter->MediaStateAlwaysConnected = FALSE; |
|
Adapter->LogicalMediaState = FALSE; |
|
Adapter->AllowNonAdmin = FALSE; |
|
// |
|
// Open the registry for this adapter to read advanced |
|
// configuration parameters stored by the INF file. |
|
// |
|
NdisZeroMemory(&configObject, sizeof(configObject)); |
|
|
|
{C_ASSERT(sizeof(configObject) >= NDIS_SIZEOF_CONFIGURATION_OBJECT_REVISION_1);} |
|
configObject.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT; |
|
configObject.Header.Size = NDIS_SIZEOF_CONFIGURATION_OBJECT_REVISION_1; |
|
configObject.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1; |
|
|
|
configObject.NdisHandle = Adapter->MiniportAdapterHandle; |
|
configObject.Flags = 0; |
|
|
|
status = NdisOpenConfigurationEx( |
|
&configObject, |
|
&configHandle |
|
); |
|
|
|
// Read on the opened configuration handle. |
|
if(status == NDIS_STATUS_SUCCESS) |
|
{ |
|
NDIS_CONFIGURATION_PARAMETER *configParameter; |
|
NDIS_STRING mkey = NDIS_STRING_CONST("NetCfgInstanceId"); |
|
|
|
// |
|
// Read NetCfgInstanceId from the registry. |
|
// ------------------------------------ |
|
// NetCfgInstanceId is required to create device and associated |
|
// symbolic link for the adapter device. |
|
// |
|
// NetCfgInstanceId is a GUID string provided by NDIS that identifies |
|
// the adapter instance. An example is: |
|
// |
|
// NetCfgInstanceId={410EB49D-2381-4FE7-9B36-498E22619DF0} |
|
// |
|
// Other names are derived from NetCfgInstanceId. For example, MiniportName: |
|
// |
|
// MiniportName=\DEVICE\{410EB49D-2381-4FE7-9B36-498E22619DF0} |
|
// |
|
NdisReadConfiguration ( |
|
&status, |
|
&configParameter, |
|
configHandle, |
|
&mkey, |
|
NdisParameterString |
|
); |
|
|
|
if (status == NDIS_STATUS_SUCCESS) |
|
{ |
|
if (configParameter->ParameterType == NdisParameterString) |
|
{ |
|
DEBUGP (("[TAP] NdisReadConfiguration (NetCfgInstanceId=%wZ)\n", |
|
&configParameter->ParameterData.StringData )); |
|
|
|
// Save NetCfgInstanceId as UNICODE_STRING. |
|
Adapter->NetCfgInstanceId.Length = Adapter->NetCfgInstanceId.MaximumLength |
|
= configParameter->ParameterData.StringData.Length; |
|
|
|
Adapter->NetCfgInstanceId.Buffer = Adapter->NetCfgInstanceIdBuffer; |
|
|
|
NdisMoveMemory( |
|
Adapter->NetCfgInstanceId.Buffer, |
|
configParameter->ParameterData.StringData.Buffer, |
|
Adapter->NetCfgInstanceId.Length |
|
); |
|
|
|
// Save NetCfgInstanceId as ANSI_STRING as well. |
|
if (RtlUnicodeStringToAnsiString ( |
|
&Adapter->NetCfgInstanceIdAnsi, |
|
&configParameter->ParameterData.StringData, |
|
TRUE) != STATUS_SUCCESS |
|
) |
|
{ |
|
DEBUGP (("[TAP] NetCfgInstanceId ANSI name conversion failed\n")); |
|
status = NDIS_STATUS_RESOURCES; |
|
} |
|
} |
|
else |
|
{ |
|
DEBUGP (("[TAP] NetCfgInstanceId has invalid type\n")); |
|
status = NDIS_STATUS_INVALID_DATA; |
|
} |
|
} |
|
else |
|
{ |
|
DEBUGP (("[TAP] NetCfgInstanceId failed\n")); |
|
status = NDIS_STATUS_INVALID_DATA; |
|
} |
|
|
|
if (status == NDIS_STATUS_SUCCESS) |
|
{ |
|
NDIS_STATUS localStatus; // Use default if these fail. |
|
NDIS_CONFIGURATION_PARAMETER *configParameter; |
|
NDIS_STRING mtuKey = NDIS_STRING_CONST("MTU"); |
|
NDIS_STRING mediaStatusKey = NDIS_STRING_CONST("MediaStatus"); |
|
#if ENABLE_NONADMIN |
|
NDIS_STRING allowNonAdminKey = NDIS_STRING_CONST("AllowNonAdmin"); |
|
#endif |
|
|
|
// Read MTU from the registry. |
|
NdisReadConfiguration ( |
|
&localStatus, |
|
&configParameter, |
|
configHandle, |
|
&mtuKey, |
|
NdisParameterInteger |
|
); |
|
|
|
if (localStatus == NDIS_STATUS_SUCCESS) |
|
{ |
|
if (configParameter->ParameterType == NdisParameterInteger) |
|
{ |
|
int mtu = configParameter->ParameterData.IntegerData; |
|
|
|
if(mtu == 0) |
|
{ |
|
mtu = ETHERNET_MTU; |
|
} |
|
|
|
// Sanity check |
|
if (mtu < MINIMUM_MTU) |
|
{ |
|
mtu = MINIMUM_MTU; |
|
} |
|
else if (mtu > MAXIMUM_MTU) |
|
{ |
|
mtu = MAXIMUM_MTU; |
|
} |
|
|
|
Adapter->MtuSize = mtu; |
|
} |
|
} |
|
|
|
DEBUGP (("[%s] Using MTU %d\n", |
|
MINIPORT_INSTANCE_ID (Adapter), |
|
Adapter->MtuSize |
|
)); |
|
|
|
// Read MediaStatus setting from registry. |
|
NdisReadConfiguration ( |
|
&localStatus, |
|
&configParameter, |
|
configHandle, |
|
&mediaStatusKey, |
|
NdisParameterInteger |
|
); |
|
|
|
if (localStatus == NDIS_STATUS_SUCCESS) |
|
{ |
|
if (configParameter->ParameterType == NdisParameterInteger) |
|
{ |
|
if(configParameter->ParameterData.IntegerData == 0) |
|
{ |
|
// Connect state is appplication controlled. |
|
DEBUGP(("[%s] Initial MediaConnectState: Application Controlled\n", |
|
MINIPORT_INSTANCE_ID (Adapter))); |
|
|
|
Adapter->MediaStateAlwaysConnected = FALSE; |
|
Adapter->LogicalMediaState = FALSE; |
|
} |
|
else |
|
{ |
|
// Connect state is always connected. |
|
DEBUGP(("[%s] Initial MediaConnectState: Always Connected\n", |
|
MINIPORT_INSTANCE_ID (Adapter))); |
|
|
|
Adapter->MediaStateAlwaysConnected = TRUE; |
|
Adapter->LogicalMediaState = TRUE; |
|
} |
|
} |
|
} |
|
|
|
// Read MAC PermanentAddress setting from registry. |
|
tapReadPermanentAddress( |
|
Adapter, |
|
configHandle, |
|
Adapter->PermanentAddress |
|
); |
|
|
|
DEBUGP (("[%s] Using MAC PermanentAddress %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", |
|
MINIPORT_INSTANCE_ID (Adapter), |
|
Adapter->PermanentAddress[0], |
|
Adapter->PermanentAddress[1], |
|
Adapter->PermanentAddress[2], |
|
Adapter->PermanentAddress[3], |
|
Adapter->PermanentAddress[4], |
|
Adapter->PermanentAddress[5]) |
|
); |
|
|
|
// Now seed the current MAC address with the permanent address. |
|
ETH_COPY_NETWORK_ADDRESS(Adapter->CurrentAddress, Adapter->PermanentAddress); |
|
|
|
DEBUGP (("[%s] Using MAC CurrentAddress %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", |
|
MINIPORT_INSTANCE_ID (Adapter), |
|
Adapter->CurrentAddress[0], |
|
Adapter->CurrentAddress[1], |
|
Adapter->CurrentAddress[2], |
|
Adapter->CurrentAddress[3], |
|
Adapter->CurrentAddress[4], |
|
Adapter->CurrentAddress[5]) |
|
); |
|
|
|
// Read optional AllowNonAdmin setting from registry. |
|
#if ENABLE_NONADMIN |
|
NdisReadConfiguration ( |
|
&localStatus, |
|
&configParameter, |
|
configHandle, |
|
&allowNonAdminKey, |
|
NdisParameterInteger |
|
); |
|
|
|
if (localStatus == NDIS_STATUS_SUCCESS) |
|
{ |
|
if (configParameter->ParameterType == NdisParameterInteger) |
|
{ |
|
Adapter->AllowNonAdmin = TRUE; |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
// Close the configuration handle. |
|
NdisCloseConfiguration(configHandle); |
|
} |
|
else |
|
{ |
|
DEBUGP (("[TAP] Couldn't open adapter registry\n")); |
|
} |
|
|
|
DEBUGP (("[TAP] <-- tapReadConfiguration; status = %8.8X\n",status)); |
|
|
|
return status; |
|
} |
|
|
|
VOID |
|
tapAdapterContextAddToGlobalList( |
|
__in PTAP_ADAPTER_CONTEXT Adapter |
|
) |
|
{ |
|
LOCK_STATE lockState; |
|
PLIST_ENTRY listEntry = &Adapter->AdapterListLink; |
|
|
|
// Acquire global adapter list lock. |
|
NdisAcquireReadWriteLock( |
|
&GlobalData.Lock, |
|
TRUE, // Acquire for write |
|
&lockState |
|
); |
|
|
|
// Adapter context should NOT be in any list. |
|
ASSERT( (listEntry->Flink == listEntry) && (listEntry->Blink == listEntry ) ); |
|
|
|
// Add reference to persist until after removal. |
|
tapAdapterContextReference(Adapter); |
|
|
|
// Add the adapter context to the global list. |
|
InsertTailList(&GlobalData.AdapterList,&Adapter->AdapterListLink); |
|
|
|
// Release global adapter list lock. |
|
NdisReleaseReadWriteLock(&GlobalData.Lock,&lockState); |
|
} |
|
|
|
VOID |
|
tapAdapterContextRemoveFromGlobalList( |
|
__in PTAP_ADAPTER_CONTEXT Adapter |
|
) |
|
{ |
|
LOCK_STATE lockState; |
|
|
|
// Acquire global adapter list lock. |
|
NdisAcquireReadWriteLock( |
|
&GlobalData.Lock, |
|
TRUE, // Acquire for write |
|
&lockState |
|
); |
|
|
|
// Remove the adapter context from the global list. |
|
RemoveEntryList(&Adapter->AdapterListLink); |
|
|
|
// Safe for multiple removes. |
|
NdisInitializeListHead(&Adapter->AdapterListLink); |
|
|
|
// Remove reference added in tapAdapterContextAddToGlobalList. |
|
tapAdapterContextDereference(Adapter); |
|
|
|
// Release global adapter list lock. |
|
NdisReleaseReadWriteLock(&GlobalData.Lock,&lockState); |
|
} |
|
|
|
// Returns with added reference on adapter context. |
|
PTAP_ADAPTER_CONTEXT |
|
tapAdapterContextFromDeviceObject( |
|
__in PDEVICE_OBJECT DeviceObject |
|
) |
|
{ |
|
LOCK_STATE lockState; |
|
|
|
// Acquire global adapter list lock. |
|
NdisAcquireReadWriteLock( |
|
&GlobalData.Lock, |
|
FALSE, // Acquire for read |
|
&lockState |
|
); |
|
|
|
if (!IsListEmpty(&GlobalData.AdapterList)) |
|
{ |
|
PLIST_ENTRY entry = GlobalData.AdapterList.Flink; |
|
PTAP_ADAPTER_CONTEXT adapter; |
|
|
|
while (entry != &GlobalData.AdapterList) |
|
{ |
|
adapter = CONTAINING_RECORD(entry, TAP_ADAPTER_CONTEXT, AdapterListLink); |
|
|
|
// Match on DeviceObject |
|
if(adapter->DeviceObject == DeviceObject ) |
|
{ |
|
// Add reference to adapter context. |
|
tapAdapterContextReference(adapter); |
|
|
|
// Release global adapter list lock. |
|
NdisReleaseReadWriteLock(&GlobalData.Lock,&lockState); |
|
|
|
return adapter; |
|
} |
|
|
|
// Move to next entry |
|
entry = entry->Flink; |
|
} |
|
} |
|
|
|
// Release global adapter list lock. |
|
NdisReleaseReadWriteLock(&GlobalData.Lock,&lockState); |
|
|
|
return (PTAP_ADAPTER_CONTEXT )NULL; |
|
} |
|
|
|
NDIS_STATUS |
|
AdapterSetOptions( |
|
__in NDIS_HANDLE NdisDriverHandle, |
|
__in NDIS_HANDLE DriverContext |
|
) |
|
/*++ |
|
Routine Description: |
|
|
|
The MiniportSetOptions function registers optional handlers. For each |
|
optional handler that should be registered, this function makes a call |
|
to NdisSetOptionalHandlers. |
|
|
|
MiniportSetOptions runs at IRQL = PASSIVE_LEVEL. |
|
|
|
Arguments: |
|
|
|
DriverContext The context handle |
|
|
|
Return Value: |
|
|
|
NDIS_STATUS_xxx code |
|
|
|
--*/ |
|
{ |
|
NDIS_STATUS status; |
|
|
|
DEBUGP (("[TAP] --> AdapterSetOptions\n")); |
|
|
|
// |
|
// Set any optional handlers by filling out the appropriate struct and |
|
// calling NdisSetOptionalHandlers here. |
|
// |
|
|
|
status = NDIS_STATUS_SUCCESS; |
|
|
|
DEBUGP (("[TAP] <-- AdapterSetOptions; status = %8.8X\n",status)); |
|
|
|
return status; |
|
} |
|
|
|
NDIS_STATUS |
|
AdapterCreate( |
|
__in NDIS_HANDLE MiniportAdapterHandle, |
|
__in NDIS_HANDLE MiniportDriverContext, |
|
__in PNDIS_MINIPORT_INIT_PARAMETERS MiniportInitParameters |
|
) |
|
{ |
|
PTAP_ADAPTER_CONTEXT adapter = NULL; |
|
NDIS_STATUS status; |
|
|
|
UNREFERENCED_PARAMETER(MiniportDriverContext); |
|
UNREFERENCED_PARAMETER(MiniportInitParameters); |
|
|
|
DEBUGP (("[TAP] --> AdapterCreate\n")); |
|
|
|
do |
|
{ |
|
NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES regAttributes = {0}; |
|
NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES genAttributes = {0}; |
|
NDIS_PNP_CAPABILITIES pnpCapabilities = {0}; |
|
|
|
// |
|
// Allocate adapter context structure and initialize all the |
|
// memory resources for sending and receiving packets. |
|
// |
|
// Returns with reference count initialized to one. |
|
// |
|
adapter = tapAdapterContextAllocate(MiniportAdapterHandle); |
|
|
|
if(adapter == NULL) |
|
{ |
|
DEBUGP (("[TAP] Couldn't allocate adapter memory\n")); |
|
status = NDIS_STATUS_RESOURCES; |
|
break; |
|
} |
|
|
|
// Enter the Initializing state. |
|
DEBUGP (("[TAP] Miniport State: Initializing\n")); |
|
|
|
tapAdapterAcquireLock(adapter,FALSE); |
|
adapter->Locked.AdapterState = MiniportInitializingState; |
|
tapAdapterReleaseLock(adapter,FALSE); |
|
|
|
// |
|
// First read adapter configuration from registry. |
|
// ----------------------------------------------- |
|
// Subsequent device registration will fail if NetCfgInstanceId |
|
// has not been successfully read. |
|
// |
|
status = tapReadConfiguration(adapter); |
|
|
|
// |
|
// Set the registration attributes. |
|
// |
|
{C_ASSERT(sizeof(regAttributes) >= NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1);} |
|
regAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES; |
|
regAttributes.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1; |
|
regAttributes.Header.Revision = NDIS_SIZEOF_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1; |
|
|
|
regAttributes.MiniportAdapterContext = adapter; |
|
regAttributes.AttributeFlags = TAP_ADAPTER_ATTRIBUTES_FLAGS; |
|
|
|
regAttributes.CheckForHangTimeInSeconds = TAP_ADAPTER_CHECK_FOR_HANG_TIME_IN_SECONDS; |
|
regAttributes.InterfaceType = TAP_INTERFACE_TYPE; |
|
|
|
//NDIS_DECLARE_MINIPORT_ADAPTER_CONTEXT(TAP_ADAPTER_CONTEXT); |
|
status = NdisMSetMiniportAttributes( |
|
MiniportAdapterHandle, |
|
(PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)®Attributes |
|
); |
|
|
|
if (status != NDIS_STATUS_SUCCESS) |
|
{ |
|
DEBUGP (("[TAP] NdisSetOptionalHandlers failed; Status 0x%08x\n",status)); |
|
break; |
|
} |
|
|
|
// |
|
// Next, set the general attributes. |
|
// |
|
{C_ASSERT(sizeof(genAttributes) >= NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1);} |
|
genAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES; |
|
genAttributes.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1; |
|
genAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1; |
|
|
|
// |
|
// Specify the medium type that the NIC can support but not |
|
// necessarily the medium type that the NIC currently uses. |
|
// |
|
genAttributes.MediaType = TAP_MEDIUM_TYPE; |
|
|
|
// |
|
// Specifiy medium type that the NIC currently uses. |
|
// |
|
genAttributes.PhysicalMediumType = TAP_PHYSICAL_MEDIUM; |
|
|
|
// |
|
// Specifiy the maximum network frame size, in bytes, that the NIC |
|
// supports excluding the header. |
|
// |
|
genAttributes.MtuSize = TAP_FRAME_MAX_DATA_SIZE; |
|
genAttributes.MaxXmitLinkSpeed = TAP_XMIT_SPEED; |
|
genAttributes.XmitLinkSpeed = TAP_XMIT_SPEED; |
|
genAttributes.MaxRcvLinkSpeed = TAP_RECV_SPEED; |
|
genAttributes.RcvLinkSpeed = TAP_RECV_SPEED; |
|
|
|
if(adapter->MediaStateAlwaysConnected) |
|
{ |
|
DEBUGP(("[%s] Initial MediaConnectState: Connected\n", |
|
MINIPORT_INSTANCE_ID (adapter))); |
|
|
|
genAttributes.MediaConnectState = MediaConnectStateConnected; |
|
} |
|
else |
|
{ |
|
DEBUGP(("[%s] Initial MediaConnectState: Disconnected\n", |
|
MINIPORT_INSTANCE_ID (adapter))); |
|
|
|
genAttributes.MediaConnectState = MediaConnectStateDisconnected; |
|
} |
|
|
|
genAttributes.MediaDuplexState = MediaDuplexStateFull; |
|
|
|
// |
|
// The maximum number of bytes the NIC can provide as lookahead data. |
|
// If that value is different from the size of the lookahead buffer |
|
// supported by bound protocols, NDIS will call MiniportOidRequest to |
|
// set the size of the lookahead buffer provided by the miniport driver |
|
// to the minimum of the miniport driver and protocol(s) values. If the |
|
// driver always indicates up full packets with |
|
// NdisMIndicateReceiveNetBufferLists, it should set this value to the |
|
// maximum total frame size, which excludes the header. |
|
// |
|
// Upper-layer drivers examine lookahead data to determine whether a |
|
// packet that is associated with the lookahead data is intended for |
|
// one or more of their clients. If the underlying driver supports |
|
// multipacket receive indications, bound protocols are given full net |
|
// packets on every indication. Consequently, this value is identical |
|
// to that returned for OID_GEN_RECEIVE_BLOCK_SIZE. |
|
// |
|
genAttributes.LookaheadSize = TAP_MAX_LOOKAHEAD; |
|
genAttributes.MacOptions = TAP_MAC_OPTIONS; |
|
genAttributes.SupportedPacketFilters = TAP_SUPPORTED_FILTERS; |
|
|
|
// |
|
// The maximum number of multicast addresses the NIC driver can manage. |
|
// This list is global for all protocols bound to (or above) the NIC. |
|
// Consequently, a protocol can receive NDIS_STATUS_MULTICAST_FULL from |
|
// the NIC driver when attempting to set the multicast address list, |
|
// even if the number of elements in the given list is less than the |
|
// number originally returned for this query. |
|
// |
|
genAttributes.MaxMulticastListSize = TAP_MAX_MCAST_LIST; |
|
genAttributes.MacAddressLength = MACADDR_SIZE; |
|
|
|
// |
|
// Return the MAC address of the NIC burnt in the hardware. |
|
// |
|
ETH_COPY_NETWORK_ADDRESS(genAttributes.PermanentMacAddress, adapter->PermanentAddress); |
|
|
|
// |
|
// Return the MAC address the NIC is currently programmed to use. Note |
|
// that this address could be different from the permananent address as |
|
// the user can override using registry. Read NdisReadNetworkAddress |
|
// doc for more info. |
|
// |
|
ETH_COPY_NETWORK_ADDRESS(genAttributes.CurrentMacAddress, adapter->CurrentAddress); |
|
|
|
genAttributes.RecvScaleCapabilities = NULL; |
|
genAttributes.AccessType = TAP_ACCESS_TYPE; |
|
genAttributes.DirectionType = TAP_DIRECTION_TYPE; |
|
genAttributes.ConnectionType = TAP_CONNECTION_TYPE; |
|
genAttributes.IfType = TAP_IFTYPE; |
|
genAttributes.IfConnectorPresent = TAP_HAS_PHYSICAL_CONNECTOR; |
|
genAttributes.SupportedStatistics = TAP_SUPPORTED_STATISTICS; |
|
genAttributes.SupportedPauseFunctions = NdisPauseFunctionsUnsupported; // IEEE 802.3 pause frames |
|
genAttributes.DataBackFillSize = 0; |
|
genAttributes.ContextBackFillSize = 0; |
|
|
|
// |
|
// The SupportedOidList is an array of OIDs for objects that the |
|
// underlying driver or its NIC supports. Objects include general, |
|
// media-specific, and implementation-specific objects. NDIS forwards a |
|
// subset of the returned list to protocols that make this query. That |
|
// is, NDIS filters any supported statistics OIDs out of the list |
|
// because protocols never make statistics queries. |
|
// |
|
genAttributes.SupportedOidList = TAPSupportedOids; |
|
genAttributes.SupportedOidListLength = sizeof(TAPSupportedOids); |
|
genAttributes.AutoNegotiationFlags = NDIS_LINK_STATE_DUPLEX_AUTO_NEGOTIATED; |
|
|
|
// |
|
// Set power management capabilities |
|
// |
|
NdisZeroMemory(&pnpCapabilities, sizeof(pnpCapabilities)); |
|
pnpCapabilities.WakeUpCapabilities.MinMagicPacketWakeUp = NdisDeviceStateUnspecified; |
|
pnpCapabilities.WakeUpCapabilities.MinPatternWakeUp = NdisDeviceStateUnspecified; |
|
genAttributes.PowerManagementCapabilities = &pnpCapabilities; |
|
|
|
status = NdisMSetMiniportAttributes( |
|
MiniportAdapterHandle, |
|
(PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&genAttributes |
|
); |
|
|
|
if (status != NDIS_STATUS_SUCCESS) |
|
{ |
|
DEBUGP (("[TAP] NdisMSetMiniportAttributes failed; Status 0x%08x\n",status)); |
|
break; |
|
} |
|
|
|
// |
|
// Create the Win32 device I/O interface. |
|
// |
|
status = CreateTapDevice(adapter); |
|
|
|
if (status == NDIS_STATUS_SUCCESS) |
|
{ |
|
// Add this adapter to the global adapter list. |
|
tapAdapterContextAddToGlobalList(adapter); |
|
} |
|
else |
|
{ |
|
DEBUGP (("[TAP] CreateTapDevice failed; Status 0x%08x\n",status)); |
|
break; |
|
} |
|
} while(FALSE); |
|
|
|
if(status == NDIS_STATUS_SUCCESS) |
|
{ |
|
// Enter the Paused state if initialization is complete. |
|
DEBUGP (("[TAP] Miniport State: Paused\n")); |
|
|
|
tapAdapterAcquireLock(adapter,FALSE); |
|
adapter->Locked.AdapterState = MiniportPausedState; |
|
tapAdapterReleaseLock(adapter,FALSE); |
|
} |
|
else |
|
{ |
|
if(adapter != NULL) |
|
{ |
|
DEBUGP (("[TAP] Miniport State: Halted\n")); |
|
|
|
// |
|
// Remove reference when adapter context was allocated |
|
// --------------------------------------------------- |
|
// This should result in freeing adapter context memory |
|
// and assiciated resources. |
|
// |
|
tapAdapterContextDereference(adapter); |
|
adapter = NULL; |
|
} |
|
} |
|
|
|
DEBUGP (("[TAP] <-- AdapterCreate; status = %8.8X\n",status)); |
|
|
|
return status; |
|
} |
|
|
|
VOID |
|
AdapterHalt( |
|
__in NDIS_HANDLE MiniportAdapterContext, |
|
__in NDIS_HALT_ACTION HaltAction |
|
) |
|
/*++ |
|
|
|
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 |
|
HaltAction The reason for halting the adapter |
|
|
|
Return Value: |
|
|
|
None. |
|
|
|
--*/ |
|
{ |
|
PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; |
|
|
|
UNREFERENCED_PARAMETER(HaltAction); |
|
|
|
DEBUGP (("[TAP] --> AdapterHalt\n")); |
|
|
|
// Enter the Halted state. |
|
DEBUGP (("[TAP] Miniport State: Halted\n")); |
|
|
|
tapAdapterAcquireLock(adapter,FALSE); |
|
adapter->Locked.AdapterState = MiniportHaltedState; |
|
tapAdapterReleaseLock(adapter,FALSE); |
|
|
|
// Remove this adapter from the global adapter list. |
|
tapAdapterContextRemoveFromGlobalList(adapter); |
|
|
|
// BUGBUG!!! Call AdapterShutdownEx to do some of the work of stopping. |
|
|
|
// TODO!!! More... |
|
|
|
// |
|
// Destroy the TAP Win32 device. |
|
// |
|
DestroyTapDevice(adapter); |
|
|
|
// |
|
// Remove initial reference added in AdapterCreate. |
|
// ------------------------------------------------ |
|
// This should result in freeing adapter context memory |
|
// and resources allocated in AdapterCreate. |
|
// |
|
tapAdapterContextDereference(adapter); |
|
adapter = NULL; |
|
|
|
DEBUGP (("[TAP] <-- AdapterHalt\n")); |
|
} |
|
|
|
VOID |
|
tapWaitForReceiveNblInFlightCountZeroEvent( |
|
__in PTAP_ADAPTER_CONTEXT Adapter |
|
) |
|
{ |
|
LONG nblCount; |
|
|
|
// |
|
// Wait until higher-level protocol has returned all NBLs |
|
// to the driver. |
|
// |
|
|
|
// Add one NBL "bias" to insure allow event to be reset safely. |
|
nblCount = NdisInterlockedIncrement(&Adapter->ReceiveNblInFlightCount); |
|
ASSERT(nblCount > 0 ); |
|
NdisResetEvent(&Adapter->ReceiveNblInFlightCountZeroEvent); |
|
|
|
// |
|
// Now remove the bias and wait for the ReceiveNblInFlightCountZeroEvent |
|
// if the count returned is not zero. |
|
// |
|
nblCount = NdisInterlockedDecrement(&Adapter->ReceiveNblInFlightCount); |
|
ASSERT(nblCount >= 0); |
|
|
|
if(nblCount) |
|
{ |
|
LARGE_INTEGER startTime, currentTime; |
|
|
|
NdisGetSystemUpTimeEx(&startTime); |
|
|
|
for (;;) |
|
{ |
|
BOOLEAN waitResult = NdisWaitEvent( |
|
&Adapter->ReceiveNblInFlightCountZeroEvent, |
|
TAP_WAIT_POLL_LOOP_TIMEOUT |
|
); |
|
|
|
NdisGetSystemUpTimeEx(¤tTime); |
|
|
|
if (waitResult) |
|
{ |
|
break; |
|
} |
|
|
|
DEBUGP (("[%s] Waiting for %d in-flight receive NBLs to be returned.\n", |
|
MINIPORT_INSTANCE_ID (Adapter), |
|
Adapter->ReceiveNblInFlightCount |
|
)); |
|
} |
|
|
|
DEBUGP (("[%s] Waited %d ms for all in-flight NBLs to be returned.\n", |
|
MINIPORT_INSTANCE_ID (Adapter), |
|
(currentTime.LowPart - startTime.LowPart) |
|
)); |
|
} |
|
} |
|
|
|
NDIS_STATUS |
|
AdapterPause( |
|
__in NDIS_HANDLE MiniportAdapterContext, |
|
__in PNDIS_MINIPORT_PAUSE_PARAMETERS PauseParameters |
|
) |
|
/*++ |
|
|
|
Routine Description: |
|
|
|
When a miniport receives a pause request, it enters into a Pausing state. |
|
The miniport should not indicate up any more network data. Any pending |
|
send requests must be completed, and new requests must be rejected with |
|
NDIS_STATUS_PAUSED. |
|
|
|
Once all sends have been completed and all recieve NBLs have returned to |
|
the miniport, the miniport enters the Paused state. |
|
|
|
While paused, the miniport can still service interrupts from the hardware |
|
(to, for example, continue to indicate NDIS_STATUS_MEDIA_CONNECT |
|
notifications). |
|
|
|
The miniport must continue to be able to handle status indications and OID |
|
requests. MiniportPause is different from MiniportHalt because, in |
|
general, the MiniportPause operation won't release any resources. |
|
MiniportPause must not attempt to acquire any resources where allocation |
|
can fail, since MiniportPause itself must not fail. |
|
|
|
|
|
MiniportPause runs at IRQL = PASSIVE_LEVEL. |
|
|
|
Arguments: |
|
|
|
MiniportAdapterContext Pointer to the Adapter |
|
MiniportPauseParameters Additional information about the pause operation |
|
|
|
Return Value: |
|
|
|
If the miniport is able to immediately enter the Paused state, it should |
|
return NDIS_STATUS_SUCCESS. |
|
|
|
If the miniport must wait for send completions or pending receive NBLs, it |
|
should return NDIS_STATUS_PENDING now, and call NDISMPauseComplete when the |
|
miniport has entered the Paused state. |
|
|
|
No other return value is permitted. The pause operation must not fail. |
|
|
|
--*/ |
|
{ |
|
PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; |
|
NDIS_STATUS status; |
|
|
|
UNREFERENCED_PARAMETER(PauseParameters); |
|
|
|
DEBUGP (("[TAP] --> AdapterPause\n")); |
|
|
|
// Enter the Pausing state. |
|
DEBUGP (("[TAP] Miniport State: Pausing\n")); |
|
|
|
tapAdapterAcquireLock(adapter,FALSE); |
|
adapter->Locked.AdapterState = MiniportPausingState; |
|
tapAdapterReleaseLock(adapter,FALSE); |
|
|
|
// |
|
// Stop the flow of network data through the receive path |
|
// ------------------------------------------------------ |
|
// In the Pausing and Paused state tapAdapterSendAndReceiveReady |
|
// will prevent new calls to NdisMIndicateReceiveNetBufferLists |
|
// to indicate additional receive NBLs to the host. |
|
// |
|
// However, there may be some in-flight NBLs owned by the driver |
|
// that have been indicated to the host but have not yet been |
|
// returned. |
|
// |
|
// Wait here for all in-flight receive indications to be returned. |
|
// |
|
tapWaitForReceiveNblInFlightCountZeroEvent(adapter); |
|
|
|
// |
|
// Stop the flow of network data through the send path |
|
// --------------------------------------------------- |
|
// The initial implementation of the NDIS 6 send path follows the |
|
// NDIS 5 pattern. Under this approach every send packet is copied |
|
// into a driver-owned TAP_PACKET structure and the NBL owned by |
|
// higher-level protocol is immediatly completed. |
|
// |
|
// With this deep-copy approach the driver never claims ownership |
|
// of any send NBL. |
|
// |
|
// A future implementation may queue send NBLs and thereby eliminate |
|
// the need for the unnecessary allocation and deep copy of each packet. |
|
// |
|
// So, nothing to do here for the send path for now... |
|
|
|
status = NDIS_STATUS_SUCCESS; |
|
|
|
// Enter the Paused state. |
|
DEBUGP (("[TAP] Miniport State: Paused\n")); |
|
|
|
tapAdapterAcquireLock(adapter,FALSE); |
|
adapter->Locked.AdapterState = MiniportPausedState; |
|
tapAdapterReleaseLock(adapter,FALSE); |
|
|
|
DEBUGP (("[TAP] <-- AdapterPause; status = %8.8X\n",status)); |
|
|
|
return status; |
|
} |
|
|
|
NDIS_STATUS |
|
AdapterRestart( |
|
__in NDIS_HANDLE MiniportAdapterContext, |
|
__in PNDIS_MINIPORT_RESTART_PARAMETERS RestartParameters |
|
) |
|
/*++ |
|
|
|
Routine Description: |
|
|
|
When a miniport receives a restart request, it enters into a Restarting |
|
state. The miniport may begin indicating received data (e.g., using |
|
NdisMIndicateReceiveNetBufferLists), handling status indications, and |
|
processing OID requests in the Restarting state. However, no sends will be |
|
requested while the miniport is in the Restarting state. |
|
|
|
Once the miniport is ready to send data, it has entered the Running state. |
|
The miniport informs NDIS that it is in the Running state by returning |
|
NDIS_STATUS_SUCCESS from this MiniportRestart function; or if this function |
|
has already returned NDIS_STATUS_PENDING, by calling NdisMRestartComplete. |
|
|
|
|
|
MiniportRestart runs at IRQL = PASSIVE_LEVEL. |
|
|
|
Arguments: |
|
|
|
MiniportAdapterContext Pointer to the Adapter |
|
RestartParameters Additional information about the restart operation |
|
|
|
Return Value: |
|
|
|
If the miniport is able to immediately enter the Running state, it should |
|
return NDIS_STATUS_SUCCESS. |
|
|
|
If the miniport is still in the Restarting state, it should return |
|
NDIS_STATUS_PENDING now, and call NdisMRestartComplete when the miniport |
|
has entered the Running state. |
|
|
|
Other NDIS_STATUS codes indicate errors. If an error is encountered, the |
|
miniport must return to the Paused state (i.e., stop indicating receives). |
|
|
|
--*/ |
|
{ |
|
PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; |
|
NDIS_STATUS status; |
|
|
|
UNREFERENCED_PARAMETER(RestartParameters); |
|
|
|
DEBUGP (("[TAP] --> AdapterRestart\n")); |
|
|
|
// Enter the Restarting state. |
|
DEBUGP (("[TAP] Miniport State: Restarting\n")); |
|
|
|
tapAdapterAcquireLock(adapter,FALSE); |
|
adapter->Locked.AdapterState = MiniportRestartingState; |
|
tapAdapterReleaseLock(adapter,FALSE); |
|
|
|
status = NDIS_STATUS_SUCCESS; |
|
|
|
if(status == NDIS_STATUS_SUCCESS) |
|
{ |
|
// Enter the Running state. |
|
DEBUGP (("[TAP] Miniport State: Running\n")); |
|
|
|
tapAdapterAcquireLock(adapter,FALSE); |
|
adapter->Locked.AdapterState = MiniportRunning; |
|
tapAdapterReleaseLock(adapter,FALSE); |
|
} |
|
else |
|
{ |
|
// Enter the Paused state if restart failed. |
|
DEBUGP (("[TAP] Miniport State: Paused\n")); |
|
|
|
tapAdapterAcquireLock(adapter,FALSE); |
|
adapter->Locked.AdapterState = MiniportPausedState; |
|
tapAdapterReleaseLock(adapter,FALSE); |
|
} |
|
|
|
DEBUGP (("[TAP] <-- AdapterRestart; status = %8.8X\n",status)); |
|
|
|
return status; |
|
} |
|
|
|
BOOLEAN |
|
tapAdapterReadAndWriteReady( |
|
__in PTAP_ADAPTER_CONTEXT Adapter |
|
) |
|
/*++ |
|
|
|
Routine Description: |
|
|
|
This routine determines whether the adapter device interface can |
|
accept read and write operations. |
|
|
|
Arguments: |
|
|
|
Adapter Pointer to our adapter context |
|
|
|
Return Value: |
|
|
|
Returns TRUE if the adapter state allows it to queue IRPs passed to |
|
the device read and write callbacks. |
|
--*/ |
|
{ |
|
if(!Adapter->TapDeviceCreated) |
|
{ |
|
// TAP device not created or is being destroyed. |
|
return FALSE; |
|
} |
|
|
|
if(Adapter->TapFileObject == NULL) |
|
{ |
|
// TAP application file object not open. |
|
return FALSE; |
|
} |
|
|
|
if(!Adapter->TapFileIsOpen) |
|
{ |
|
// TAP application file object may be closing. |
|
return FALSE; |
|
} |
|
|
|
if(!Adapter->LogicalMediaState) |
|
{ |
|
// Don't handle read/write if media not connected. |
|
return FALSE; |
|
} |
|
|
|
if(Adapter->CurrentPowerState != NdisDeviceStateD0) |
|
{ |
|
// Don't handle read/write if device is not fully powered. |
|
return FALSE; |
|
} |
|
|
|
return TRUE; |
|
} |
|
|
|
NDIS_STATUS |
|
tapAdapterSendAndReceiveReady( |
|
__in PTAP_ADAPTER_CONTEXT Adapter |
|
) |
|
/*++ |
|
|
|
Routine Description: |
|
|
|
This routine determines whether the adapter NDIS send and receive |
|
paths are ready. |
|
|
|
This routine examines various adapter state variables and returns |
|
a value that indicates whether the adapter NDIS interfaces can |
|
accept send packets or indicate receive packets. |
|
|
|
In normal operation the adapter may temporarily enter and then exit |
|
a not-ready condition. In particular, the adapter becomes not-ready |
|
when in the Pausing/Paused states, but may become ready again when |
|
Restarted. |
|
|
|
Runs at IRQL <= DISPATCH_LEVEL |
|
|
|
Arguments: |
|
|
|
Adapter Pointer to our adapter context |
|
|
|
Return Value: |
|
|
|
Returns NDIS_STATUS_SUCCESS if the adapter state allows it to |
|
accept send packets and indicate receive packets. |
|
|
|
Otherwise it returns a NDIS_STATUS value other than NDIS_STATUS_SUCCESS. |
|
These status values can be used directly as the completion status for |
|
packets that must be completed immediatly in the send path. |
|
--*/ |
|
{ |
|
NDIS_STATUS status = NDIS_STATUS_SUCCESS; |
|
|
|
// |
|
// Check various state variables to insure adapter is ready. |
|
// |
|
tapAdapterAcquireLock(Adapter,FALSE); |
|
|
|
if(!Adapter->LogicalMediaState) |
|
{ |
|
status = NDIS_STATUS_MEDIA_DISCONNECTED; |
|
} |
|
else if(Adapter->CurrentPowerState != NdisDeviceStateD0) |
|
{ |
|
status = NDIS_STATUS_LOW_POWER_STATE; |
|
} |
|
else if(Adapter->ResetInProgress) |
|
{ |
|
status = NDIS_STATUS_RESET_IN_PROGRESS; |
|
} |
|
else |
|
{ |
|
switch(Adapter->Locked.AdapterState) |
|
{ |
|
case MiniportPausingState: |
|
case MiniportPausedState: |
|
status = NDIS_STATUS_PAUSED; |
|
break; |
|
|
|
case MiniportHaltedState: |
|
status = NDIS_STATUS_INVALID_STATE; |
|
break; |
|
|
|
default: |
|
status = NDIS_STATUS_SUCCESS; |
|
break; |
|
} |
|
} |
|
|
|
tapAdapterReleaseLock(Adapter,FALSE); |
|
|
|
return status; |
|
} |
|
|
|
BOOLEAN |
|
AdapterCheckForHangEx( |
|
__in NDIS_HANDLE MiniportAdapterContext |
|
) |
|
/*++ |
|
|
|
Routine Description: |
|
|
|
The MiniportCheckForHangEx 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. |
|
|
|
MiniportCheckForHangEx 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 |
|
|
|
--*/ |
|
{ |
|
PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; |
|
|
|
//DEBUGP (("[TAP] --> AdapterCheckForHangEx\n")); |
|
|
|
//DEBUGP (("[TAP] <-- AdapterCheckForHangEx; status = FALSE\n")); |
|
|
|
return FALSE; // Everything is fine |
|
} |
|
|
|
NDIS_STATUS |
|
AdapterReset( |
|
__in NDIS_HANDLE MiniportAdapterContext, |
|
__out PBOOLEAN AddressingReset |
|
) |
|
/*++ |
|
|
|
Routine Description: |
|
|
|
MiniportResetEx 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 |
|
|
|
--*/ |
|
{ |
|
PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; |
|
NDIS_STATUS status; |
|
|
|
UNREFERENCED_PARAMETER(MiniportAdapterContext); |
|
UNREFERENCED_PARAMETER(AddressingReset); |
|
|
|
DEBUGP (("[TAP] --> AdapterReset\n")); |
|
|
|
// Indicate that adapter reset is in progress. |
|
adapter->ResetInProgress = TRUE; |
|
|
|
// See note above... |
|
*AddressingReset = FALSE; |
|
|
|
// BUGBUG!!! TODO!!! Lots of work here... |
|
|
|
// Indicate that adapter reset has completed. |
|
adapter->ResetInProgress = FALSE; |
|
|
|
status = NDIS_STATUS_SUCCESS; |
|
|
|
DEBUGP (("[TAP] <-- AdapterReset; status = %8.8X\n",status)); |
|
|
|
return status; |
|
} |
|
|
|
VOID |
|
AdapterDevicePnpEventNotify( |
|
__in NDIS_HANDLE MiniportAdapterContext, |
|
__in PNET_DEVICE_PNP_EVENT NetDevicePnPEvent |
|
) |
|
{ |
|
PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; |
|
|
|
DEBUGP (("[TAP] --> AdapterDevicePnpEventNotify\n")); |
|
|
|
/* |
|
switch (NetDevicePnPEvent->DevicePnPEvent) |
|
{ |
|
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, "[%p] MPDevicePnpEventNotify: NdisDevicePnPEventSurpriseRemoved\n", Adapter); |
|
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, "[%p] MPDevicePnpEventNotify: NdisDevicePnPEventPowerProfileChanged\n", Adapter); |
|
|
|
if (NetDevicePnPEvent->InformationBufferLength == sizeof(ULONG)) |
|
{ |
|
ULONG NdisPowerProfile = *((PULONG)NetDevicePnPEvent->InformationBuffer); |
|
|
|
if (NdisPowerProfile == NdisPowerProfileBattery) |
|
{ |
|
DEBUGP(MP_INFO, "[%p] The host system is running on battery power\n", Adapter); |
|
} |
|
if (NdisPowerProfile == NdisPowerProfileAcOnLine) |
|
{ |
|
DEBUGP(MP_INFO, "[%p] The host system is running on AC power\n", Adapter); |
|
} |
|
} |
|
break; |
|
|
|
default: |
|
DEBUGP(MP_ERROR, "[%p] MPDevicePnpEventNotify: unknown PnP event 0x%x\n", Adapter, NetDevicePnPEvent->DevicePnPEvent); |
|
} |
|
*/ |
|
DEBUGP (("[TAP] <-- AdapterDevicePnpEventNotify\n")); |
|
} |
|
|
|
VOID |
|
AdapterShutdownEx( |
|
__in NDIS_HANDLE MiniportAdapterContext, |
|
__in NDIS_SHUTDOWN_ACTION ShutdownAction |
|
) |
|
/*++ |
|
|
|
Routine Description: |
|
|
|
The MiniportShutdownEx handler restores hardware 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. Do not call |
|
into NDIS at this time. |
|
|
|
This can be called at aribitrary IRQL, including in the context of a |
|
bugcheck. |
|
|
|
Arguments: |
|
|
|
MiniportAdapterContext Pointer to our adapter |
|
ShutdownAction The reason why NDIS called the shutdown function |
|
|
|
Return Value: |
|
|
|
None. |
|
|
|
--*/ |
|
{ |
|
PTAP_ADAPTER_CONTEXT adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext; |
|
|
|
UNREFERENCED_PARAMETER(ShutdownAction); |
|
UNREFERENCED_PARAMETER(MiniportAdapterContext); |
|
|
|
DEBUGP (("[TAP] --> AdapterShutdownEx\n")); |
|
|
|
// Enter the Shutdown state. |
|
DEBUGP (("[TAP] Miniport State: Shutdown\n")); |
|
|
|
tapAdapterAcquireLock(adapter,FALSE); |
|
adapter->Locked.AdapterState = MiniportShutdownState; |
|
tapAdapterReleaseLock(adapter,FALSE); |
|
|
|
// |
|
// BUGBUG!!! FlushIrpQueues??? |
|
// |
|
|
|
DEBUGP (("[TAP] <-- AdapterShutdownEx\n")); |
|
} |
|
|
|
|
|
// Free adapter context memory and associated resources. |
|
VOID |
|
tapAdapterContextFree( |
|
__in PTAP_ADAPTER_CONTEXT Adapter |
|
) |
|
{ |
|
PLIST_ENTRY listEntry = &Adapter->AdapterListLink; |
|
|
|
DEBUGP (("[TAP] --> tapAdapterContextFree\n")); |
|
|
|
// Adapter context should already be removed. |
|
ASSERT( (listEntry->Flink == listEntry) && (listEntry->Blink == listEntry ) ); |
|
|
|
// Insure that adapter context has been removed from global adapter list. |
|
RemoveEntryList(&Adapter->AdapterListLink); |
|
|
|
// Free the adapter lock. |
|
NdisFreeSpinLock(&Adapter->AdapterLock); |
|
|
|
// Free the ANSI NetCfgInstanceId buffer. |
|
if(Adapter->NetCfgInstanceIdAnsi.Buffer != NULL) |
|
{ |
|
RtlFreeAnsiString(&Adapter->NetCfgInstanceIdAnsi); |
|
} |
|
|
|
Adapter->NetCfgInstanceIdAnsi.Buffer = NULL; |
|
|
|
// Free the receive NBL pool. |
|
if(Adapter->ReceiveNblPool != NULL ) |
|
{ |
|
NdisFreeNetBufferListPool(Adapter->ReceiveNblPool); |
|
} |
|
|
|
Adapter->ReceiveNblPool = NULL; |
|
|
|
NdisFreeMemory(Adapter,0,0); |
|
|
|
DEBUGP (("[TAP] <-- tapAdapterContextFree\n")); |
|
} |
|
ULONG |
|
tapGetNetBufferFrameType( |
|
__in PNET_BUFFER NetBuffer |
|
) |
|
/*++ |
|
|
|
Routine Description: |
|
|
|
Reads the network frame's destination address to determine the type |
|
(broadcast, multicast, etc) |
|
|
|
Runs at IRQL <= DISPATCH_LEVEL. |
|
|
|
Arguments: |
|
|
|
NetBuffer The NB to examine |
|
|
|
Return Value: |
|
|
|
NDIS_PACKET_TYPE_BROADCAST |
|
NDIS_PACKET_TYPE_MULTICAST |
|
NDIS_PACKET_TYPE_DIRECTED |
|
|
|
--*/ |
|
{ |
|
PETH_HEADER ethernetHeader; |
|
|
|
ethernetHeader = (PETH_HEADER )NdisGetDataBuffer( |
|
NetBuffer, |
|
sizeof(ETH_HEADER), |
|
NULL, |
|
1, |
|
0 |
|
); |
|
|
|
ASSERT(ethernetHeader); |
|
|
|
if (ETH_IS_BROADCAST(ethernetHeader->dest)) |
|
{ |
|
return NDIS_PACKET_TYPE_BROADCAST; |
|
} |
|
else if(ETH_IS_MULTICAST(ethernetHeader->dest)) |
|
{ |
|
return NDIS_PACKET_TYPE_MULTICAST; |
|
} |
|
else |
|
{ |
|
return NDIS_PACKET_TYPE_DIRECTED; |
|
} |
|
|
|
} |
|
|
|
ULONG |
|
tapGetNetBufferCountsFromNetBufferList( |
|
__in PNET_BUFFER_LIST NetBufferList, |
|
__inout_opt PULONG TotalByteCount // Of all linked NBs |
|
) |
|
/*++ |
|
|
|
Routine Description: |
|
|
|
Returns the number of net buffers linked to the net buffer list. |
|
|
|
Optionally retuens the total byte count of all net buffers linked |
|
to the net buffer list |
|
|
|
Runs at IRQL <= DISPATCH_LEVEL. |
|
|
|
Arguments: |
|
|
|
NetBufferList The NBL to examine |
|
|
|
Return Value: |
|
|
|
The number of net buffers linked to the net buffer list. |
|
|
|
--*/ |
|
{ |
|
ULONG netBufferCount = 0; |
|
PNET_BUFFER currentNb; |
|
|
|
if(TotalByteCount) |
|
{ |
|
*TotalByteCount = 0; |
|
} |
|
|
|
currentNb = NET_BUFFER_LIST_FIRST_NB(NetBufferList); |
|
|
|
while(currentNb) |
|
{ |
|
++netBufferCount; |
|
|
|
if(TotalByteCount) |
|
{ |
|
*TotalByteCount += NET_BUFFER_DATA_LENGTH(currentNb); |
|
} |
|
|
|
// Move to next NB |
|
currentNb = NET_BUFFER_NEXT_NB(currentNb); |
|
} |
|
|
|
return netBufferCount; |
|
} |
|
|
|
VOID |
|
tapAdapterAcquireLock( |
|
__in PTAP_ADAPTER_CONTEXT Adapter, |
|
__in BOOLEAN DispatchLevel |
|
) |
|
{ |
|
ASSERT(!DispatchLevel || (DISPATCH_LEVEL == KeGetCurrentIrql())); |
|
|
|
if (DispatchLevel) |
|
{ |
|
NdisDprAcquireSpinLock(&Adapter->AdapterLock); |
|
} |
|
else |
|
{ |
|
NdisAcquireSpinLock(&Adapter->AdapterLock); |
|
} |
|
} |
|
|
|
VOID |
|
tapAdapterReleaseLock( |
|
__in PTAP_ADAPTER_CONTEXT Adapter, |
|
__in BOOLEAN DispatchLevel |
|
) |
|
{ |
|
ASSERT(!DispatchLevel || (DISPATCH_LEVEL == KeGetCurrentIrql())); |
|
|
|
if (DispatchLevel) |
|
{ |
|
NdisDprReleaseSpinLock(&Adapter->AdapterLock); |
|
} |
|
else |
|
{ |
|
NdisReleaseSpinLock(&Adapter->AdapterLock); |
|
} |
|
} |
|
|
|
|
|
|