Leaked source code of windows server 2003
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.
 
 
 
 
 
 

3427 lines
96 KiB

/*++
Copyright(c) 1992-2000 Microsoft Corporation
Module Name:
protocol.c
Abstract:
NDIS Protocol Entry points and utility functions for the NDIS
MUX Intermediate Miniport sample.
The protocol edge binds to Ethernet (NdisMedium802_3) adapters,
and initiates creation of zero or more Virtual Ethernet LAN (VELAN)
miniport instances by calling NdisIMInitializeDeviceInstanceEx once
for each VELAN configured over a lower binding.
Environment:
Kernel mode.
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#define MODULE_NUMBER MODULE_PROT
VOID
PtBindAdapter(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE BindContext,
IN PNDIS_STRING DeviceName,
IN PVOID SystemSpecific1,
IN PVOID SystemSpecific2
)
/*++
Routine Description:
Called by NDIS to bind to a miniport below. This routine
creates a binding by calling NdisOpenAdapter, and then
initiates creation of all configured VELANs on this binding.
Arguments:
Status - Return status of bind here.
BindContext - Can be passed to NdisCompleteBindAdapter if this
call is pended.
DeviceName - Device name to bind to. This is passed to
NdisOpenAdapter.
SystemSpecific1 - Can be passed to NdisOpenProtocolConfiguration to
read per-binding information
SystemSpecific2 - Unused
Return Value:
*Status is set to NDIS_STATUS_SUCCESS if no failure occurred
while handling this call, otherwise an error code.
--*/
{
PADAPT pAdapt = NULL;
NDIS_STATUS OpenErrorStatus;
UINT MediumIndex;
PNDIS_STRING pConfigString;
ULONG Length;
UNREFERENCED_PARAMETER(BindContext);
UNREFERENCED_PARAMETER(SystemSpecific2);
pConfigString = (PNDIS_STRING)SystemSpecific1;
DBGPRINT(MUX_LOUD, ("==> Protocol BindAdapter: %ws\n", pConfigString->Buffer));
do
{
//
// Allocate memory for Adapter struct plus the config
// string with two extra WCHARs for NULL termination.
//
Length = sizeof(ADAPT) +
pConfigString->MaximumLength + sizeof(WCHAR);
NdisAllocateMemoryWithTag(&pAdapt, Length , TAG);
if (pAdapt == NULL)
{
*Status = NDIS_STATUS_RESOURCES;
break;
}
//
// Initialize the adapter structure
//
NdisZeroMemory(pAdapt, sizeof(ADAPT));
(VOID)PtReferenceAdapter(pAdapt, (PUCHAR)"openadapter");
//
// Copy in the Config string - we will use this to open the
// registry section for this adapter at a later point.
//
pAdapt->ConfigString.MaximumLength = pConfigString->MaximumLength;
pAdapt->ConfigString.Length = pConfigString->Length;
pAdapt->ConfigString.Buffer = (PWCHAR)((PUCHAR)pAdapt +
sizeof(ADAPT));
NdisMoveMemory(pAdapt->ConfigString.Buffer,
pConfigString->Buffer,
pConfigString->Length);
pAdapt->ConfigString.Buffer[pConfigString->Length/sizeof(WCHAR)] =
((WCHAR)0);
NdisInitializeEvent(&pAdapt->Event);
NdisInitializeListHead(&pAdapt->VElanList);
pAdapt->PtDevicePowerState = NdisDeviceStateD0;
MUX_INIT_ADAPT_RW_LOCK(pAdapt);
//
// TODO: Allocate a packet pool and buffers for send & receive.
//
// Now open the adapter below and complete the initialization
//
NdisOpenAdapter(Status,
&OpenErrorStatus,
&pAdapt->BindingHandle,
&MediumIndex,
MediumArray,
sizeof(MediumArray)/sizeof(NDIS_MEDIUM),
ProtHandle,
pAdapt,
DeviceName,
0,
NULL);
if (*Status == NDIS_STATUS_PENDING)
{
NdisWaitEvent(&pAdapt->Event, 0);
*Status = pAdapt->Status;
}
if (*Status != NDIS_STATUS_SUCCESS)
{
pAdapt->BindingHandle = NULL;
break;
}
pAdapt->Medium = MediumArray[MediumIndex];
//
// Add this adapter to the global AdapterList
//
MUX_ACQUIRE_MUTEX(&GlobalMutex);
InsertTailList(&AdapterList, &pAdapt->Link);
MUX_RELEASE_MUTEX(&GlobalMutex);
//
// Get some information from the adapter below.
//
PtQueryAdapterInfo(pAdapt);
//
// Start all VELANS configured on this adapter.
//
*Status = PtBootStrapVElans(pAdapt);
} while(FALSE);
if (*Status != NDIS_STATUS_SUCCESS)
{
if (pAdapt != NULL)
{
//
// For some reason, the driver cannot create velan for the binding
//
if (pAdapt->BindingHandle != NULL)
{
NDIS_STATUS LocalStatus;
//
// Close the binding the driver opened above
//
NdisResetEvent(&pAdapt->Event);
NdisCloseAdapter(&LocalStatus, pAdapt->BindingHandle);
pAdapt->BindingHandle = NULL;
if (LocalStatus == NDIS_STATUS_PENDING)
{
NdisWaitEvent(&pAdapt->Event, 0);
}
MUX_ACQUIRE_MUTEX(&GlobalMutex);
RemoveEntryList(&pAdapt->Link);
MUX_RELEASE_MUTEX(&GlobalMutex);
}
PtDereferenceAdapter(pAdapt, (PUCHAR)"openadapter");
pAdapt = NULL;
}
}
DBGPRINT(MUX_INFO, ("<== Protocol BindAdapter: pAdapt %p, Status %x\n", pAdapt, *Status));
}
VOID
PtOpenAdapterComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status,
IN NDIS_STATUS OpenErrorStatus
)
/*++
Routine Description:
Completion routine for NdisOpenAdapter issued from within the
PtBindAdapter. Simply unblock the caller.
Arguments:
ProtocolBindingContext Pointer to the adapter
Status Status of the NdisOpenAdapter call
OpenErrorStatus Secondary status(ignored by us).
Return Value:
None
--*/
{
PADAPT pAdapt =(PADAPT)ProtocolBindingContext;
UNREFERENCED_PARAMETER(OpenErrorStatus);
DBGPRINT(MUX_LOUD, ("==> PtOpenAdapterComplete: Adapt %p, Status %x\n", pAdapt, Status));
pAdapt->Status = Status;
NdisSetEvent(&pAdapt->Event);
}
VOID
PtQueryAdapterInfo(
IN PADAPT pAdapt
)
/*++
Routine Description:
Query the adapter we are bound to for some standard OID values
which we cache.
Arguments:
pAdapt Pointer to the adapter
Return Value:
None
--*/
{
//
// Get the link speed.
//
pAdapt->LinkSpeed = MUX_DEFAULT_LINK_SPEED;
PtQueryAdapterSync(pAdapt,
OID_GEN_LINK_SPEED,
&pAdapt->LinkSpeed,
sizeof(pAdapt->LinkSpeed));
//
// Get the max lookahead size.
//
pAdapt->MaxLookAhead = MUX_DEFAULT_LOOKAHEAD_SIZE;
PtQueryAdapterSync(pAdapt,
OID_GEN_MAXIMUM_LOOKAHEAD,
&pAdapt->MaxLookAhead,
sizeof(pAdapt->MaxLookAhead));
//
// Get the Ethernet MAC address.
//
PtQueryAdapterSync(pAdapt,
OID_802_3_CURRENT_ADDRESS,
&pAdapt->CurrentAddress,
sizeof(pAdapt->CurrentAddress));
}
VOID
PtQueryAdapterSync(
IN PADAPT pAdapt,
IN NDIS_OID Oid,
IN PVOID InformationBuffer,
IN ULONG InformationBufferLength
)
/*++
Routine Description:
Utility routine to query the adapter for a single OID value. This
blocks for the query to complete.
Arguments:
pAdapt Pointer to the adapter
Oid OID to query for
InformationBuffer Place for the result
InformationBufferLength Length of the above
Return Value:
None.
--*/
{
PMUX_NDIS_REQUEST pMuxNdisRequest = NULL;
NDIS_STATUS Status;
do
{
NdisAllocateMemoryWithTag(&pMuxNdisRequest, sizeof(MUX_NDIS_REQUEST), TAG);
if (pMuxNdisRequest == NULL)
{
break;
}
pMuxNdisRequest->pVElan = NULL; // internal request
//
// Set up completion routine.
//
pMuxNdisRequest->pCallback = PtCompleteBlockingRequest;
NdisInitializeEvent(&pMuxNdisRequest->Event);
pMuxNdisRequest->Request.RequestType = NdisRequestQueryInformation;
pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.Oid = Oid;
pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.InformationBuffer =
InformationBuffer;
pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.InformationBufferLength =
InformationBufferLength;
NdisRequest(&Status,
pAdapt->BindingHandle,
&pMuxNdisRequest->Request);
if (Status == NDIS_STATUS_PENDING)
{
NdisWaitEvent(&pMuxNdisRequest->Event, 0);
Status = pMuxNdisRequest->Status;
}
}
while (FALSE);
if (NULL != pMuxNdisRequest)
{
NdisFreeMemory(pMuxNdisRequest, sizeof(MUX_NDIS_REQUEST), 0);
}
}
VOID
PtRequestAdapterAsync(
IN PADAPT pAdapt,
IN NDIS_REQUEST_TYPE RequestType,
IN NDIS_OID Oid,
IN PVOID InformationBuffer,
IN ULONG InformationBufferLength,
IN PMUX_REQ_COMPLETE_HANDLER pCallback
)
/*++
Routine Description:
Utility routine to query the adapter for a single OID value.
This completes asynchronously, i.e. the calling thread is
not blocked until the request completes.
Arguments:
pAdapt Pointer to the adapter
RequestType NDIS request type
Oid OID to set/query
InformationBuffer Input/output buffer
InformationBufferLength Length of the above
pCallback Function to call on request completion
Return Value:
None.
--*/
{
PMUX_NDIS_REQUEST pMuxNdisRequest = NULL;
PNDIS_REQUEST pNdisRequest;
NDIS_STATUS Status;
do
{
NdisAllocateMemoryWithTag(&pMuxNdisRequest, sizeof(MUX_NDIS_REQUEST), TAG);
if (pMuxNdisRequest == NULL)
{
break;
}
pMuxNdisRequest->pVElan = NULL; // internal request
//
// Set up completion routine.
//
pMuxNdisRequest->pCallback = pCallback;
pNdisRequest = &pMuxNdisRequest->Request;
pNdisRequest->RequestType = RequestType;
switch (RequestType)
{
case NdisRequestQueryInformation:
pNdisRequest->DATA.QUERY_INFORMATION.Oid = Oid;
pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer =
InformationBuffer;
pNdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength =
InformationBufferLength;
break;
case NdisRequestSetInformation:
pNdisRequest->DATA.SET_INFORMATION.Oid = Oid;
pNdisRequest->DATA.SET_INFORMATION.InformationBuffer =
InformationBuffer;
pNdisRequest->DATA.SET_INFORMATION.InformationBufferLength =
InformationBufferLength;
break;
default:
ASSERT(FALSE);
break;
}
NdisRequest(&Status,
pAdapt->BindingHandle,
pNdisRequest);
if (Status != NDIS_STATUS_PENDING)
{
PtRequestComplete(
(NDIS_HANDLE)pAdapt,
pNdisRequest,
Status);
}
}
while (FALSE);
}
VOID
PtUnbindAdapter(
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_HANDLE UnbindContext
)
/*++
Routine Description:
Called by NDIS when we are required to unbind to the adapter below.
Go through all VELANs on the adapter and shut them down.
Arguments:
Status Placeholder for return status
ProtocolBindingContext Pointer to the adapter structure
UnbindContext Context for NdisUnbindComplete() if this pends
Return Value:
Status from closing the binding.
--*/
{
PADAPT pAdapt =(PADAPT)ProtocolBindingContext;
PLIST_ENTRY p;
PVELAN pVElan = NULL;
LOCK_STATE LockState;
UNREFERENCED_PARAMETER(UnbindContext);
DBGPRINT(MUX_LOUD, ("==> PtUnbindAdapter: Adapt %p\n", pAdapt));
//
// Stop all VELANs associated with the adapter.
// Repeatedly find the first unprocessed VELAN on
// the adapter, mark it, and stop it.
//
MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState);
do
{
for (p = pAdapt->VElanList.Flink;
p != &pAdapt->VElanList;
p = p->Flink)
{
pVElan = CONTAINING_RECORD(p, VELAN, Link);
if (!pVElan->DeInitializing)
{
pVElan->DeInitializing = TRUE;
break;
}
}
if (p != &pAdapt->VElanList)
{
ASSERT(pVElan == CONTAINING_RECORD(p, VELAN, Link));
//
// Got a VELAN to stop. Add a temp ref
// so that the VELAN won't go away when
// we release the ADAPT lock below.
//
PtReferenceVElan(pVElan, (PUCHAR)"UnbindTemp");
//
// Release the read lock because we want to
// run StopVElan at passive IRQL.
//
MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState);
PtStopVElan(pVElan);
PtDereferenceVElan(pVElan, (PUCHAR)"UnbindTemp");
MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState);
}
else
{
//
// No unmarked VELAN, so exit.
//
break;
}
}
while (TRUE);
//
// Wait until all VELANs are unlinked from the adapter.
// This is so that we don't attempt to forward down packets
// and/or requests from VELANs after calling NdisCloseAdapter.
//
while (!IsListEmpty(&pAdapt->VElanList))
{
MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState);
DBGPRINT(MUX_INFO, ("PtUnbindAdapter: pAdapt %p, VELANlist not yet empty\n",
pAdapt));
NdisMSleep(2000);
MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState);
}
MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState);
//
// Close the binding to the lower adapter.
//
if (pAdapt->BindingHandle != NULL)
{
NdisResetEvent(&pAdapt->Event);
NdisCloseAdapter(Status, pAdapt->BindingHandle);
//
// Wait for it to complete.
//
if (*Status == NDIS_STATUS_PENDING)
{
NdisWaitEvent(&pAdapt->Event, 0);
*Status = pAdapt->Status;
}
}
else
{
//
// Binding Handle should not be NULL.
//
*Status = NDIS_STATUS_FAILURE;
ASSERT(0);
}
//
// Remove the adapter from the global AdapterList
//
MUX_ACQUIRE_MUTEX(&GlobalMutex);
RemoveEntryList(&pAdapt->Link);
MUX_RELEASE_MUTEX(&GlobalMutex);
//
// Free all the resources associated with this Adapter except the
// ADAPT struct itself, because that will be freed by
// PtDereferenceAdapter call when the reference drops to zero.
// Note: Every VELAN associated with this Adapter takes a ref count
// on it. So the adapter memory wouldn't be freed until all the VELANs
// are shutdown.
//
PtDereferenceAdapter(pAdapt, (PUCHAR)"Unbind");
DBGPRINT(MUX_INFO, ("<== PtUnbindAdapter: Adapt %p\n", pAdapt));
}
VOID
PtCloseAdapterComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status
)
/*++
Routine Description:
Completion for the CloseAdapter call.
Arguments:
ProtocolBindingContext Pointer to the adapter structure
Status Completion status
Return Value:
None.
--*/
{
PADAPT pAdapt =(PADAPT)ProtocolBindingContext;
DBGPRINT(MUX_INFO, ("==> PtCloseAdapterComplete: Adapt %p, Status %x\n",
pAdapt, Status));
pAdapt->Status = Status;
NdisSetEvent(&pAdapt->Event);
}
VOID
PtResetComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status
)
/*++
Routine Description:
Completion for the reset.
Arguments:
ProtocolBindingContext Pointer to the adapter structure
Status Completion status
Return Value:
None.
--*/
{
#if DBG
PADAPT pAdapt =(PADAPT)ProtocolBindingContext;
#endif
#if !DBG
UNREFERENCED_PARAMETER(ProtocolBindingContext);
UNREFERENCED_PARAMETER(Status);
#endif
DBGPRINT(MUX_ERROR, ("==> PtResetComplete: Adapt %p, Status %x\n",
pAdapt, Status));
//
// We never issue a reset, so we should not be here.
//
ASSERT(0);
}
VOID
PtRequestComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_REQUEST NdisRequest,
IN NDIS_STATUS Status
)
/*++
Routine Description:
Completion handler for an NDIS request sent to a lower
miniport.
Arguments:
ProtocolBindingContext Pointer to the adapter structure
NdisRequest The completed request
Status Completion status
Return Value:
None
--*/
{
PADAPT pAdapt = (PADAPT)ProtocolBindingContext;
PMUX_NDIS_REQUEST pMuxNdisRequest;
pMuxNdisRequest = CONTAINING_RECORD(NdisRequest, MUX_NDIS_REQUEST, Request);
ASSERT(pMuxNdisRequest->pCallback != NULL);
//
// Completion is handled by the callback routine:
//
(*pMuxNdisRequest->pCallback)(pAdapt,
pMuxNdisRequest,
Status);
}
VOID
PtCompleteForwardedRequest(
IN PADAPT pAdapt,
IN PMUX_NDIS_REQUEST pMuxNdisRequest,
IN NDIS_STATUS Status
)
/*++
Routine Description:
Handle completion of an NDIS request that was originally
submitted to our VELAN miniport and was forwarded down
to the lower binding.
We do some postprocessing, to cache the results of
certain queries.
Arguments:
pAdapt - Adapter on which the request was forwarded
pMuxNdisRequest - super-struct for request
Status - request completion status
Return Value:
None
--*/
{
PVELAN pVElan = NULL;
PNDIS_REQUEST pNdisRequest = &pMuxNdisRequest->Request;
NDIS_OID Oid = pNdisRequest->DATA.SET_INFORMATION.Oid;
UNREFERENCED_PARAMETER(pAdapt);
//
// Get the originating VELAN. The VELAN will not be dereferenced
// away until the pended request is completed.
//
pVElan = pMuxNdisRequest->pVElan;
ASSERT(pVElan != NULL);
ASSERT(pMuxNdisRequest == &pVElan->Request);
if (Status != NDIS_STATUS_SUCCESS)
{
DBGPRINT(MUX_WARN, ("PtCompleteForwardedReq: pVElan %p, OID %x, Status %x\n",
pVElan,
pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.Oid,
Status));
}
//
// Complete the original request.
//
switch (pNdisRequest->RequestType)
{
case NdisRequestQueryInformation:
*pVElan->BytesReadOrWritten =
pNdisRequest->DATA.QUERY_INFORMATION.BytesWritten;
*pVElan->BytesNeeded =
pNdisRequest->DATA.QUERY_INFORMATION.BytesNeeded;
//
// Before completing the request, do any necessary
// post-processing.
//
Oid = pNdisRequest->DATA.QUERY_INFORMATION.Oid;
if (Status == NDIS_STATUS_SUCCESS)
{
if (Oid == OID_GEN_LINK_SPEED)
{
NdisMoveMemory (&pVElan->LinkSpeed,
pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer,
sizeof(ULONG));
}
else if (Oid == OID_PNP_CAPABILITIES)
{
PtPostProcessPnPCapabilities(pVElan,
pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer,
pNdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength);
}
}
NdisMQueryInformationComplete(pVElan->MiniportAdapterHandle, Status);
break;
case NdisRequestSetInformation:
*pVElan->BytesReadOrWritten =
pNdisRequest->DATA.SET_INFORMATION.BytesRead;
*pVElan->BytesNeeded =
pNdisRequest->DATA.SET_INFORMATION.BytesNeeded;
//
// Before completing the request, cache relevant information
// in our structure.
//
if (Status == NDIS_STATUS_SUCCESS)
{
Oid = pNdisRequest->DATA.SET_INFORMATION.Oid;
switch (Oid)
{
case OID_GEN_CURRENT_LOOKAHEAD:
NdisMoveMemory(&pVElan->LookAhead,
pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer,
sizeof(ULONG));
break;
default:
break;
}
}
NdisMSetInformationComplete(pVElan->MiniportAdapterHandle, Status);
break;
default:
ASSERT(FALSE);
break;
}
MUX_DECR_PENDING_SENDS(pVElan);
}
VOID
PtPostProcessPnPCapabilities(
IN PVELAN pVElan,
IN PVOID InformationBuffer,
IN ULONG InformationBufferLength
)
/*++
Routine Description:
Postprocess a successfully completed query for OID_PNP_CAPABILITIES.
We modify the returned information slightly before completing
it to the VELAN above.
Arguments:
pVElan - Pointer to VELAN
InformationBuffer - points to buffer for the OID
InformationBufferLength - byte length of the above.
Return Value:
None
--*/
{
PNDIS_PNP_CAPABILITIES pPNPCapabilities;
PNDIS_PM_WAKE_UP_CAPABILITIES pPMstruct;
UNREFERENCED_PARAMETER(pVElan);
if (InformationBufferLength >= sizeof(NDIS_PNP_CAPABILITIES))
{
pPNPCapabilities = (PNDIS_PNP_CAPABILITIES)InformationBuffer;
//
// The following fields must be overwritten by an IM driver.
//
pPMstruct= &pPNPCapabilities->WakeUpCapabilities;
pPMstruct->MinMagicPacketWakeUp = NdisDeviceStateUnspecified;
pPMstruct->MinPatternWakeUp = NdisDeviceStateUnspecified;
pPMstruct->MinLinkChangeWakeUp = NdisDeviceStateUnspecified;
}
}
VOID
PtCompleteBlockingRequest(
IN PADAPT pAdapt,
IN PMUX_NDIS_REQUEST pMuxNdisRequest,
IN NDIS_STATUS Status
)
/*++
Routine Description:
Handle completion of an NDIS request that was originated
by this driver and the calling thread is blocked waiting
for completion.
Arguments:
pAdapt - Adapter on which the request was forwarded
pMuxNdisRequest - super-struct for request
Status - request completion status
Return Value:
None
--*/
{
UNREFERENCED_PARAMETER(pAdapt);
pMuxNdisRequest->Status = Status;
//
// The request was originated from this driver. Wake up the
// thread blocked for its completion.
//
pMuxNdisRequest->Status = Status;
NdisSetEvent(&pMuxNdisRequest->Event);
}
VOID
PtDiscardCompletedRequest(
IN PADAPT pAdapt,
IN PMUX_NDIS_REQUEST pMuxNdisRequest,
IN NDIS_STATUS Status
)
/*++
Routine Description:
Handle completion of an NDIS request that was originated
by this driver - the request is to be discarded.
Arguments:
pAdapt - Adapter on which the request was forwarded
pMuxNdisRequest - super-struct for request
Status - request completion status
Return Value:
None
--*/
{
UNREFERENCED_PARAMETER(pAdapt);
UNREFERENCED_PARAMETER(Status);
NdisFreeMemory(pMuxNdisRequest, sizeof(MUX_NDIS_REQUEST), 0);
}
VOID
PtStatus(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS GeneralStatus,
IN PVOID StatusBuffer,
IN UINT StatusBufferSize
)
/*++
Routine Description:
Handle a status indication on the lower binding (ADAPT).
If this is a media status indication, we also pass this
on to all associated VELANs.
Arguments:
ProtocolBindingContext Pointer to the adapter structure
GeneralStatus Status code
StatusBuffer Status buffer
StatusBufferSize Size of the status buffer
Return Value:
None
--*/
{
PADAPT pAdapt = (PADAPT)ProtocolBindingContext;
PLIST_ENTRY p;
PVELAN pVElan;
LOCK_STATE LockState;
DBGPRINT(MUX_LOUD, ("PtStatus: Adapt %p, Status %x\n", pAdapt, GeneralStatus));
do
{
//
// Ignore status indications that we aren't going
// to pass up.
//
if ((GeneralStatus != NDIS_STATUS_MEDIA_CONNECT) &&
(GeneralStatus != NDIS_STATUS_MEDIA_DISCONNECT))
{
break;
}
MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState);
for (p = pAdapt->VElanList.Flink;
p != &pAdapt->VElanList;
p = p->Flink)
{
pVElan = CONTAINING_RECORD(p, VELAN, Link);
MUX_INCR_PENDING_RECEIVES(pVElan);
//
// Should the indication be sent on this VELAN?
//
if ((pVElan->MiniportHalting) ||
(pVElan->MiniportAdapterHandle == NULL) ||
MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState))
{
MUX_DECR_PENDING_RECEIVES(pVElan);
if (MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState))
{
//
// Keep track of the lastest status to indicated when VELAN power is on
//
ASSERT((GeneralStatus == NDIS_STATUS_MEDIA_CONNECT) || (GeneralStatus == NDIS_STATUS_MEDIA_DISCONNECT));
pVElan->LatestUnIndicateStatus = GeneralStatus;
}
continue;
}
//
// Save the last indicated status when
pVElan->LastIndicatedStatus = GeneralStatus;
NdisMIndicateStatus(pVElan->MiniportAdapterHandle,
GeneralStatus,
StatusBuffer,
StatusBufferSize);
//
// Mark this so that we forward a status complete
// indication as well.
//
pVElan->IndicateStatusComplete = TRUE;
MUX_DECR_PENDING_RECEIVES(pVElan);
}
MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState);
}
while (FALSE);
}
VOID
PtStatusComplete(
IN NDIS_HANDLE ProtocolBindingContext
)
/*++
Routine Description:
Marks the end of a status indication. Pass it on to
associated VELANs if necessary.
Arguments:
ProtocolBindingContext - pointer to ADAPT
Return Value:
None.
--*/
{
PADAPT pAdapt = (PADAPT)ProtocolBindingContext;
PLIST_ENTRY p;
PVELAN pVElan;
LOCK_STATE LockState;
MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState);
for (p = pAdapt->VElanList.Flink;
p != &pAdapt->VElanList;
p = p->Flink)
{
pVElan = CONTAINING_RECORD(p, VELAN, Link);
MUX_INCR_PENDING_RECEIVES(pVElan);
//
// Should this indication be sent on this VELAN?
//
if ((pVElan->MiniportHalting) ||
(pVElan->MiniportAdapterHandle == NULL) ||
(!pVElan->IndicateStatusComplete) ||
(MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)))
{
MUX_DECR_PENDING_RECEIVES(pVElan);
continue;
}
pVElan->IndicateStatusComplete = FALSE;
NdisMIndicateStatusComplete(pVElan->MiniportAdapterHandle);
MUX_DECR_PENDING_RECEIVES(pVElan);
}
MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState);
}
VOID
PtSendComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET Packet,
IN NDIS_STATUS Status
)
/*++
Routine Description:
Called by NDIS when the miniport below had completed a send.
We complete the corresponding upper-edge send this represents.
The packet being completed belongs to our send packet pool,
however we store a pointer to the original packet this represents,
in the packet's reserved field.
Arguments:
ProtocolBindingContext - Points to ADAPT structure
Packet - Packet being completed by the lower miniport
Status - status of send
Return Value:
None
--*/
{
PVELAN pVElan;
PMUX_SEND_RSVD pSendReserved;
PNDIS_PACKET OriginalPacket;
#if IEEE_VLAN_SUPPORT
NDIS_PACKET_8021Q_INFO NdisPacket8021qInfo;
BOOLEAN IsTagInsert;
PNDIS_BUFFER pNdisBuffer;
PVOID pVa;
ULONG BufferLength;
#endif
UNREFERENCED_PARAMETER(ProtocolBindingContext);
pSendReserved = MUX_RSVD_FROM_SEND_PACKET(Packet);
OriginalPacket = pSendReserved->pOriginalPacket;
pVElan = pSendReserved->pVElan;
#if IEEE_VLAN_SUPPORT
//
// Check if we had inserted a tag header
//
IsTagInsert = FALSE;
NdisPacket8021qInfo.Value = NDIS_PER_PACKET_INFO_FROM_PACKET(
OriginalPacket,
Ieee8021QInfo);
if ((pVElan->VlanId != 0) || (NdisPacket8021qInfo.Value != NULL))
{
IsTagInsert = TRUE;
}
#endif
#ifndef WIN9X
NdisIMCopySendCompletePerPacketInfo(OriginalPacket, Packet);
#endif
//
// Update statistics.
//
if (Status == NDIS_STATUS_SUCCESS)
{
MUX_INCR_STATISTICS64(&pVElan->GoodTransmits);
}
else
{
MUX_INCR_STATISTICS(&pVElan->TransmitFailuresOther);
}
//
// Complete the original send.
//
NdisMSendComplete(pVElan->MiniportAdapterHandle,
OriginalPacket,
Status);
#if IEEE_VLAN_SUPPORT
//
// If we had inserted a tag header, then remove the header
// buffer and free it. We would also have created a new
// NDIS buffer to map part of the original packet's header;
// free that, too.
//
if (IsTagInsert)
{
pNdisBuffer = Packet->Private.Head;
#ifdef NDIS51_MINIPORT
NdisQueryBufferSafe(pNdisBuffer, &pVa, (PUINT)&BufferLength, NormalPagePriority);
#else
NdisQueryBuffer(pNdisBuffer, &pVa, &BufferLength);
#endif
if (pVa != NULL)
{
NdisFreeToNPagedLookasideList(&pVElan->TagLookaside, pVa);
}
NdisFreeBuffer(NDIS_BUFFER_LINKAGE(pNdisBuffer));
NdisFreeBuffer (pNdisBuffer);
}
#endif
//
// Free our packet.
//
NdisFreePacket(Packet);
//
// Note down send-completion.
//
MUX_DECR_PENDING_SENDS(pVElan);
}
VOID
PtTransferDataComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET Packet,
IN NDIS_STATUS Status,
IN UINT BytesTransferred
)
/*++
Routine Description:
Entry point called by NDIS to indicate completion of a call by us
to NdisTransferData. We locate the original packet and VELAN on
which our TransferData function (see MPTransferData) was called,
and complete the original request.
Arguments:
ProtocolBindingContext - lower binding context, pointer to ADAPT
Packet - Packet allocated by us
Status - Completion status
BytesTransferred - Number of bytes copied in
Return Value:
None
--*/
{
PVELAN pVElan;
PNDIS_PACKET pOriginalPacket;
PMUX_TD_RSVD pTDReserved;
UNREFERENCED_PARAMETER(ProtocolBindingContext);
pTDReserved = MUX_RSVD_FROM_TD_PACKET(Packet);
pOriginalPacket = pTDReserved->pOriginalPacket;
pVElan = pTDReserved->pVElan;
//
// Complete the original TransferData request.
//
NdisMTransferDataComplete(pVElan->MiniportAdapterHandle,
pOriginalPacket,
Status,
BytesTransferred);
//
// Free our packet.
//
NdisFreePacket(Packet);
}
BOOLEAN
PtMulticastMatch(
IN PVELAN pVElan,
IN PUCHAR pDstMac
)
/*++
Routine Description:
Check if the given multicast destination MAC address matches
any of the multicast address entries set on the VELAN.
NOTE: the caller is assumed to hold a READ/WRITE lock
to the parent ADAPT structure. This is so that the multicast
list on the VELAN is invariant for the duration of this call.
Arguments:
pVElan - VELAN to look in
pDstMac - Destination MAC address to compare
Return Value:
TRUE iff the address matches an entry in the VELAN
--*/
{
ULONG i;
UINT AddrCompareResult;
for (i = 0; i < pVElan->McastAddrCount; i++)
{
ETH_COMPARE_NETWORK_ADDRESSES_EQ(pVElan->McastAddrs[i],
pDstMac,
&AddrCompareResult);
if (AddrCompareResult == 0)
{
break;
}
}
return (i != pVElan->McastAddrCount);
}
BOOLEAN
PtMatchPacketToVElan(
IN PVELAN pVElan,
IN PUCHAR pDstMac,
IN BOOLEAN bIsMulticast,
IN BOOLEAN bIsBroadcast
)
/*++
Routine Description:
Check if the destination address of a received packet
matches the receive criteria on the specified VELAN.
NOTE: the caller is assumed to hold a READ/WRITE lock
to the parent ADAPT structure.
Arguments:
pVElan - VELAN to check on
pDstMac - Destination MAC address in received packet
bIsMulticast - is this a multicast address
bIsBroadcast - is this a broadcast address
Return Value:
TRUE iff this packet should be received on the VELAN
--*/
{
UINT AddrCompareResult;
ULONG PacketFilter;
BOOLEAN bPacketMatch;
PacketFilter = pVElan->PacketFilter;
//
// Handle the directed packet case first.
//
if (!bIsMulticast)
{
//
// If the VELAN is not in promisc. mode, check if
// the destination MAC address matches the local
// address.
//
if ((PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) == 0)
{
ETH_COMPARE_NETWORK_ADDRESSES_EQ(pVElan->CurrentAddress,
pDstMac,
&AddrCompareResult);
bPacketMatch = ((AddrCompareResult == 0) &&
((PacketFilter & NDIS_PACKET_TYPE_DIRECTED) != 0));
}
else
{
bPacketMatch = TRUE;
}
}
else
{
//
// Multicast or broadcast packet.
//
//
// Indicate if the filter is set to promisc mode ...
//
if ((PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS)
||
//
// or if this is a broadcast packet and the filter
// is set to receive all broadcast packets...
//
(bIsBroadcast &&
(PacketFilter & NDIS_PACKET_TYPE_BROADCAST))
||
//
// or if this is a multicast packet, and the filter is
// either set to receive all multicast packets, or
// set to receive specific multicast packets. In the
// latter case, indicate receive only if the destn
// MAC address is present in the list of multicast
// addresses set on the VELAN.
//
(!bIsBroadcast &&
((PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST) ||
((PacketFilter & NDIS_PACKET_TYPE_MULTICAST) &&
PtMulticastMatch(pVElan, pDstMac))))
)
{
bPacketMatch = TRUE;
}
else
{
//
// No protocols above are interested in this
// multicast/broadcast packet.
//
bPacketMatch = FALSE;
}
}
return (bPacketMatch);
}
NDIS_STATUS
PtReceive(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_HANDLE MacReceiveContext,
IN PVOID HeaderBuffer,
IN UINT HeaderBufferSize,
IN PVOID LookAheadBuffer,
IN UINT LookAheadBufferSize,
IN UINT PacketSize
)
/*++
Routine Description:
Handle receive data indicated up by the miniport below.
We forward this up to all VELANs that are eligible to
receive this packet:
- If this is directed to a broadcast/multicast address,
indicate up on all VELANs that have multicast or broadcast
or promisc. bits set in their packet filters.
- If this is a directed packet, indicate it up on all VELANs
that have the a matching MAC address or have the promisc.
bit set in their packet filters.
We acquire a read lock on the ADAPT structure to ensure
that the VELAN list on the adapter is undisturbed.
If the miniport below indicates packets, NDIS would more
likely call us at our ReceivePacket handler. However we
might be called here in certain situations even though
the miniport below has indicated a receive packet, e.g.
if the miniport had set packet status to NDIS_STATUS_RESOURCES.
Arguments:
<see DDK ref page for ProtocolReceive>
Return Value:
NDIS_STATUS_SUCCESS if we processed the receive successfully,
NDIS_STATUS_XXX error code if we discarded it.
--*/
{
PADAPT pAdapt =(PADAPT)ProtocolBindingContext;
PLIST_ENTRY p;
PVELAN pVElan;
PNDIS_PACKET MyPacket, Packet;
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PUCHAR pData;
PUCHAR pDstMac;
BOOLEAN bIsMulticast, bIsBroadcast;
PMUX_RECV_RSVD pRecvReserved;
LOCK_STATE LockState;
#if IEEE_VLAN_SUPPORT
VLAN_TAG_HEADER UNALIGNED * pTagHeader;
USHORT UNALIGNED * pTpid;
MUX_RCV_CONTEXT MuxRcvContext;
#endif
do
{
if (HeaderBufferSize != ETH_HEADER_SIZE)
{
Status = NDIS_STATUS_NOT_ACCEPTED;
break;
}
if (pAdapt->PacketFilter == 0)
{
//
// We could get receives in the interval between
// initiating a request to set the packet filter on
// the binding to 0 and completion of that request.
// Drop such packets.
//
Status = NDIS_STATUS_NOT_ACCEPTED;
break;
}
//
// Collect some information from the packet.
//
pData = (PUCHAR)HeaderBuffer;
pDstMac = pData;
bIsMulticast = ETH_IS_MULTICAST(pDstMac);
bIsBroadcast = ETH_IS_BROADCAST(pDstMac);
//
// Get at the packet, if any, indicated up by the miniport below.
//
Packet = NdisGetReceivedPacket(pAdapt->BindingHandle, MacReceiveContext);
//
// Lock down the VELAN list on the adapter so that
// no insertions/deletions to this list happen while
// we loop through it. The packet filter will also not
// change during the time we hold the read lock.
//
MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState);
for (p = pAdapt->VElanList.Flink;
p != &pAdapt->VElanList;
p = p->Flink)
{
BOOLEAN bIndicateReceive;
pVElan = CONTAINING_RECORD(p, VELAN, Link);
//
// Should the packet be indicated up on this VELAN?
//
bIndicateReceive = PtMatchPacketToVElan(pVElan,
pDstMac,
bIsMulticast,
bIsBroadcast);
if (!bIndicateReceive)
{
continue;
}
//
// Make sure we don't Halt the VELAN miniport while
// we are accessing it here. See MPHalt.
//
// Also don't indicate receives if the virtual miniport
// has been set to a low power state. A specific case
// is when the system is resuming from "Stand-by", if
// the lower adapter is restored to D0 before the upper
// miniports are.
//
//
MUX_INCR_PENDING_RECEIVES(pVElan);
if ((pVElan->MiniportHalting) ||
(MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)))
{
MUX_DECR_PENDING_RECEIVES(pVElan);
continue;
}
if (Packet != NULL)
{
//
// The miniport below did indicate up a packet. Use information
// from that packet to construct a new packet to indicate up.
//
//
// Get a packet off our receive pool and indicate that up.
//
NdisDprAllocatePacket(&Status,
&MyPacket,
pVElan->RecvPacketPoolHandle);
if (Status == NDIS_STATUS_SUCCESS)
{
//
// Make our packet point to data from the original
// packet. NOTE: this works only because we are
// indicating a receive directly from the context of
// our receive indication. If we need to queue this
// packet and indicate it from another thread context,
// we will also have to allocate a new buffer and copy
// over the packet contents, OOB data and per-packet
// information. This is because the packet data
// is available only for the duration of this
// receive indication call.
//
MyPacket->Private.Head = Packet->Private.Head;
MyPacket->Private.Tail = Packet->Private.Tail;
#if IEEE_VLAN_SUPPORT
Status = PtHandleRcvTagging(pVElan, Packet, MyPacket);
if (Status != NDIS_STATUS_SUCCESS)
{
NdisFreePacket(MyPacket);
MUX_DECR_PENDING_RECEIVES(pVElan);
continue;
}
#endif
//
// Get the original packet (it could be the same packet
// as the one received or a different one based on the
// number of layered miniports below) and set it on the
// indicated packet so the OOB data is visible correctly
// at protocols above.
//
NDIS_SET_ORIGINAL_PACKET(MyPacket,
NDIS_GET_ORIGINAL_PACKET(Packet));
NDIS_SET_PACKET_HEADER_SIZE(MyPacket, HeaderBufferSize);
//
// Copy packet flags.
//
NdisGetPacketFlags(MyPacket) = NdisGetPacketFlags(Packet);
//
// Force protocols above to make a copy if they want to hang
// on to data in this packet. This is because we are in our
// Receive handler (not ReceivePacket), and the original
// packet can't be accessed after we return from here.
//
NDIS_SET_PACKET_STATUS(MyPacket, NDIS_STATUS_RESOURCES);
//
// Set our context information in the packet. Since
// the original packet from the miniport below is not being
// queued up, set this to NULL:
//
pRecvReserved = MUX_RSVD_FROM_RECV_PACKET(MyPacket);
pRecvReserved->pOriginalPacket = NULL;
MUX_INCR_STATISTICS64(&pVElan->GoodReceives);
//
// By setting NDIS_STATUS_RESOURCES, we also know that
// we can reclaim this packet as soon as the call to
// NdisMIndicateReceivePacket returns.
//
NdisMIndicateReceivePacket(pVElan->MiniportAdapterHandle,
&MyPacket,
1);
//
// Reclaim the indicated packet. Since we had set its status
// to NDIS_STATUS_RESOURCES, we are guaranteed that protocols
// above are done with it. Our ReturnPacket handler will
// not be called for this packet, so call it ourselves.
//
MPReturnPacket((NDIS_HANDLE)pVElan, MyPacket);
//
// Done with this VELAN.
//
continue;
}
//
// else...
//
// Failed to allocate a packet to indicate up - fall through.
// We will still indicate up using the non-packet API, but
// other per-packet/OOB information won't be available
// to protocols above.
//
}
else
{
//
// The miniport below us uses the old-style (not packet)
// receive indication. Fall through.
//
}
//
// Fall through to here if the miniport below us has
// either not indicated an NDIS_PACKET or we could not
// allocate one.
//
//
// Mark the VELAN so that we will forward up a receive
// complete indication.
//
pVElan->IndicateRcvComplete = TRUE;
#if IEEE_VLAN_SUPPORT
//
// Get at the EtherType field.
//
pTpid = (PUSHORT)((PUCHAR)HeaderBuffer + 2 * ETH_LENGTH_OF_ADDRESS);
//
// Check if the EtherType indicates presence of a tag header.
//
if (*pTpid == TPID)
{
pTagHeader = (VLAN_TAG_HEADER UNALIGNED *)LookAheadBuffer;
//
// Drop this frame if it contains Routing information;
// we don't support this.
//
if (GET_CANONICAL_FORMAT_ID_FROM_TAG(pTagHeader) != 0)
{
Status = NDIS_STATUS_INVALID_PACKET;
MUX_DECR_PENDING_RECEIVES(pVElan);
MUX_INCR_STATISTICS(&pVElan->RcvFormatErrors);
continue;
}
//
// If there is a VLAN ID in this frame, and we have
// a configured VLAN ID for this VELAN, check if they
// are the same - drop if not.
//
if ((GET_VLAN_ID_FROM_TAG(pTagHeader) != (unsigned)0) &&
(pVElan->VlanId != (unsigned)0) &&
(ULONG)(GET_VLAN_ID_FROM_TAG(pTagHeader) != pVElan->VlanId))
{
Status = NDIS_STATUS_NOT_ACCEPTED;
MUX_DECR_PENDING_RECEIVES(pVElan);
MUX_INCR_STATISTICS(&pVElan->RcvVlanIdErrors);
continue;
}
//
// Copy information from the tag header to per-packet
// info fields.
//
MuxRcvContext.NdisPacket8021QInfo.Value = NULL;
COPY_TAG_INFO_FROM_HEADER_TO_PACKET_INFO(
MuxRcvContext.NdisPacket8021QInfo,
pTagHeader);
//
// Prepare for indicating up this frame (the tag
// header must be removed). First, copy in the real
// EtherType value from the tag header.
//
*pTpid = *((PUSHORT)((PUCHAR)LookAheadBuffer + sizeof(pTagHeader->TagInfo)));
//
// Account for removing the tag header.
//
LookAheadBuffer = (PVOID)((PUCHAR)LookAheadBuffer + VLAN_TAG_HEADER_SIZE);
LookAheadBufferSize -= VLAN_TAG_HEADER_SIZE;
PacketSize -= VLAN_TAG_HEADER_SIZE;
//
// Use MuxRcvContext to store context for the receive,
// to be used in MpTransferData, if called.
//
MuxRcvContext.TagHeaderLen = VLAN_TAG_HEADER_SIZE;
}
else
{
MuxRcvContext.TagHeaderLen = 0;
}
MuxRcvContext.MacRcvContext = MacReceiveContext;
//
// In order not to change the code a lot
//
MacReceiveContext = &MuxRcvContext;
#endif
MUX_INCR_STATISTICS64(&pVElan->GoodReceives);
//
// Indicate receive using the non-packet API.
//
NdisMEthIndicateReceive(pVElan->MiniportAdapterHandle,
MacReceiveContext,
HeaderBuffer,
HeaderBufferSize,
LookAheadBuffer,
LookAheadBufferSize,
PacketSize);
MUX_DECR_PENDING_RECEIVES(pVElan);
} // for (each VELAN)
MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState);
}
while(FALSE);
return Status;
}
VOID
PtReceiveComplete(
IN NDIS_HANDLE ProtocolBindingContext
)
/*++
Routine Description:
Called by the adapter below us when it is done indicating a batch of
received packets. We forward this up on all VELANs that need
this indication.
Arguments:
ProtocolBindingContext Pointer to our adapter structure.
Return Value:
None
--*/
{
PADAPT pAdapt = (PADAPT)ProtocolBindingContext;
PLIST_ENTRY p;
PVELAN pVElan;
LOCK_STATE LockState;
MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState);
for (p = pAdapt->VElanList.Flink;
p != &pAdapt->VElanList;
p = p->Flink)
{
pVElan = CONTAINING_RECORD(p, VELAN, Link);
if (pVElan->IndicateRcvComplete)
{
pVElan->IndicateRcvComplete = FALSE;
NdisMEthIndicateReceiveComplete(pVElan->MiniportAdapterHandle);
}
}
MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState);
}
INT
PtReceivePacket(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET Packet
)
/*++
Routine Description:
ReceivePacket handler. Called by NDIS if the miniport below supports
NDIS 4.0 style receives. Re-package the buffer chain in a new packet
and indicate the new packet to interested protocols above us.
Arguments:
ProtocolBindingContext - Pointer to our adapter structure.
Packet - Pointer to the packet
Return Value:
== 0 -> We are done with the packet
!= 0 -> We will keep the packet and call NdisReturnPackets() this
many times when done.
--*/
{
PADAPT pAdapt = (PADAPT)ProtocolBindingContext;
PVELAN pVElan;
PLIST_ENTRY p;
NDIS_STATUS Status;
NDIS_STATUS PacketStatus;
PNDIS_PACKET MyPacket;
PUCHAR pData;
PNDIS_BUFFER pNdisBuffer;
UINT FirstBufferLength;
UINT TotalLength;
PUCHAR pDstMac;
BOOLEAN bIsMulticast, bIsBroadcast;
PMUX_RECV_RSVD pRecvReserved;
ULONG ReturnCount;
LOCK_STATE LockState;
ReturnCount = 0;
do
{
if (pAdapt->PacketFilter == 0)
{
//
// We could get receives in the interval between
// initiating a request to set the packet filter on
// the binding to 0 and completion of that request.
// Drop such packets.
//
break;
}
#ifdef NDIS51
//
// Collect some information from the packet.
//
NdisGetFirstBufferFromPacketSafe(Packet,
&pNdisBuffer,
&pData,
&FirstBufferLength,
&TotalLength,
NormalPagePriority);
if (pNdisBuffer == NULL)
{
//
// Out of system resources. Drop this packet.
//
break;
}
#else
NdisGetFirstBufferFromPacket(Packet,
&pNdisBuffer,
&pData,
&FirstBufferLength,
&TotalLength);
#endif
pDstMac = pData;
bIsMulticast = ETH_IS_MULTICAST(pDstMac);
bIsBroadcast = ETH_IS_BROADCAST(pDstMac);
//
// Lock down the VELAN list on the adapter so that
// no insertions/deletions to this list happen while
// we loop through it. The packet filter will also not
// change during the time we hold the read lock.
//
MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState);
for (p = pAdapt->VElanList.Flink;
p != &pAdapt->VElanList;
p = p->Flink)
{
BOOLEAN bIndicateReceive;
pVElan = CONTAINING_RECORD(p, VELAN, Link);
//
// Should the packet be indicated up on this VELAN?
//
bIndicateReceive = PtMatchPacketToVElan(pVElan,
pDstMac,
bIsMulticast,
bIsBroadcast);
if (!bIndicateReceive)
{
continue;
}
//
// Make sure we don't Halt the VELAN miniport while
// we are accessing it here. See MPHalt.
//
// Also don't indicate receives if the virtual miniport
// has been set to a low power state. A specific case
// is when the system is resuming from "Stand-by", if
// the lower adapter is restored to D0 before the upper
// miniports are.
//
MUX_INCR_PENDING_RECEIVES(pVElan);
if ((pVElan->MiniportHalting) ||
(MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)))
{
MUX_DECR_PENDING_RECEIVES(pVElan);
continue;
}
//
// Get a packet off the pool and indicate that up
//
NdisDprAllocatePacket(&Status,
&MyPacket,
pVElan->RecvPacketPoolHandle);
if (Status == NDIS_STATUS_SUCCESS)
{
PacketStatus = NDIS_GET_PACKET_STATUS(Packet);
pRecvReserved = MUX_RSVD_FROM_RECV_PACKET(MyPacket);
if (PacketStatus != NDIS_STATUS_RESOURCES)
{
pRecvReserved->pOriginalPacket = Packet;
}
else
{
//
// This will ensure we don't call NdisReturnPacket for the packet if the packet
// status is NDIS_STATUS_RESOURCES
//
pRecvReserved->pOriginalPacket = NULL;
}
MyPacket->Private.Head = Packet->Private.Head;
MyPacket->Private.Tail = Packet->Private.Tail;
//
// Get the original packet (it could be the same
// packet as the one received or a different one
// based on the number of layered miniports below)
// and set it on the indicated packet so the OOB
// data is visible correctly to protocols above us.
//
NDIS_SET_ORIGINAL_PACKET(MyPacket, NDIS_GET_ORIGINAL_PACKET(Packet));
//
// Copy Packet Flags
//
NdisGetPacketFlags(MyPacket) = NdisGetPacketFlags(Packet);
NDIS_SET_PACKET_STATUS(MyPacket, PacketStatus);
NDIS_SET_PACKET_HEADER_SIZE(MyPacket, NDIS_GET_PACKET_HEADER_SIZE(Packet));
#if IEEE_VLAN_SUPPORT
Status = PtHandleRcvTagging(pVElan, Packet, MyPacket);
if (Status != NDIS_STATUS_SUCCESS)
{
NdisFreePacket(MyPacket);
MUX_DECR_PENDING_RECEIVES(pVElan);
continue;
}
#endif
MUX_INCR_STATISTICS64(&pVElan->GoodReceives);
//
// Indicate it up.
//
if (PacketStatus != NDIS_STATUS_RESOURCES)
{
ReturnCount++;
}
NdisMIndicateReceivePacket(pVElan->MiniportAdapterHandle,
&MyPacket,
1);
//
// Check if we had indicated up the packet with
// status set to NDIS_STATUS_RESOURCES.
//
// NOTE -- do not use NDIS_GET_PACKET_STATUS(MyPacket)
// for this since it might have changed! Use the value
// saved in the local variable.
//
if (PacketStatus == NDIS_STATUS_RESOURCES)
{
//
// Our ReturnPackets handler will not be called
// for this packet. We should reclaim it right here.
//
MPReturnPacket((NDIS_HANDLE)pVElan, MyPacket);
}
}
else
{
//
// Failed to allocate a packet.
//
MUX_INCR_STATISTICS(&pVElan->RcvResourceErrors);
MUX_DECR_PENDING_RECEIVES(pVElan);
}
} // for (loop thru all VELANs)
MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState);
}
while (FALSE);
//
// Return the # of receive indications made for this packet.
// We will call NdisReturnPackets for this packet as many
// times (see MPReturnPackets).
//
return (ReturnCount);
}
NDIS_STATUS
PtPnPNetEventSetPower(
IN PADAPT pAdapt,
IN PNET_PNP_EVENT pNetPnPEvent
)
/*++
Routine Description:
This is a notification to our protocol edge of the power state
of the lower miniport. If it is going to a low-power state, we must
wait here for all outstanding sends and requests to complete.
Arguments:
pAdapt - Pointer to the adpater structure
pNetPnPEvent - The Net Pnp Event. this contains the new device state
Return Value:
NDIS_STATUS_SUCCESS
--*/
{
PLIST_ENTRY p;
PVELAN pVElan;
LOCK_STATE LockState;
NDIS_STATUS Status;
//
// Store the new power state.
//
pAdapt->PtDevicePowerState = *(PNDIS_DEVICE_POWER_STATE)pNetPnPEvent->Buffer;
DBGPRINT(MUX_LOUD, ("PnPNetEventSetPower: Adapt %p, SetPower to %d\n",
pAdapt, pAdapt->PtDevicePowerState));
//
// Check if the miniport below is going to a low power state.
//
if (MUX_IS_LOW_POWER_STATE(pAdapt->PtDevicePowerState))
{
ULONG i;
//
// It is going to a low power state. Wait for outstanding
// I/O to complete on the adapter.
//
for (i = 0; i < 10000; i++)
{
MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState);
for (p = pAdapt->VElanList.Flink;
p != &pAdapt->VElanList;
p = p->Flink)
{
pVElan = CONTAINING_RECORD(p, VELAN, Link);
if ((pVElan->OutstandingSends != 0) ||
(pVElan->OutstandingReceives != 0))
{
break;
}
}
MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState);
if (p == &pAdapt->VElanList)
{
//
// There are no VELANs with pending I/O.
//
break;
}
DBGPRINT(MUX_INFO, ("SetPower: Adapt %p, waiting for pending IO to complete\n",
pAdapt));
NdisMSleep(1000);
}
}
else
{
//
// The device below is powered on. If we had requests
// pending on any VELANs, send them down now.
//
MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState);
for (p = pAdapt->VElanList.Flink;
p != &pAdapt->VElanList;
p = p->Flink)
{
pVElan = CONTAINING_RECORD(p, VELAN, Link);
//
// Need to make sure other threads do not try to acquire the write lock while holding
// the same spin lock
//
NdisAcquireSpinLock(&pVElan->Lock);
if (pVElan->QueuedRequest)
{
pVElan->QueuedRequest = FALSE;
NdisReleaseSpinLock(&pVElan->Lock);
NdisRequest(&Status,
pAdapt->BindingHandle,
&pVElan->Request.Request);
if (Status != NDIS_STATUS_PENDING)
{
PtRequestComplete(pAdapt,
&pVElan->Request.Request,
Status);
}
}
else
{
NdisReleaseSpinLock(&pVElan->Lock);
}
}
MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState);
}
return (NDIS_STATUS_SUCCESS);
}
NDIS_STATUS
PtPNPHandler(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNET_PNP_EVENT pNetPnPEvent
)
/*++
Routine Description:
This is called by NDIS to notify us of a PNP event related to a lower
binding. Based on the event, this dispatches to other helper routines.
Arguments:
ProtocolBindingContext - Pointer to our adapter structure. Can be NULL
for "global" notifications
pNetPnPEvent - Pointer to the PNP event to be processed.
Return Value:
NDIS_STATUS code indicating status of event processing.
--*/
{
PADAPT pAdapt =(PADAPT)ProtocolBindingContext;
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PLIST_ENTRY p;
DBGPRINT(MUX_LOUD, ("PtPnPHandler: Adapt %p, NetPnPEvent %d\n", pAdapt,
pNetPnPEvent->NetEvent));
switch (pNetPnPEvent->NetEvent)
{
case NetEventSetPower:
Status = PtPnPNetEventSetPower(pAdapt, pNetPnPEvent);
break;
case NetEventReconfigure:
//
// Rescan configuration and bring up any VELANs that
// have been newly added. Make sure that the global
// adapter list is undisturbed while we traverse it.
//
MUX_ACQUIRE_MUTEX(&GlobalMutex);
for (p = AdapterList.Flink;
p != &AdapterList;
p = p->Flink)
{
pAdapt = CONTAINING_RECORD(p, ADAPT, Link);
PtBootStrapVElans(pAdapt);
}
MUX_RELEASE_MUTEX(&GlobalMutex);
Status = NDIS_STATUS_SUCCESS;
break;
default:
Status = NDIS_STATUS_SUCCESS;
break;
}
return Status;
}
NDIS_STATUS
PtCreateAndStartVElan(
IN PADAPT pAdapt,
IN PNDIS_STRING pVElanKey
)
/*++
Routine Description:
Create and start a VELAN with the given key name. Check if a VELAN
with this key name already exists; if so do nothing.
ASSUMPTION: this is called from either the BindAdapter handler for
the underlying adapter, or from the PNP reconfig handler. Both these
routines are protected by NDIS against pre-emption by UnbindAdapter.
If this routine will be called from any other context, it should
be protected against a simultaneous call to our UnbindAdapter handler.
Arguments:
pAdapt - Pointer to Adapter structure
pVElanKey - Points to a Unicode string naming the VELAN to create.
Return Value:
NDIS_STATUS_SUCCESS if we either found a duplicate VELAN or
successfully initiated a new ELAN with the given key.
NDIS_STATUS_XXX error code otherwise (failure initiating a new VELAN).
--*/
{
NDIS_STATUS Status;
PVELAN pVElan;
Status = NDIS_STATUS_SUCCESS;
pVElan = NULL;
DBGPRINT(MUX_LOUD, ("=> Create VElan: Adapter %p, ElanKey %ws\n",
pAdapt, pVElanKey->Buffer));
do
{
//
// Weed out duplicates.
//
if (pVElanKey != NULL)
{
pVElan = PtFindVElan(pAdapt, pVElanKey);
if (NULL != pVElan)
{
//
// Duplicate - bail out silently.
//
DBGPRINT(MUX_WARN, ("CreateElan: found duplicate pVElan %x\n", pVElan));
Status = NDIS_STATUS_SUCCESS;
pVElan = NULL;
break;
}
}
pVElan = PtAllocateAndInitializeVElan(pAdapt, pVElanKey);
if (pVElan == NULL)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
//
// Request NDIS to initialize the virtual miniport. Set
// the flag below just in case an unbind occurs before
// MiniportInitialize is called.
//
pVElan->MiniportInitPending = TRUE;
NdisInitializeEvent(&pVElan->MiniportInitEvent);
Status = NdisIMInitializeDeviceInstanceEx(DriverHandle,
&pVElan->CfgDeviceName,
pVElan);
if (Status != NDIS_STATUS_SUCCESS)
{
PtUnlinkVElanFromAdapter(pVElan); // IMInit failed
pVElan = NULL;
break;
}
}
while (FALSE);
DBGPRINT(MUX_INFO, ("<= Create VElan: Adapter %p, VELAN %p\n", pAdapt, pVElan));
return Status;
}
PVELAN
PtAllocateAndInitializeVElan(
IN PADAPT pAdapt,
IN PNDIS_STRING pVElanKey
)
/*++
Routine Description:
Allocates and initializes a VELAN structure. Also links it to
the specified ADAPT.
Arguments:
pAdapt - Adapter to link VELAN to
pVElanKey - Key to the VELAN
Return Value:
Pointer to VELAN structure if successful, NULL otherwise.
--*/
{
PVELAN pVElan;
ULONG Length;
NDIS_STATUS Status;
LOCK_STATE LockState;
pVElan = NULL;
Status = NDIS_STATUS_SUCCESS;
do
{
Length = sizeof(VELAN) + pVElanKey->Length + sizeof(WCHAR);
//
// Allocate a VELAN data structure.
//
NdisAllocateMemoryWithTag(&pVElan, Length, TAG);
if (pVElan == NULL)
{
DBGPRINT(MUX_FATAL, ("AllocateVElan: Failed to allocate %d bytes for VELAN\n",
Length));
Status = NDIS_STATUS_RESOURCES;
break;
}
//
// Initialize it.
//
NdisZeroMemory(pVElan, Length);
NdisInitializeListHead(&pVElan->Link);
//
// Initialize the built-in request structure to signify
// that it is used to forward NDIS requests.
//
pVElan->Request.pVElan = pVElan;
NdisInitializeEvent(&pVElan->Request.Event);
//
// Store in the key name.
//
pVElan->CfgDeviceName.Length = 0;
pVElan->CfgDeviceName.Buffer = (PWCHAR)((PUCHAR)pVElan +
sizeof(VELAN));
pVElan->CfgDeviceName.MaximumLength =
pVElanKey->MaximumLength + sizeof(WCHAR);
(VOID)NdisUpcaseUnicodeString(&pVElan->CfgDeviceName, pVElanKey);
pVElan->CfgDeviceName.Buffer[pVElanKey->Length/sizeof(WCHAR)] =
((WCHAR)0);
//
// Initialize LastIndicatedStatus to media connect
//
pVElan->LastIndicatedStatus = NDIS_STATUS_MEDIA_CONNECT;
//
// Set power state of virtual miniport to D0.
//
pVElan->MPDevicePowerState = NdisDeviceStateD0;
//
// Cache the binding handle for quick reference.
//
pVElan->BindingHandle = pAdapt->BindingHandle;
pVElan->pAdapt = pAdapt;
//
// Copy in some adapter parameters.
//
pVElan->LookAhead = pAdapt->MaxLookAhead;
pVElan->LinkSpeed = pAdapt->LinkSpeed;
NdisMoveMemory(pVElan->PermanentAddress,
pAdapt->CurrentAddress,
sizeof(pVElan->PermanentAddress));
NdisMoveMemory(pVElan->CurrentAddress,
pAdapt->CurrentAddress,
sizeof(pVElan->CurrentAddress));
DBGPRINT(MUX_LOUD, ("Alloced VELAN %p, MAC addr %s\n",
pVElan, MacAddrToString(pVElan->CurrentAddress)));
NdisAllocateSpinLock(&pVElan->Lock);
#if IEEE_VLAN_SUPPORT
//
// Allocate lookaside list for tag headers.
//
NdisInitializeNPagedLookasideList (
&pVElan->TagLookaside,
NULL,
NULL,
0,
ETH_HEADER_SIZE + VLAN_TAG_HEADER_SIZE,
'TxuM',
0);
#endif
//
// Allocate a packet pool for sends.
//
NdisAllocatePacketPoolEx(&Status,
&pVElan->SendPacketPoolHandle,
MIN_PACKET_POOL_SIZE,
MAX_PACKET_POOL_SIZE - MIN_PACKET_POOL_SIZE,
sizeof(MUX_SEND_RSVD));
if (Status != NDIS_STATUS_SUCCESS)
{
DBGPRINT(MUX_FATAL, ("PtAllocateVElan: failed to allocate send packet pool\n"));
break;
}
//
// NOTE: this sample driver does not -originate- packets in the
// send or receive directions. If the driver must originate packets,
// here is a good place to allocate NDIS buffer pool(s) for
// this purpose.
//
#if IEEE_VLAN_SUPPORT
//
// Allocate a buffer pool for tag headers.
//
NdisAllocateBufferPool (&Status,
&pVElan->BufferPoolHandle,
MIN_PACKET_POOL_SIZE);
ASSERT(Status == NDIS_STATUS_SUCCESS);
#endif
//
// Allocate a packet pool for receives.
//
NdisAllocatePacketPoolEx(&Status,
&pVElan->RecvPacketPoolHandle,
MIN_PACKET_POOL_SIZE,
MAX_PACKET_POOL_SIZE - MIN_PACKET_POOL_SIZE,
PROTOCOL_RESERVED_SIZE_IN_PACKET);
if (Status != NDIS_STATUS_SUCCESS)
{
DBGPRINT(MUX_FATAL, ("PtAllocateVElan: failed to allocate receive packet pool\n"));
break;
}
//
// Finally link this VELAN to the Adapter's VELAN list.
//
PtReferenceVElan(pVElan, (PUCHAR)"adapter");
MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState);
PtReferenceAdapter(pAdapt, (PUCHAR)"VElan");
InsertTailList(&pAdapt->VElanList, &pVElan->Link);
pAdapt->VElanCount++;
pVElan->VElanNumber = NdisInterlockedIncrement((PLONG)&NextVElanNumber);
MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState);
}
while (FALSE);
if (Status != NDIS_STATUS_SUCCESS)
{
if (pVElan)
{
PtDeallocateVElan(pVElan);
pVElan = NULL;
}
}
return (pVElan);
}
VOID
PtDeallocateVElan(
IN PVELAN pVElan
)
/*++
Routine Description:
Free up all resources allocated to a VELAN, and then the VELAN
structure itself.
Arguments:
pVElan - Pointer to VELAN to be deallocated.
Return Value:
None
--*/
{
if (pVElan->SendPacketPoolHandle != NULL)
{
NdisFreePacketPool(pVElan->SendPacketPoolHandle);
}
if (pVElan->RecvPacketPoolHandle != NULL)
{
NdisFreePacketPool(pVElan->RecvPacketPoolHandle);
}
#if IEEE_VLAN_SUPPORT
NdisFreeBufferPool(pVElan->BufferPoolHandle);
NdisDeleteNPagedLookasideList(&pVElan->TagLookaside);
#endif
NdisFreeMemory(pVElan, 0, 0);
}
VOID
PtStopVElan(
IN PVELAN pVElan
)
/*++
Routine Description:
Stop a VELAN by requesting NDIS to halt the virtual miniport.
The caller has a reference on the VELAN, so it won't go away
while we are executing in this routine.
ASSUMPTION: this is only called in the context of unbinding
from the underlying miniport. If it may be called from elsewhere,
this should protect itself from re-entrancy.
Arguments:
pVElan - Pointer to VELAN to be stopped.
Return Value:
None
--*/
{
NDIS_STATUS Status;
NDIS_HANDLE MiniportAdapterHandle;
BOOLEAN bMiniportInitCancelled = FALSE;
DBGPRINT(MUX_LOUD, ("=> StopVElan: VELAN %p, Adapt %p\n", pVElan, pVElan->pAdapt));
//
// We make blocking calls below.
//
ASSERT_AT_PASSIVE();
//
// If there was a queued request on this VELAN, fail it now.
//
NdisAcquireSpinLock(&pVElan->Lock);
ASSERT(pVElan->DeInitializing == TRUE);
if (pVElan->QueuedRequest)
{
pVElan->QueuedRequest = FALSE;
NdisReleaseSpinLock(&pVElan->Lock);
PtRequestComplete(pVElan->pAdapt,
&pVElan->Request.Request,
NDIS_STATUS_FAILURE);
}
else
{
NdisReleaseSpinLock(&pVElan->Lock);
}
//
// Check if we had called NdisIMInitializeDeviceInstanceEx and
// we are awaiting a call to MiniportInitialize.
//
if (pVElan->MiniportInitPending)
{
//
// Attempt to cancel miniport init.
//
Status = NdisIMCancelInitializeDeviceInstance(
DriverHandle,
&pVElan->CfgDeviceName);
if (Status == NDIS_STATUS_SUCCESS)
{
//
// Successfully cancelled IM initialization; our
// Miniport Init routine will not be called for this
// VELAN miniport.
//
pVElan->MiniportInitPending = FALSE;
ASSERT(pVElan->MiniportAdapterHandle == NULL);
bMiniportInitCancelled = TRUE;
}
else
{
//
// Our Miniport Initialize routine will be called
// (may be running on another thread at this time).
// Wait for it to finish.
//
NdisWaitEvent(&pVElan->MiniportInitEvent, 0);
ASSERT(pVElan->MiniportInitPending == FALSE);
}
}
//
// Check if Miniport Init has run. If so, deinitialize the virtual
// miniport. This will result in a call to our Miniport Halt routine,
// where the VELAN will be cleaned up.
//
MiniportAdapterHandle = pVElan->MiniportAdapterHandle;
if ((NULL != MiniportAdapterHandle) &&
(!pVElan->MiniportHalting))
{
//
// The miniport was initialized, and has not yet halted.
//
ASSERT(bMiniportInitCancelled == FALSE);
(VOID)NdisIMDeInitializeDeviceInstance(MiniportAdapterHandle);
}
else
{
if (bMiniportInitCancelled)
{
//
// No NDIS events can come to this VELAN since it
// was never initialized as a miniport. We need to unlink
// it explicitly here.
//
PtUnlinkVElanFromAdapter(pVElan);
}
}
}
VOID
PtUnlinkVElanFromAdapter(
IN PVELAN pVElan
)
/*++
Routine Description:
Utility routine to unlink a VELAN from its parent ADAPT structure.
Arguments:
pVElan - Pointer to VELAN to be unlinked.
Return Value:
None
--*/
{
PADAPT pAdapt = pVElan->pAdapt;
LOCK_STATE LockState;
ASSERT(pAdapt != NULL);
//
// Remove this VELAN from the Adapter list
//
MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState);
RemoveEntryList(&pVElan->Link);
pAdapt->VElanCount--;
MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState);
pVElan->pAdapt = NULL;
PtDereferenceVElan(pVElan, (PUCHAR)"adapter");
PtDereferenceAdapter(pAdapt, (PUCHAR)"VElan");
}
PVELAN
PtFindVElan(
IN PADAPT pAdapt,
IN PNDIS_STRING pVElanKey
)
/*++
Routine Description:
Find an ELAN by bind name/key
Arguments:
pAdapt - Pointer to an adapter struct.
pVElanKey - The VELAN's device name
Return Value:
Pointer to matching VELAN or NULL if not found.
--*/
{
PLIST_ENTRY p;
PVELAN pVElan;
BOOLEAN Found;
NDIS_STRING VElanKeyName;
LOCK_STATE LockState;
ASSERT_AT_PASSIVE();
DBGPRINT(MUX_LOUD, ("FindElan: Adapter %p, ElanKey %ws\n", pAdapt,
pVElanKey->Buffer));
pVElan = NULL;
Found = FALSE;
VElanKeyName.Buffer = NULL;
do
{
//
// Make an up-cased copy of the given string.
//
NdisAllocateMemoryWithTag(&VElanKeyName.Buffer,
pVElanKey->MaximumLength, TAG);
if (VElanKeyName.Buffer == NULL)
{
break;
}
VElanKeyName.Length = pVElanKey->Length;
VElanKeyName.MaximumLength = pVElanKey->MaximumLength;
(VOID)NdisUpcaseUnicodeString(&VElanKeyName, pVElanKey);
//
// Go through all VELANs on the ADAPT structure, looking
// for a VELAN that has a matching device name.
//
MUX_ACQUIRE_ADAPT_READ_LOCK(pAdapt, &LockState);
p = pAdapt->VElanList.Flink;
while (p != &pAdapt->VElanList)
{
pVElan = CONTAINING_RECORD(p, VELAN, Link);
if ((VElanKeyName.Length == pVElan->CfgDeviceName.Length) &&
(memcmp(VElanKeyName.Buffer, pVElan->CfgDeviceName.Buffer,
VElanKeyName.Length) == 0))
{
Found = TRUE;
break;
}
p = p->Flink;
}
MUX_RELEASE_ADAPT_READ_LOCK(pAdapt, &LockState);
}
while (FALSE);
if (!Found)
{
DBGPRINT(MUX_INFO, ( "FindElan: No match found!\n"));
pVElan = NULL;
}
if (VElanKeyName.Buffer)
{
NdisFreeMemory(VElanKeyName.Buffer, VElanKeyName.Length, 0);
}
return pVElan;
}
NDIS_STATUS
PtBootStrapVElans(
IN PADAPT pAdapt
)
/*++
Routine Description:
Start up the VELANs configured for an adapter.
Arguments:
pAdapt - Pointer to ATMLANE Adapter structure
Return Value:
None
--*/
{
NDIS_STATUS Status;
NDIS_HANDLE AdapterConfigHandle;
PNDIS_CONFIGURATION_PARAMETER Param;
NDIS_STRING DeviceStr = NDIS_STRING_CONST("UpperBindings");
PWSTR buffer;
LOCK_STATE LockState;
//
// Initialize.
//
Status = NDIS_STATUS_SUCCESS;
AdapterConfigHandle = NULL;
do
{
DBGPRINT(MUX_LOUD, ("BootStrapElans: Starting ELANs on adapter %x\n", pAdapt));
//
// Open the protocol configuration section for this adapter.
//
NdisOpenProtocolConfiguration(&Status,
&AdapterConfigHandle,
&pAdapt->ConfigString);
if (NDIS_STATUS_SUCCESS != Status)
{
AdapterConfigHandle = NULL;
DBGPRINT(MUX_ERROR, ("BootStrapElans: OpenProtocolConfiguration failed\n"));
Status = NDIS_STATUS_OPEN_FAILED;
break;
}
//
// Read the "UpperBindings" reserved key that contains a list
// of device names representing our miniport instances corresponding
// to this lower binding. The UpperBindings is a
// MULTI_SZ containing a list of device names. We will loop through
// this list and initialize the virtual miniports.
//
NdisReadConfiguration(&Status,
&Param,
AdapterConfigHandle,
&DeviceStr,
NdisParameterMultiString);
if (NDIS_STATUS_SUCCESS != Status)
{
DBGPRINT(MUX_ERROR, ("BootStrapElans: NdisReadConfiguration failed\n"));
break;
}
//
// Parse the Multi_sz string to extract the device name of each VELAN.
// This is used as the key name for the VELAN.
//
buffer = (PWSTR)Param->ParameterData.StringData.Buffer;
while(*buffer != L'\0')
{
NDIS_STRING DeviceName;
NdisInitUnicodeString(&DeviceName, buffer);
Status = PtCreateAndStartVElan(pAdapt, &DeviceName);
if (NDIS_STATUS_SUCCESS != Status)
{
DBGPRINT(MUX_ERROR, ("BootStrapElans: CreateVElan failed\n"));
break;
}
buffer = (PWSTR)((PUCHAR)buffer + DeviceName.Length + sizeof(WCHAR));
};
} while (FALSE);
//
// Close config handles
//
if (NULL != AdapterConfigHandle)
{
NdisCloseConfiguration(AdapterConfigHandle);
}
//
// If the driver cannot create any velan for the adapter
//
if (Status != NDIS_STATUS_SUCCESS)
{
MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState);
//
// No VElan is created for this adapter
//
if (pAdapt->VElanCount != 0)
{
Status = NDIS_STATUS_SUCCESS;
}
MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState);
}
return Status;
}
VOID
PtReferenceVElan(
IN PVELAN pVElan,
IN PUCHAR String
)
/*++
Routine Description:
Add a references to an Elan structure.
Arguments:
pElan - Pointer to the Elan structure.
Return Value:
None.
--*/
{
NdisInterlockedIncrement((PLONG)&pVElan->RefCount);
#if !DBG
UNREFERENCED_PARAMETER(String);
#endif
DBGPRINT(MUX_LOUD, ("ReferenceElan: Elan %p (%s) new count %d\n",
pVElan, String, pVElan->RefCount));
return;
}
ULONG
PtDereferenceVElan(
IN PVELAN pVElan,
IN PUCHAR String
)
/*++
Routine Description:
Subtract a reference from an VElan structure.
If the reference count becomes zero, deallocate it.
Arguments:
pElan - Pointer to an VElan structure.
Return Value:
None.
--*/
{
ULONG rc;
#if !DBG
UNREFERENCED_PARAMETER(String);
#endif
ASSERT(pVElan->RefCount > 0);
rc = NdisInterlockedDecrement((PLONG)&pVElan->RefCount);
if (rc == 0)
{
//
// Free memory if there is no outstanding reference.
// Note: Length field is not required if the memory
// is allocated with NdisAllocateMemoryWithTag.
//
PtDeallocateVElan(pVElan);
}
DBGPRINT(MUX_LOUD, ("DereferenceElan: VElan %p (%s) new count %d\n",
pVElan, String, rc));
return (rc);
}
BOOLEAN
PtReferenceAdapter(
IN PADAPT pAdapt,
IN PUCHAR String
)
/*++
Routine Description:
Add a references to an Adapter structure.
Arguments:
pAdapt - Pointer to the Adapter structure.
Return Value:
None.
--*/
{
#if !DBG
UNREFERENCED_PARAMETER(String);
#endif
NdisInterlockedIncrement((PLONG)&pAdapt->RefCount);
DBGPRINT(MUX_LOUD, ("ReferenceAdapter: Adapter %x (%s) new count %d\n",
pAdapt, String, pAdapt->RefCount));
return TRUE;
}
ULONG
PtDereferenceAdapter(
IN PADAPT pAdapt,
IN PUCHAR String
)
/*++
Routine Description:
Subtract a reference from an Adapter structure.
If the reference count becomes zero, deallocate it.
Arguments:
pAdapt - Pointer to an adapter structure.
Return Value:
None.
--*/
{
ULONG rc;
#if !DBG
UNREFERENCED_PARAMETER(String);
#endif
ASSERT(pAdapt->RefCount > 0);
rc = NdisInterlockedDecrement ((PLONG)&pAdapt->RefCount);
if (rc == 0)
{
//
// Free memory if there is no outstanding reference.
// Note: Length field is not required if the memory
// is allocated with NdisAllocateMemoryWithTag.
//
NdisFreeMemory(pAdapt, 0, 0);
}
DBGPRINT(MUX_LOUD, ("DereferenceAdapter: Adapter %x (%s) new count %d\n",
pAdapt, String, rc));
return (rc);
}
#if IEEE_VLAN_SUPPORT
NDIS_STATUS
PtHandleRcvTagging(
IN PVELAN pVElan,
IN PNDIS_PACKET Packet,
IN OUT PNDIS_PACKET MyPacket
)
/*++
Routine Description:
Parse a received Ethernet frame for 802.1Q tag information.
If a tag header is present, copy in relevant field values to
per-packet information to the new packet (MyPacket) used to
indicate up this frame.
Arguments:
pVElan - Pointer to the VELAN structure.
Packet - Pointer to the indicated packet from the lower miniport
MyPacket - Pointer to the new allocated packet
Return Value:
NDIS_STATUS_SUCCESS if the frame was successfully parsed
and hence should be indicated up this VELAN. NDIS_STATUS_XXX
otherwise.
--*/
{
VLAN_TAG_HEADER UNALIGNED * pTagHeader;
USHORT UNALIGNED * pTpid;
PVOID pVa;
ULONG BufferLength;
PNDIS_BUFFER pNdisBuffer;
NDIS_PACKET_8021Q_INFO NdisPacket8021qInfo;
PVOID pDst;
BOOLEAN OnlyOneBuffer = FALSE;
NDIS_STATUS Status;
Status = NDIS_STATUS_SUCCESS;
do
{
//
// If the vlan ID the virtual miniport is 0, the miniport should act like it doesn't support
// VELAN tag processing
//
if (pVElan->VlanId == 0)
{
break;
}
pNdisBuffer = Packet->Private.Head;
#ifdef NDIS51_MINIPORT
NdisQueryBufferSafe(pNdisBuffer, &pVa, (PUINT)&BufferLength, NormalPagePriority );
if (pVa == NULL)
{
Status = NDIS_STATUS_RESOURCES;
MUX_INCR_STATISTICS(&pVElan->RcvResourceErrors);
break;
}
#else
NdisQueryBuffer(pNdisBuffer, &pVa, &BufferLength);
#endif
//
// The first NDIS buffer (lookahead) must be longer than
// ETH_HEADER_SIZE + VLAN_TAG_HEADER_SIZE
//
ASSERT(BufferLength >= ETH_HEADER_SIZE + VLAN_TAG_HEADER_SIZE);
//
// Get at the EtherType field.
//
pTpid = (USHORT UNALIGNED *)((PUCHAR)pVa + 2 * ETH_LENGTH_OF_ADDRESS);
//
// Check if a tag header is present.
//
if (*pTpid != TPID)
{
//
// No tag header exists - nothing more to do here.
//
NDIS_PER_PACKET_INFO_FROM_PACKET(MyPacket, Ieee8021QInfo) = 0;
break;
}
//
// We do have a tag header. Parse it further.
//
//
// If E-RIF is present, discard the packet - we don't
// support this variation.
//
pTagHeader = (VLAN_TAG_HEADER UNALIGNED *)(pTpid + 1);
if (GET_CANONICAL_FORMAT_ID_FROM_TAG(pTagHeader) != 0)
{
//
// Drop the packet
//
Status = NDIS_STATUS_NOT_ACCEPTED;
MUX_INCR_STATISTICS(&pVElan->RcvFormatErrors);
break;
}
//
// If there is a VLAN ID in this frame, and we have
// a configured VLAN ID for this VELAN, check if they
// are the same - drop if not.
//
if ((GET_VLAN_ID_FROM_TAG(pTagHeader) != 0) &&
(GET_VLAN_ID_FROM_TAG(pTagHeader) != pVElan->VlanId))
{
Status = NDIS_STATUS_NOT_ACCEPTED;
MUX_INCR_STATISTICS(&pVElan->RcvVlanIdErrors);
break;
}
//
// Parsed this frame successfully. Copy in relevant
// parts of the tag header to per-packet information.
//
NdisPacket8021qInfo.Value = NULL; // initialize
COPY_TAG_INFO_FROM_HEADER_TO_PACKET_INFO(NdisPacket8021qInfo, pTagHeader);
NDIS_PER_PACKET_INFO_FROM_PACKET(MyPacket, Ieee8021QInfo) =
NdisPacket8021qInfo.Value;
//
// Strip off the tag header "in place":
//
pDst = (PVOID)((PUCHAR)pVa + VLAN_TAG_HEADER_SIZE);
RtlMoveMemory(pDst, pVa, 2 * ETH_LENGTH_OF_ADDRESS);
//
// Allocate a new buffer to describe the new first
// buffer in the packet. This could very well be the
// only buffer in the packet.
//
NdisAllocateBuffer(&Status,
&pNdisBuffer,
pVElan->BufferPoolHandle,
pDst,
BufferLength - VLAN_TAG_HEADER_SIZE);
if (Status != NDIS_STATUS_SUCCESS)
{
//
// Drop the packet
//
Status = NDIS_STATUS_RESOURCES;
MUX_INCR_STATISTICS(&pVElan->RcvResourceErrors);
break;
}
//
// Prepare the new packet to be indicated up: this consists
// of the buffer chain starting with the second buffer,
// appended to the first buffer set up in the previous step.
//
MyPacket->Private.Head = NDIS_BUFFER_LINKAGE(Packet->Private.Head);
//
// Only one buffer in the packet
//
if (MyPacket->Private.Head == NULL)
{
OnlyOneBuffer = TRUE;
}
NdisChainBufferAtFront(MyPacket, pNdisBuffer);
if (OnlyOneBuffer)
{
MyPacket->Private.Tail = MyPacket->Private.Head;
}
else
{
MyPacket->Private.Tail = Packet->Private.Tail;
}
}
while (FALSE);
return Status;
}
#endif // IEEE_VLAN_SUPPORT