windows-nt/Source/XPSP1/NT/net/ndis/samples/muxim/miniport.c

2055 lines
58 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1992-2000 Microsoft Corporation
Module Name:
miniport.c
Abstract:
NDIS Miniport Entry points and utility functions for the NDIS
MUX Intermediate Miniport sample. The driver exposes zero or more
Virtual Ethernet LANs (VELANs) as NDIS miniport instances over
each lower (protocol-edge) binding to an underlying adapter.
Environment:
Kernel mode.
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#define MODULE_NUMBER MODULE_MINI
NDIS_OID VElanSupportedOids[] =
{
OID_GEN_SUPPORTED_LIST,
OID_GEN_HARDWARE_STATUS,
OID_GEN_MEDIA_SUPPORTED,
OID_GEN_MEDIA_IN_USE,
OID_GEN_MAXIMUM_LOOKAHEAD,
OID_GEN_MAXIMUM_FRAME_SIZE,
OID_GEN_LINK_SPEED,
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_PROTOCOL_OPTIONS,
OID_GEN_MAC_OPTIONS,
OID_GEN_MEDIA_CONNECT_STATUS,
OID_GEN_MAXIMUM_SEND_PACKETS,
OID_GEN_XMIT_OK,
OID_GEN_RCV_OK,
OID_GEN_XMIT_ERROR,
OID_GEN_RCV_ERROR,
OID_GEN_RCV_NO_BUFFER,
OID_GEN_RCV_CRC_ERROR,
OID_GEN_TRANSMIT_QUEUE_LENGTH,
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,
OID_802_3_XMIT_DEFERRED,
OID_802_3_XMIT_MAX_COLLISIONS,
OID_802_3_RCV_OVERRUN,
OID_802_3_XMIT_UNDERRUN,
OID_802_3_XMIT_HEARTBEAT_FAILURE,
OID_802_3_XMIT_TIMES_CRS_LOST,
OID_802_3_XMIT_LATE_COLLISIONS,
OID_PNP_CAPABILITIES,
OID_PNP_SET_POWER,
OID_PNP_QUERY_POWER,
OID_PNP_ADD_WAKE_UP_PATTERN,
OID_PNP_REMOVE_WAKE_UP_PATTERN,
#if IEEE_VLAN_SUPPORT
OID_GEN_VLAN_ID,
#endif
OID_PNP_ENABLE_WAKE_UP
};
NDIS_STATUS
MPInitialize(
OUT PNDIS_STATUS OpenErrorStatus,
OUT PUINT SelectedMediumIndex,
IN PNDIS_MEDIUM MediumArray,
IN UINT MediumArraySize,
IN NDIS_HANDLE MiniportAdapterHandle,
IN NDIS_HANDLE WrapperConfigurationContext
)
/*++
Routine Description:
This is the Miniport Initialize routine which gets called as a
result of our call to NdisIMInitializeDeviceInstanceEx.
The context parameter which we pass there is the VELan structure
which we retrieve here.
Arguments:
OpenErrorStatus Not used by us.
SelectedMediumIndex Place-holder for what media we are using
MediumArray Array of ndis media passed down to us to pick from
MediumArraySize Size of the array
MiniportAdapterHandle The handle NDIS uses to refer to us
WrapperConfigurationContext For use by NdisOpenConfiguration
Return Value:
NDIS_STATUS_SUCCESS unless something goes wrong
--*/
{
UINT i;
PVELAN pVElan;
NDIS_STATUS Status = NDIS_STATUS_FAILURE;
NDIS_MEDIUM Medium;
NDIS_HANDLE ConfigurationHandle;
PVOID NetworkAddress;
#if IEEE_VLAN_SUPPORT
PNDIS_CONFIGURATION_PARAMETER Params;
NDIS_STRING strVlanId = NDIS_STRING_CONST("VlanID");
#endif
//
// Start off by retrieving our virtual miniport context (VELAN) and
// storing the Miniport handle in it.
//
pVElan = NdisIMGetDeviceContext(MiniportAdapterHandle);
DBGPRINT(MUX_LOUD, ("==> Miniport Initialize: VELAN %p\n", pVElan));
ASSERT(pVElan != NULL);
ASSERT(pVElan->pAdapt != NULL);
do
{
pVElan->MiniportAdapterHandle = MiniportAdapterHandle;
for (i = 0; i < MediumArraySize; i++)
{
if (MediumArray[i] == VELAN_MEDIA_TYPE)
{
*SelectedMediumIndex = i;
break;
}
}
if (i == MediumArraySize)
{
Status = NDIS_STATUS_UNSUPPORTED_MEDIA;
break;
}
//
// Access configuration parameters for this miniport.
//
NdisOpenConfiguration(
&Status,
&ConfigurationHandle,
WrapperConfigurationContext);
if (Status != NDIS_STATUS_SUCCESS)
{
break;
}
NdisReadNetworkAddress(
&Status,
&NetworkAddress,
&i,
ConfigurationHandle);
//
// If there is a NetworkAddress override, use it
//
if ((Status == NDIS_STATUS_SUCCESS) && (i == ETH_LENGTH_OF_ADDRESS))
{
ETH_COPY_NETWORK_ADDRESS(
pVElan->CurrentAddress,
NetworkAddress);
}
else
{
MPGenerateMacAddr(pVElan);
}
#if IEEE_VLAN_SUPPORT
//
// Read VLAN ID
//
NdisReadConfiguration(
&Status,
&Params,
ConfigurationHandle,
&strVlanId,
NdisParameterInteger);
if (Status == NDIS_STATUS_SUCCESS)
{
//
// Check for out of bound
//
if (Params->ParameterData.IntegerData > VLAN_ID_MAX)
{
pVElan->VlanId = VLANID_DEFAULT;
}
else
{
pVElan->VlanId = Params->ParameterData.IntegerData;
}
}
else
{
//
// Should fail the initialization or use default value
//
pVElan->VlanId = VLANID_DEFAULT;
Status = NDIS_STATUS_SUCCESS;
}
#endif
NdisCloseConfiguration(ConfigurationHandle);
//
// Set the attributes now. NDIS_ATTRIBUTE_DESERIALIZE enables us
// to make up-calls to NDIS from arbitrary execution contexts.
// This also forces us to protect our data structures using
// spinlocks where appropriate. Also in this case NDIS does not queue
// packets on our behalf.
//
NdisMSetAttributesEx(MiniportAdapterHandle,
pVElan,
0,
NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT |
NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT|
NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER |
NDIS_ATTRIBUTE_DESERIALIZE |
NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND,
0);
//
// Create an ioctl interface
//
(VOID)PtRegisterDevice();
Status = NDIS_STATUS_SUCCESS;
} while (FALSE);
//
// If we had received an UnbindAdapter notification on the underlying
// adapter, we would have blocked that thread waiting for the IM Init
// process to complete. Wake up any such thread.
//
// See PtUnbindAdapter for more details.
//
ASSERT(pVElan->MiniportInitPending == TRUE);
pVElan->MiniportInitPending = FALSE;
NdisSetEvent(&pVElan->MiniportInitEvent);
DBGPRINT(MUX_INFO, ("<== Miniport Initialize: VELAN %p, Status %x\n", pVElan, Status));
return Status;
}
VOID
MPSendPackets(
IN NDIS_HANDLE MiniportAdapterContext,
IN PPNDIS_PACKET PacketArray,
IN UINT NumberOfPackets
)
/*++
Routine Description:
Send Packet Array handler. Called by NDIS whenever a protocol
bound to our VELAN miniport sends one or more packets.
We forward each packet to the lower binding.
NOTE: NDIS will not Halt this VELAN miniport until all
these packets are "send-completed", and we don't unbind
the lower binding until all VELANs are halted. Therefore
we don't need locks or references on VELAN or ADAPT structures.
Arguments:
MiniportAdapterContext Pointer to our VELAN
PacketArray Set of packets to send
NumberOfPackets Length of above array
Return Value:
None - we call NdisMSendComplete whenever we are done with a packet.
--*/
{
PVELAN pVElan = (PVELAN)MiniportAdapterContext;
PADAPT pAdapt = pVElan->pAdapt;
PNDIS_PACKET Packet, MyPacket;
NDIS_STATUS Status;
PVOID MediaSpecificInfo;
ULONG MediaSpecificInfoSize;
UINT i;
for (i = 0; i < NumberOfPackets; i++)
{
Packet = PacketArray[i];
//
// Allocate a new packet to encapsulate data from the original.
//
NdisAllocatePacket(&Status,
&MyPacket,
pVElan->SendPacketPoolHandle);
if (Status == NDIS_STATUS_SUCCESS)
{
PMUX_SEND_RSVD pSendReserved;
pSendReserved = MUX_RSVD_FROM_SEND_PACKET(MyPacket);
pSendReserved->pOriginalPacket = Packet;
pSendReserved->pVElan = pVElan;
MyPacket->Private.Flags = NdisGetPacketFlags(Packet) |
MUX_SEND_PACKET_FLAGS;
MyPacket->Private.Head = Packet->Private.Head;
MyPacket->Private.Tail = Packet->Private.Tail;
#ifdef WIN9X
//
// Work around the fact that NDIS does not initialize this
// to FALSE on Win9x.
//
MyPacket->Private.ValidCounts = FALSE;
#endif // WIN9X
//
// Copy OOB data to the new packet.
//
NdisMoveMemory(NDIS_OOB_DATA_FROM_PACKET(MyPacket),
NDIS_OOB_DATA_FROM_PACKET(Packet),
sizeof(NDIS_PACKET_OOB_DATA));
//
// Copy relevant parts of per packet info into the new packet.
//
#ifndef WIN9X
NdisIMCopySendPerPacketInfo(MyPacket, Packet);
#endif
//
// Copy Media specific information.
//
NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(Packet,
&MediaSpecificInfo,
&MediaSpecificInfoSize);
if (MediaSpecificInfo || MediaSpecificInfoSize)
{
NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(MyPacket,
MediaSpecificInfo,
MediaSpecificInfoSize);
}
#if IEEE_VLAN_SUPPORT
Status = MPHandleSendTagging(pVElan, Packet, MyPacket);
if (Status != NDIS_STATUS_SUCCESS)
{
NdisFreePacket(MyPacket);
NdisMSendComplete(pVElan->MiniportAdapterHandle,
Packet,
Status);
continue;
}
#endif
//
// Make note of the upcoming send.
//
MUX_INCR_PENDING_SENDS(pVElan);
//
// Send it to the lower binding.
//
NdisSend(&Status,
pAdapt->BindingHandle,
MyPacket);
if (Status != NDIS_STATUS_PENDING)
{
PtSendComplete((NDIS_HANDLE)pAdapt,
MyPacket,
Status);
}
}
else
{
//
// Failed to allocate a packet.
//
break;
}
}
//
// If we bailed out above, fail any unprocessed sends.
//
while (i < NumberOfPackets)
{
NdisMSendComplete(pVElan->MiniportAdapterHandle,
PacketArray[i],
NDIS_STATUS_RESOURCES);
i++;
}
}
NDIS_STATUS
MPQueryInformation(
IN NDIS_HANDLE MiniportAdapterContext,
IN NDIS_OID Oid,
IN PVOID InformationBuffer,
IN ULONG InformationBufferLength,
OUT PULONG BytesWritten,
OUT PULONG BytesNeeded
)
/*++
Routine Description:
Entry point called by NDIS to query for the value of the specified OID.
All OID values are responded to right here, since this is a virtual
device (not pass-through).
Arguments:
MiniportAdapterContext Pointer to the adapter structure
Oid Oid for this query
InformationBuffer Buffer for information
InformationBufferLength Size of this buffer
BytesWritten Specifies how much info is written
BytesNeeded In case the buffer is smaller than
what we need, tell them how much is needed
Return Value:
Return code from the NdisRequest below.
--*/
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PVELAN pVElan;
NDIS_HARDWARE_STATUS HardwareStatus = NdisHardwareStatusReady;
NDIS_MEDIUM Medium = VELAN_MEDIA_TYPE;
UCHAR VendorDesc[] = VELAN_VENDOR_DESC;
ULONG ulInfo;
ULONG64 ulInfo64;
USHORT usInfo;
UCHAR arrInfo[ETH_LENGTH_OF_ADDRESS];
PVOID pInfo = (PVOID) &ulInfo;
ULONG ulInfoLen = sizeof(ulInfo);
// Should we forward the request to the miniport below?
BOOLEAN bForwardRequest = FALSE;
pVElan = (PVELAN) MiniportAdapterContext;
// Initialize the result
*BytesWritten = 0;
*BytesNeeded = 0;
switch (Oid)
{
case OID_GEN_SUPPORTED_LIST:
pInfo = (PVOID) VElanSupportedOids;
ulInfoLen = sizeof(VElanSupportedOids);
break;
case OID_GEN_SUPPORTED_GUIDS:
//
// Do NOT forward this down, otherwise we will
// end up with spurious instances of private WMI
// classes supported by the lower driver(s).
//
Status = NDIS_STATUS_NOT_SUPPORTED;
break;
case OID_GEN_HARDWARE_STATUS:
pInfo = (PVOID) &HardwareStatus;
ulInfoLen = sizeof(NDIS_HARDWARE_STATUS);
break;
case OID_GEN_MEDIA_SUPPORTED:
case OID_GEN_MEDIA_IN_USE:
pInfo = (PVOID) &Medium;
ulInfoLen = sizeof(NDIS_MEDIUM);
break;
case OID_GEN_CURRENT_LOOKAHEAD:
case OID_GEN_MAXIMUM_LOOKAHEAD:
ulInfo = pVElan->LookAhead - ETH_HEADER_SIZE;
break;
case OID_GEN_MAXIMUM_FRAME_SIZE:
ulInfo = ETH_MAX_PACKET_SIZE - ETH_HEADER_SIZE;
#if IEEE_VLAN_SUPPORT
ulInfo -= VLAN_TAG_HEADER_SIZE;
#endif
break;
case OID_GEN_MAXIMUM_TOTAL_SIZE:
case OID_GEN_TRANSMIT_BLOCK_SIZE:
case OID_GEN_RECEIVE_BLOCK_SIZE:
ulInfo = (ULONG) ETH_MAX_PACKET_SIZE;
#if IEEE_VLAN_SUPPORT
ulInfo -= VLAN_TAG_HEADER_SIZE;
#endif
break;
case OID_GEN_MAC_OPTIONS:
ulInfo = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA |
NDIS_MAC_OPTION_TRANSFERS_NOT_PEND |
NDIS_MAC_OPTION_NO_LOOPBACK;
#if IEEE_VLAN_SUPPORT
ulInfo |= (NDIS_MAC_OPTION_8021P_PRIORITY |
NDIS_MAC_OPTION_8021Q_VLAN);
#endif
break;
case OID_GEN_LINK_SPEED:
bForwardRequest = TRUE;
break;
case OID_GEN_TRANSMIT_BUFFER_SPACE:
ulInfo = ETH_MAX_PACKET_SIZE * pVElan->MaxBusySends;
#if IEEE_VLAN_SUPPORT
ulInfo -= VLAN_TAG_HEADER_SIZE * pVElan->MaxBusySends;
#endif
break;
case OID_GEN_RECEIVE_BUFFER_SPACE:
ulInfo = ETH_MAX_PACKET_SIZE * pVElan->MaxBusyRecvs;
#if IEEE_VLAN_SUPPORT
ulInfo -= VLAN_TAG_HEADER_SIZE * pVElan->MaxBusyRecvs;
#endif
break;
case OID_GEN_VENDOR_ID:
ulInfo = VELAN_VENDOR_ID;
break;
case OID_GEN_VENDOR_DESCRIPTION:
pInfo = VendorDesc;
ulInfoLen = sizeof(VendorDesc);
break;
case OID_GEN_VENDOR_DRIVER_VERSION:
ulInfo = VELAN_VENDOR_ID;
break;
case OID_GEN_DRIVER_VERSION:
usInfo = (USHORT) VELAN_DRIVER_VERSION;
pInfo = (PVOID) &usInfo;
ulInfoLen = sizeof(USHORT);
break;
case OID_802_3_PERMANENT_ADDRESS:
pInfo = pVElan->PermanentAddress;
ulInfoLen = ETH_LENGTH_OF_ADDRESS;
break;
case OID_802_3_CURRENT_ADDRESS:
pInfo = pVElan->CurrentAddress;
ulInfoLen = ETH_LENGTH_OF_ADDRESS;
break;
case OID_802_3_MAXIMUM_LIST_SIZE:
ulInfo = VELAN_MAX_MCAST_LIST;
break;
case OID_GEN_MAXIMUM_SEND_PACKETS:
ulInfo = VELAN_MAX_SEND_PKTS;
break;
case OID_GEN_MEDIA_CONNECT_STATUS:
//
// Get this from the adapter below.
//
bForwardRequest = TRUE;
break;
case OID_PNP_QUERY_POWER:
// simply succeed this.
ulInfoLen = 0;
break;
case OID_PNP_CAPABILITIES:
case OID_PNP_WAKE_UP_PATTERN_LIST:
//
// Pass down these power management/PNP OIDs.
//
bForwardRequest = TRUE;
break;
case OID_GEN_XMIT_OK:
ulInfo64 = pVElan->GoodTransmits;
pInfo = &ulInfo64;
if (InformationBufferLength >= sizeof(ULONG64) ||
InformationBufferLength == 0)
{
ulInfoLen = sizeof(ULONG64);
}
else
{
ulInfoLen = sizeof(ULONG);
}
break;
case OID_GEN_RCV_OK:
ulInfo64 = pVElan->GoodReceives;
pInfo = &ulInfo64;
if (InformationBufferLength >= sizeof(ULONG64) ||
InformationBufferLength == 0)
{
ulInfoLen = sizeof(ULONG64);
}
else
{
ulInfoLen = sizeof(ULONG);
}
break;
case OID_GEN_XMIT_ERROR:
ulInfo = pVElan->TxAbortExcessCollisions +
pVElan->TxDmaUnderrun +
pVElan->TxLostCRS +
pVElan->TxLateCollisions+
pVElan->TransmitFailuresOther;
break;
case OID_GEN_RCV_ERROR:
ulInfo = pVElan->RcvCrcErrors +
pVElan->RcvAlignmentErrors +
pVElan->RcvResourceErrors +
pVElan->RcvDmaOverrunErrors +
pVElan->RcvRuntErrors;
#if IEEE_VLAN_SUPPORT
ulInfo +=
(pVElan->RcvVlanIdErrors +
pVElan->RcvFormatErrors);
#endif
break;
case OID_GEN_RCV_NO_BUFFER:
ulInfo = pVElan->RcvResourceErrors;
break;
case OID_GEN_RCV_CRC_ERROR:
ulInfo = pVElan->RcvCrcErrors;
break;
case OID_GEN_TRANSMIT_QUEUE_LENGTH:
ulInfo = pVElan->RegNumTcb;
break;
case OID_802_3_RCV_ERROR_ALIGNMENT:
ulInfo = pVElan->RcvAlignmentErrors;
break;
case OID_802_3_XMIT_ONE_COLLISION:
ulInfo = pVElan->OneRetry;
break;
case OID_802_3_XMIT_MORE_COLLISIONS:
ulInfo = pVElan->MoreThanOneRetry;
break;
case OID_802_3_XMIT_DEFERRED:
ulInfo = pVElan->TxOKButDeferred;
break;
case OID_802_3_XMIT_MAX_COLLISIONS:
ulInfo = pVElan->TxAbortExcessCollisions;
break;
case OID_802_3_RCV_OVERRUN:
ulInfo = pVElan->RcvDmaOverrunErrors;
break;
case OID_802_3_XMIT_UNDERRUN:
ulInfo = pVElan->TxDmaUnderrun;
break;
case OID_802_3_XMIT_HEARTBEAT_FAILURE:
ulInfo = pVElan->TxLostCRS;
break;
case OID_802_3_XMIT_TIMES_CRS_LOST:
ulInfo = pVElan->TxLostCRS;
break;
case OID_802_3_XMIT_LATE_COLLISIONS:
ulInfo = pVElan->TxLateCollisions;
break;
#if IEEE_VLAN_SUPPORT
case OID_GEN_VLAN_ID:
ulInfo = pVElan->VlanId;
break;
#endif
default:
Status = NDIS_STATUS_INVALID_OID;
break;
}
if (bForwardRequest == FALSE)
{
//
// No need to forward this request down.
//
if (Status == NDIS_STATUS_SUCCESS)
{
if (ulInfoLen <= InformationBufferLength)
{
// Copy result into InformationBuffer
*BytesWritten = ulInfoLen;
if(ulInfoLen)
{
NdisMoveMemory(InformationBuffer, pInfo, ulInfoLen);
}
}
else
{
// too short
*BytesNeeded = ulInfoLen;
Status = NDIS_STATUS_BUFFER_TOO_SHORT;
}
}
}
else
{
//
// Send this request to the binding below.
//
Status = MPForwardRequest(pVElan,
NdisRequestQueryInformation,
Oid,
InformationBuffer,
InformationBufferLength,
BytesWritten,
BytesNeeded);
}
if ((Status != NDIS_STATUS_SUCCESS) &&
(Status != NDIS_STATUS_PENDING))
{
DBGPRINT(MUX_WARN, ("MPQueryInformation VELAN %p, OID 0x%08x, Status = 0x%08x\n",
pVElan, Oid, Status));
}
return(Status);
}
NDIS_STATUS
MPSetInformation(
IN NDIS_HANDLE MiniportAdapterContext,
IN NDIS_OID Oid,
IN PVOID InformationBuffer,
IN ULONG InformationBufferLength,
OUT PULONG BytesRead,
OUT PULONG BytesNeeded
)
/*++
Routine Description:
This is the handler for an OID set operation. Relevant
OIDs are forwarded down to the lower miniport for handling.
Arguments:
MiniportAdapterContext Pointer to the adapter structure
Oid Oid for this query
InformationBuffer Buffer for information
InformationBufferLength Size of this buffer
BytesRead Specifies how much info is read
BytesNeeded In case the buffer is smaller than what
we need, tell them how much is needed
Return Value:
Return code from the NdisRequest below.
--*/
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PVELAN pVElan = (PVELAN) MiniportAdapterContext;
ULONG PacketFilter;
NDIS_DEVICE_POWER_STATE NewDeviceState;
// Should we forward the request to the miniport below?
BOOLEAN bForwardRequest = FALSE;
*BytesRead = 0;
*BytesNeeded = 0;
switch (Oid)
{
//
// Let the miniport below handle these OIDs:
//
case OID_PNP_ADD_WAKE_UP_PATTERN:
case OID_PNP_REMOVE_WAKE_UP_PATTERN:
case OID_PNP_ENABLE_WAKE_UP:
bForwardRequest = TRUE;
break;
case OID_PNP_SET_POWER:
//
// Store new power state and succeed the request.
//
*BytesNeeded = sizeof(NDIS_DEVICE_POWER_STATE);
if (InformationBufferLength < *BytesNeeded)
{
Status = NDIS_STATUS_INVALID_LENGTH;
break;
}
NewDeviceState = (*(PNDIS_DEVICE_POWER_STATE)InformationBuffer);
//
// Check if the VELAN adapter goes from lower power state to D0
//
if ((MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState))
&& (!MUX_IS_LOW_POWER_STATE(NewDeviceState)))
{
//
// Indicate the media status is necessary
//
if (pVElan->LastIndicatedStatus != pVElan->LatestUnIndicateStatus)
{
NdisMIndicateStatus(pVElan->MiniportAdapterHandle,
pVElan->LatestUnIndicateStatus,
(PVOID)NULL,
0);
NdisMIndicateStatusComplete(pVElan->MiniportAdapterHandle);
pVElan->LastIndicatedStatus = pVElan->LatestUnIndicateStatus;
}
}
//
// Check if the VELAN adapter goes from D0 to lower power state
//
if ((!MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState))
&& (MUX_IS_LOW_POWER_STATE(NewDeviceState)))
{
//
// Initialize LastUnIndicateStatus
//
pVElan->LatestUnIndicateStatus = pVElan->LastIndicatedStatus;
}
NdisMoveMemory(&pVElan->MPDevicePowerState,
InformationBuffer,
*BytesNeeded);
DBGPRINT(MUX_INFO, ("SetInfo: VElan %p, new miniport power state --- %d\n",
pVElan, pVElan->MPDevicePowerState));
break;
case OID_802_3_MULTICAST_LIST:
Status = MPSetMulticastList(pVElan,
InformationBuffer,
InformationBufferLength,
BytesRead,
BytesNeeded);
break;
case OID_GEN_CURRENT_PACKET_FILTER:
if (InformationBufferLength != sizeof(ULONG))
{
Status = NDIS_STATUS_INVALID_LENGTH;
*BytesNeeded = sizeof(ULONG);
break;
}
NdisMoveMemory(&PacketFilter, InformationBuffer, sizeof(ULONG));
*BytesRead = sizeof(ULONG);
Status = MPSetPacketFilter(pVElan,
PacketFilter);
break;
case OID_GEN_CURRENT_LOOKAHEAD:
#if IEEE_VLAN_SUPPORT
//
// In order to simplify parsing and to avoid excessive
// copying, we need the tag header also to be present in the
// lookahead buffer. Make sure that the driver below
// includes that.
//
*(PULONG)InformationBuffer += VLAN_TAG_HEADER_SIZE;
#endif
bForwardRequest = TRUE;
break;
#if IEEE_VLAN_SUPPORT
case OID_GEN_VLAN_ID:
if (InformationBufferLength != sizeof(ULONG))
{
Status = NDIS_STATUS_INVALID_LENGTH;
*BytesNeeded = sizeof(ULONG);
break;
}
NdisMoveMemory(&(pVElan->VlanId), InformationBuffer, sizeof(ULONG));
break;
#endif
default:
Status = NDIS_STATUS_INVALID_OID;
break;
}
if (bForwardRequest == FALSE)
{
if (Status == NDIS_STATUS_SUCCESS)
{
*BytesRead = InformationBufferLength;
}
}
else
{
//
// Send this request to the binding below.
//
Status = MPForwardRequest(pVElan,
NdisRequestSetInformation,
Oid,
InformationBuffer,
InformationBufferLength,
BytesRead,
BytesNeeded);
}
return(Status);
}
VOID
MPReturnPacket(
IN NDIS_HANDLE MiniportAdapterContext,
IN PNDIS_PACKET Packet
)
/*++
Routine Description:
NDIS Miniport entry point called whenever protocols are done with
a packet that we had indicated up and they had queued up for returning
later.
Arguments:
MiniportAdapterContext - pointer to VELAN structure
Packet - packet being returned.
Return Value:
None.
--*/
{
PVELAN pVElan = (PVELAN)MiniportAdapterContext;
PNDIS_PACKET pOriginalPacket;
PMUX_RECV_RSVD pRecvRsvd;
#if IEEE_VLAN_SUPPORT
NDIS_PACKET_8021Q_INFO NdisPacket8021qInfo;
#endif
pRecvRsvd = MUX_RSVD_FROM_RECV_PACKET(Packet);
pOriginalPacket = pRecvRsvd->pOriginalPacket;
//
// Reclaim our packet.
//
#if IEEE_VLAN_SUPPORT
//
// We would have set per-packet information if we had
// extracted a tag header from the received packet.
//
NdisPacket8021qInfo.Value = NDIS_PER_PACKET_INFO_FROM_PACKET (
Packet,
Ieee8021QInfo);
//
// If we did remove the tag header from the received packet,
// we would have allocated a buffer to describe the "untagged"
// header (see PtHandleRcvTagging); free it.
//
if (NdisPacket8021qInfo.Value)
{
NdisFreeBuffer(Packet->Private.Head);
}
#endif
NdisFreePacket(Packet);
//
// Return the original packet received at our protocol
// edge, if any.
//
// NOTE that we might end up calling NdisReturnPackets
// multiple times with the same "lower" packet, based on
// the number of VELANs to which we had indicated that
// packet. The number of times we do so should match
// the return value from our PtReceivePacket handler.
//
if (pOriginalPacket != NULL)
{
NdisReturnPackets(&pOriginalPacket, 1);
}
else
{
//
// If no original packet, then we have been called
// here to reclaim a packet used to forward up
// a non-packet receive (see PtReceive). There
// is nothing more to be done.
//
}
MUX_DECR_PENDING_RECEIVES(pVElan);
}
NDIS_STATUS
MPTransferData(
OUT PNDIS_PACKET Packet,
OUT PUINT BytesTransferred,
IN NDIS_HANDLE MiniportAdapterContext,
IN NDIS_HANDLE MiniportReceiveContext,
IN UINT ByteOffset,
IN UINT BytesToTransfer
)
/*++
Routine Description:
Miniport's transfer data handler. This is called if we had
indicated receive data using a non-packet API, for e.g. if
the lookahead buffer did not contain the entire data.
We need to forward this to the miniport below to that it can
copy in the rest of the data. We call NdisTransferData to do so.
However, when that completes (see PtTransferDataComplete), we
have to get back at the VELAN from which this packet came so that
we can complete this request with the right MiniportAdapterHandle.
We therefore allocate a new packet, pointing to the same buffer
as the packet just passed in, and use reserved space in the packet
to hold a backpointer to the VELAN from which this came.
Arguments:
Packet Destination packet
BytesTransferred Place to return how much data was copied
MiniportAdapterContext Pointer to the VELAN structure
MiniportReceiveContext Context
ByteOffset Offset into the packet for copying data
BytesToTransfer How much to copy.
Return Value:
Status of transfer
--*/
{
PVELAN pVElan = (PVELAN)MiniportAdapterContext;
NDIS_STATUS Status;
PNDIS_PACKET MyPacket;
PMUX_TD_RSVD pTDReserved;
#if IEEE_VLAN_SUPPORT
PMUX_RCV_CONTEXT pMuxRcvContext;
#endif
do
{
NdisAllocatePacket(&Status,
&MyPacket,
pVElan->SendPacketPoolHandle);
if (Status != NDIS_STATUS_SUCCESS)
{
break;
}
pTDReserved = MUX_RSVD_FROM_TD_PACKET(MyPacket);
pTDReserved->pOriginalPacket = Packet;
pTDReserved->pVElan = pVElan;
MyPacket->Private.Flags = NdisGetPacketFlags(Packet) |
MUX_SEND_PACKET_FLAGS;
MyPacket->Private.Head = Packet->Private.Head;
MyPacket->Private.Tail = Packet->Private.Tail;
#ifdef WIN9X
//
// Work around the fact that NDIS does not initialize this
// field on Win9x.
//
MyPacket->Private.ValidCounts = FALSE;
#endif // WIN9X
#if IEEE_VLAN_SUPPORT
//
// Check if the original received packet did contain a
// VLAN tag header. If so, make sure we get the upcoming
// call to NdisTransferData to skip the tag header.
//
pMuxRcvContext = (PMUX_RCV_CONTEXT)MiniportReceiveContext;
if (pMuxRcvContext->TagHeaderLen == VLAN_TAG_HEADER_SIZE)
{
//
// There was a tag header in the received packet.
//
ByteOffset += VLAN_TAG_HEADER_SIZE;
//
// Copy the 8021Q info into the packet
//
NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, Ieee8021QInfo) =
pMuxRcvContext->NdisPacket8021QInfo.Value;
}
//
// Get back the lower driver's receive context for this indication.
//
MiniportReceiveContext = pMuxRcvContext->MacRcvContext;
#endif
NdisTransferData(&Status,
pVElan->pAdapt->BindingHandle,
MiniportReceiveContext,
ByteOffset,
BytesToTransfer,
MyPacket,
BytesTransferred);
if (Status != NDIS_STATUS_PENDING)
{
PtTransferDataComplete(pVElan->pAdapt,
MyPacket,
Status,
*BytesTransferred);
Status = NDIS_STATUS_PENDING;
}
}
while (FALSE);
return(Status);
}
VOID
MPHalt(
IN NDIS_HANDLE MiniportAdapterContext
)
/*++
Routine Description:
Halt handler. Add any further clean-up for the VELAN to this
function.
We wait for all pending I/O on the VELAN to complete and then
unlink the VELAN from the adapter.
Arguments:
MiniportAdapterContext Pointer to the pVElan
Return Value:
None.
--*/
{
PVELAN pVElan = (PVELAN)MiniportAdapterContext;
PADAPT pAdapt = pVElan->pAdapt;
DBGPRINT(MUX_LOUD, ("==>MiniportHalt: VELAN %p\n", pVElan));
//
// Mark the VELAN so that we don't send down any new requests or
// sends to the adapter below, or new receives/indications to
// protocols above.
//
pVElan->MiniportHalting = TRUE;
//
// Update the packet filter on the underlying adapter if needed.
//
if (pVElan->PacketFilter != 0)
{
MPSetPacketFilter(pVElan, 0);
}
//
// Wait for any outstanding sends or requests to complete.
//
while (pVElan->OutstandingSends)
{
DBGPRINT(MUX_INFO, ("MiniportHalt: VELAN %p has %d outstanding sends\n",
pVElan, pVElan->OutstandingSends));
NdisMSleep(20000);
}
//
// Wait for all outstanding indications to be completed and
// any pended receive packets to be returned to us.
//
while (pVElan->OutstandingReceives)
{
DBGPRINT(MUX_INFO, ("MiniportHalt: VELAN %p has %d outstanding receives\n",
pVElan, pVElan->OutstandingReceives));
NdisMSleep(20000);
}
//
// Delete the ioctl interface that was created when the miniport
// was created.
//
(VOID)PtDeregisterDevice();
//
// Unlink the VELAN from its parent ADAPT structure. This will
// dereference the VELAN.
//
pVElan->MiniportAdapterHandle = NULL;
PtUnlinkVElanFromAdapter(pVElan);
DBGPRINT(MUX_LOUD, ("<== MiniportHalt: pVElan %p\n", pVElan));
}
NDIS_STATUS
MPForwardRequest(
IN PVELAN pVElan,
IN NDIS_REQUEST_TYPE RequestType,
IN NDIS_OID Oid,
IN PVOID InformationBuffer,
IN ULONG InformationBufferLength,
OUT PULONG BytesReadOrWritten,
OUT PULONG BytesNeeded
)
/*++
Routine Description:
Utility routine that forwards an NDIS request made on a VELAN to the
lower binding. Since at most a single request can be pended on a VELAN,
we use the pre-allocated request structure embedded in the VELAN struct.
Arguments:
Return Value:
NDIS_STATUS_PENDING if a request was sent down.
--*/
{
NDIS_STATUS Status;
PMUX_NDIS_REQUEST pMuxNdisRequest = &pVElan->Request;
DBGPRINT(MUX_LOUD, ("MPForwardRequest: VELAN %p, OID %x\n", pVElan, Oid));
do
{
MUX_INCR_PENDING_SENDS(pVElan);
//
// If the virtual miniport edge is at a low power
// state, fail this request.
//
if (MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState))
{
MUX_DECR_PENDING_SENDS(pVElan);
Status = NDIS_STATUS_ADAPTER_NOT_READY;
break;
}
pVElan->BytesNeeded = BytesNeeded;
pVElan->BytesReadOrWritten = BytesReadOrWritten;
pMuxNdisRequest->pCallback = PtCompleteForwardedRequest;
switch (RequestType)
{
case NdisRequestQueryInformation:
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;
break;
case NdisRequestSetInformation:
pMuxNdisRequest->Request.RequestType = NdisRequestSetInformation;
pMuxNdisRequest->Request.DATA.SET_INFORMATION.Oid = Oid;
pMuxNdisRequest->Request.DATA.SET_INFORMATION.InformationBuffer =
InformationBuffer;
pMuxNdisRequest->Request.DATA.SET_INFORMATION.InformationBufferLength =
InformationBufferLength;
break;
default:
ASSERT(FALSE);
break;
}
//
// If the lower binding has been notified of a low
// power state, queue this request; it will be picked
// up again when the lower binding returns to D0.
//
if (MUX_IS_LOW_POWER_STATE(pVElan->pAdapt->PtDevicePowerState))
{
DBGPRINT(MUX_INFO, ("ForwardRequest: VELAN %p, Adapt %p power"
" state is %d, queueing OID %x\n",
pVElan, pVElan->pAdapt,
pVElan->pAdapt->PtDevicePowerState, Oid));
pVElan->QueuedRequest = TRUE;
Status = NDIS_STATUS_PENDING;
break;
}
NdisRequest(&Status,
pVElan->BindingHandle,
&pMuxNdisRequest->Request);
if (Status != NDIS_STATUS_PENDING)
{
PtRequestComplete(pVElan->pAdapt, &pMuxNdisRequest->Request, Status);
Status = NDIS_STATUS_PENDING;
break;
}
}
while (FALSE);
return (Status);
}
NDIS_STATUS
MPSetPacketFilter(
IN PVELAN pVElan,
IN ULONG PacketFilter
)
/*++
Routine Description:
This routine will set up the VELAN so that it accepts packets
that match the specified packet filter. The only filter bits
that can truly be toggled are for broadcast and promiscuous.
The MUX driver always sets the lower binding to promiscuous
mode, but we do some optimization here to avoid turning on
receives too soon. That is, we set the packet filter on the lower
binding to a non-zero value iff at least one of the VELANs
has a non-zero filter value.
NOTE: setting the lower binding to promiscuous mode can
impact CPU utilization. The only reason we set the lower binding
to promiscuous mode in this sample is that we need to be able
to receive unicast frames directed to MAC address(es) that do not
match the local adapter's MAC address. If VELAN MAC addresses
are set to be equal to that of the adapter below, it is sufficient
to set the lower packet filter to the bitwise OR'ed value of
packet filter settings on all VELANs.
Arguments:
pVElan - pointer to VELAN
PacketFilter - the new packet filter
Return Value:
NDIS_STATUS_SUCCESS
NDIS_STATUS_NOT_SUPPORTED
--*/
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PADAPT pAdapt;
PVELAN pTmpVElan;
PLIST_ENTRY p;
ULONG AdapterFilter;
BOOLEAN bSendUpdate = FALSE;
LOCK_STATE LockState;
DBGPRINT(MUX_LOUD, ("=> SetPacketFilter VELAN %p, Filter %x\n", pVElan, PacketFilter));
do
{
//
// Any bits not supported?
//
if (PacketFilter & ~VELAN_SUPPORTED_FILTERS)
{
Status = NDIS_STATUS_NOT_SUPPORTED;
break;
}
AdapterFilter = 0;
pAdapt = pVElan->pAdapt;
//
// Grab a Write lock on the adapter so that this operation
// does not interfere with any receives that might be accessing
// filter information.
//
MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState);
//
// Save the new packet filter value
//
pVElan->PacketFilter = PacketFilter;
//
// Compute the new combined filter for all VELANs on this
// adapter.
//
for (p = pAdapt->VElanList.Flink;
p != &pAdapt->VElanList;
p = p->Flink)
{
pTmpVElan = CONTAINING_RECORD(p, VELAN, Link);
AdapterFilter |= pTmpVElan->PacketFilter;
}
//
// If all VELANs have packet filters set to 0, turn off
// receives on the lower adapter, if not already done.
//
if ((AdapterFilter == 0) && (pAdapt->PacketFilter != 0))
{
bSendUpdate = TRUE;
pAdapt->PacketFilter = 0;
}
else
//
// If receives had been turned off on the lower adapter, and
// the new filter is non-zero, turn on the lower adapter.
// We set the adapter to promiscuous mode in this sample
// so that we are able to receive packets directed to
// any of the VELAN MAC addresses.
//
if ((AdapterFilter != 0) && (pAdapt->PacketFilter == 0))
{
bSendUpdate = TRUE;
pAdapt->PacketFilter = MUX_ADAPTER_PACKET_FILTER;
}
MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState);
if (bSendUpdate)
{
PtRequestAdapterAsync(
pAdapt,
NdisRequestSetInformation,
OID_GEN_CURRENT_PACKET_FILTER,
&pAdapt->PacketFilter,
sizeof(pAdapt->PacketFilter),
PtDiscardCompletedRequest);
}
break;
}
while (FALSE);
DBGPRINT(MUX_INFO, ("<= SetPacketFilter VELAN %p, Status %x\n", pVElan, Status));
return(Status);
}
NDIS_STATUS
MPSetMulticastList(
IN PVELAN pVElan,
IN PVOID InformationBuffer,
IN ULONG InformationBufferLength,
OUT PULONG pBytesRead,
OUT PULONG pBytesNeeded
)
/*++
Routine Description:
Set the multicast list on the specified VELAN miniport.
We simply validate all information and copy in the multicast
list.
We don't forward the multicast list information down since
we set the lower binding to promisc. mode.
Arguments:
pVElan - VELAN on which to set the multicast list
InformationBuffer - pointer to new multicast list
InformationBufferLength - length in bytes of above list
pBytesRead - place to return # of bytes read from the above
pBytesNeeded - place to return expected min # of bytes
Return Value:
NDIS_STATUS
--*/
{
NDIS_STATUS Status;
PADAPT pAdapt;
LOCK_STATE LockState;
//
// Initialize.
//
*pBytesNeeded = sizeof(MUX_MAC_ADDRESS);
*pBytesRead = 0;
Status = NDIS_STATUS_SUCCESS;
do
{
if (InformationBufferLength % sizeof(MUX_MAC_ADDRESS))
{
Status = NDIS_STATUS_INVALID_LENGTH;
break;
}
if (InformationBufferLength > (VELAN_MAX_MCAST_LIST * sizeof(MUX_MAC_ADDRESS)))
{
Status = NDIS_STATUS_MULTICAST_FULL;
*pBytesNeeded = VELAN_MAX_MCAST_LIST * sizeof(MUX_MAC_ADDRESS);
break;
}
pAdapt = pVElan->pAdapt;
//
// Grab a Write lock on the adapter so that this operation
// does not interfere with any receives that might be accessing
// multicast list information.
//
MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState);
NdisZeroMemory(&pVElan->McastAddrs[0],
VELAN_MAX_MCAST_LIST * sizeof(MUX_MAC_ADDRESS));
NdisMoveMemory(&pVElan->McastAddrs[0],
InformationBuffer,
InformationBufferLength);
pVElan->McastAddrCount = InformationBufferLength / sizeof(MUX_MAC_ADDRESS);
MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState);
}
while (FALSE);
return (Status);
}
//
// Careful! Uses static storage for string. Used to simplify DbgPrints
// of MAC addresses.
//
PUCHAR
MacAddrToString(PVOID In)
{
static UCHAR String[20];
static PUCHAR HexChars = "0123456789abcdef";
PUCHAR EthAddr = (PUCHAR) In;
UINT i;
PUCHAR s;
for (i = 0, s = String; i < 6; i++, EthAddr++)
{
*s++ = HexChars[(*EthAddr) >> 4];
*s++ = HexChars[(*EthAddr) & 0xf];
}
*s = '\0';
return String;
}
VOID
MPGenerateMacAddr(
PVELAN pVElan
)
/*++
Routine Description:
Generates a "virtual" MAC address for a VELAN.
NOTE: this is only a sample implementation of selecting
a MAC address for the VELAN. Other implementations are possible,
including using the MAC address of the underlying adapter as
the MAC address of the VELAN.
Arguments:
pVElan - Pointer to velan structure
Return Value:
None
--*/
{
pVElan->PermanentAddress[0] =
0x02 | (((UCHAR)pVElan->VElanNumber & 0x3f) << 2);
pVElan->PermanentAddress[1] =
0x02 | (((UCHAR)pVElan->VElanNumber & 0x3f) << 3);
ETH_COPY_NETWORK_ADDRESS(
pVElan->CurrentAddress,
pVElan->PermanentAddress);
DBGPRINT(MUX_LOUD, ("%d CurrentAddress %s\n",
pVElan->VElanNumber, MacAddrToString(&pVElan->CurrentAddress)));
DBGPRINT(MUX_LOUD, ("%d PermanentAddress %s\n",
pVElan->VElanNumber, MacAddrToString(&pVElan->PermanentAddress)));
}
#ifdef NDIS51_MINIPORT
VOID
MPCancelSendPackets(
IN NDIS_HANDLE MiniportAdapterContext,
IN PVOID CancelId
)
/*++
Routine Description:
The miniport entry point to handle cancellation of all send packets
that match the given CancelId. If we have queued any packets that match
this, then we should dequeue them and call NdisMSendComplete for all
such packets, with a status of NDIS_STATUS_REQUEST_ABORTED.
We should also call NdisCancelSendPackets in turn, on each lower binding
that this adapter corresponds to. This is to let miniports below cancel
any matching packets.
Arguments:
MiniportAdapterContext - pointer to VELAN structure
CancelId - ID of packets to be cancelled.
Return Value:
None
--*/
{
PVELAN pVElan = (PVELAN)MiniportAdapterContext;
//
// If we queue packets on our VELAN/adapter structure, this would be
// the place to acquire a spinlock to it, unlink any packets whose
// Id matches CancelId, release the spinlock and call NdisMSendComplete
// with NDIS_STATUS_REQUEST_ABORTED for all unlinked packets.
//
//
// Next, pass this down so that we let the miniport(s) below cancel
// any packets that they might have queued.
//
NdisCancelSendPackets(pVElan->pAdapt->BindingHandle, CancelId);
return;
}
VOID
MPDevicePnPEvent(
IN NDIS_HANDLE MiniportAdapterContext,
IN NDIS_DEVICE_PNP_EVENT DevicePnPEvent,
IN PVOID InformationBuffer,
IN ULONG InformationBufferLength
)
/*++
Routine Description:
This handler is called to notify us of PnP events directed to
our miniport device object.
Arguments:
MiniportAdapterContext - pointer to VELAN structure
DevicePnPEvent - the event
InformationBuffer - Points to additional event-specific information
InformationBufferLength - length of above
Return Value:
None
--*/
{
// TBD - add code/comments about processing this.
return;
}
VOID
MPAdapterShutdown(
IN NDIS_HANDLE MiniportAdapterContext
)
/*++
Routine Description:
This handler is called to notify us of an impending system shutdown.
Since this is not a hardware driver, there isn't anything specific
we need to do about this.
Arguments:
MiniportAdapterContext - pointer to VELAN structure
Return Value:
None
--*/
{
return;
}
#endif // NDIS51_MINIPORT
VOID
MPUnload(
IN PDRIVER_OBJECT DriverObject
)
{
NDIS_STATUS Status;
DBGPRINT(MUX_LOUD, ("==> MPUnload: DriverObj %p\n", DriverObject));
NdisDeregisterProtocol(&Status, ProtHandle);
DBGPRINT(MUX_LOUD, ("<== MPUnload \n"));
}
#if IEEE_VLAN_SUPPORT
NDIS_STATUS
MPHandleSendTagging(
IN PVELAN pVElan,
IN PNDIS_PACKET Packet,
IN OUT PNDIS_PACKET MyPacket
)
/*++
Routine Description:
This function is called when the driver supports IEEE802Q tagging.
It checks the packet to be sent on a VELAN and inserts a tag header
if necessary.
Arguments:
PVELAN - pointer to VELAN structure
Packet - pointer to original packet
MyPacket - pointer to the new allocated packet
Return Value:
NDIS_STATUS_SUCCESS if the packet was successfully parsed
and hence should be passed down to the lower driver. NDIS_STATUS_XXX
otherwise.
--*/
{
NDIS_PACKET_8021Q_INFO NdisPacket8021qInfo;
PVOID pEthTagBuffer;
PNDIS_BUFFER pNdisBuffer;
PVOID pVa;
ULONG BufferLength;
PNDIS_BUFFER pFirstBuffer;
PNDIS_BUFFER pSecondBuffer;
NDIS_STATUS Status;
NDIS_STATUS Status2;
PVOID pStartVa;
BOOLEAN IsFirstVa;
PVLAN_TAG_HEADER pTagHeader;
PUSHORT TypeLength;
PUSHORT pTpid;
ULONG BytesToSkip;
PUSHORT pTypeLength;
//
// Add tag header here
//
Status = NDIS_STATUS_SUCCESS;
NdisPacket8021qInfo.Value = NDIS_PER_PACKET_INFO_FROM_PACKET(
MyPacket,
Ieee8021QInfo);
do
{
//
// Insert a tag only if we have a configured VLAN ID
// or there is non-zero VLAN/priority information to be
// sent with the packet.
//
if ((pVElan->VlanId == 0) && (NdisPacket8021qInfo.Value == 0))
{
//
// No tag header needed.
//
break;
}
//
// We don't support E-RIF
//
if (NdisPacket8021qInfo.TagHeader.CanonicalFormatId)
{
//
// skip the packet, return NDIS_STATUS_FAILURE
//
Status = NDIS_STATUS_INVALID_PACKET;
break;
}
//
// The Vlan Id must be the same as the configured VLAN ID if it is non-zero
//
if ((NdisPacket8021qInfo.TagHeader.VlanId)
&& (pVElan->VlanId)
&& (NdisPacket8021qInfo.TagHeader.VlanId != pVElan->VlanId))
{
Status = NDIS_STATUS_INVALID_PACKET;
break;
}
//
// Find the virtual address after the Ethernet Header
//
BytesToSkip = ETH_HEADER_SIZE;
pNdisBuffer = Packet->Private.Head;
IsFirstVa = TRUE;
//
// Assume the Ethernet Header is in the first buffer of the packet.
// The following loop is to find the start address of the data after
// the ethernet header. This may be either in the first NDIS buffer
// or in the second.
//
while (TRUE)
{
#ifdef NDIS51_MINIPORT
NdisQueryBufferSafe(pNdisBuffer, &pVa, &BufferLength, NormalPagePriority);
#else
NdisQueryBuffer(pNdisBuffer, &pVa, &BufferLength);
#endif
//
// The query can fail if the system is low on resources.
//
if (pVa == NULL)
{
break;
}
//
// Remember the start of the ethernet header for later.
//
if (IsFirstVa)
{
pStartVa = pVa;
IsFirstVa = FALSE;
}
//
// Have we gone far enough into the packet?
//
if (BytesToSkip == 0)
{
break;
}
//
// Does the current buffer contain bytes past the Ethernet
// header? If so, stop.
//
if (BufferLength > BytesToSkip)
{
pVa = (PVOID)((PUCHAR)pVa + BytesToSkip);
BufferLength -= BytesToSkip;
break;
}
//
// We haven't gone past the Ethernet header yet, so go
// to the next buffer.
//
BytesToSkip -= BufferLength;
pNdisBuffer = NDIS_BUFFER_LINKAGE(pNdisBuffer);
}
if (pVa == NULL)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
//
// Allocate space for the Ethernet + VLAN tag header.
//
pEthTagBuffer = NdisAllocateFromNPagedLookasideList(&pVElan->TagLookaside);
//
// Memory allocation failed, can't send out the packet
//
if (pEthTagBuffer == NULL)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
//
// Allocate NDIS buffers for the Ethernet + VLAN tag header and
// the data that follows these.
//
NdisAllocateBuffer(&Status,
&pSecondBuffer,
pVElan->BufferPoolHandle,
pVa, // byte following the Eth+tag headers
BufferLength);
NdisAllocateBuffer(&Status2,
&pFirstBuffer,
pVElan->BufferPoolHandle,
pEthTagBuffer,
ETH_HEADER_SIZE + VLAN_TAG_HEADER_SIZE);
if (Status != NDIS_STATUS_SUCCESS || Status2 != NDIS_STATUS_SUCCESS)
{
//
// One of the buffer allocations failed.
//
if (Status == NDIS_STATUS_SUCCESS)
{
NdisFreeBuffer(pSecondBuffer);
}
if (Status2 == NDIS_STATUS_SUCCESS)
{
NdisFreeBuffer(pFirstBuffer);
}
NdisFreeToNPagedLookasideList(&pVElan->TagLookaside, pEthTagBuffer);
Status = NDIS_STATUS_RESOURCES;
break;
}
//
// All allocations were successful, now prepare the packet
// to be sent down to the lower driver.
//
MyPacket->Private.Head = NDIS_BUFFER_LINKAGE(pNdisBuffer);
NdisChainBufferAtFront(MyPacket, pSecondBuffer)
NdisChainBufferAtFront(MyPacket, pFirstBuffer)
//
// Prepare the Ethernet and tag headers.
//
NdisMoveMemory(pEthTagBuffer, pStartVa, 2 * ETH_LENGTH_OF_ADDRESS);
pTpid = (PUSHORT)((PUCHAR)pEthTagBuffer + 2 * ETH_LENGTH_OF_ADDRESS);
*pTpid = TPID;
pTagHeader = (PVLAN_TAG_HEADER)(pTpid + 1);
//
// Write Ieee 802Q info to packet frame
//
INITIALIZE_TAG_HEADER_TO_ZERO(pTagHeader);
if (NdisPacket8021qInfo.Value)
{
SET_USER_PRIORITY_TO_TAG(pTagHeader, NdisPacket8021qInfo.TagHeader.UserPriority);
}
else
{
SET_USER_PRIORITY_TO_TAG(pTagHeader, 0);
}
SET_CANONICAL_FORMAT_ID_TO_TAG (pTagHeader, 0);
if (NdisPacket8021qInfo.TagHeader.VlanId)
{
SET_VLAN_ID_TO_TAG (pTagHeader, NdisPacket8021qInfo.TagHeader.VlanId);
}
else
{
SET_VLAN_ID_TO_TAG (pTagHeader, pVElan->VlanId);
}
pTypeLength = (PUSHORT)((PUCHAR)pTagHeader + sizeof(pTagHeader->TagInfo));
*pTypeLength = *((PUSHORT)((PUCHAR)pStartVa + 2 * ETH_LENGTH_OF_ADDRESS));
//
// Clear the Ieee8021QInfo field in packet being sent down
// to prevent double tag insertion!
//
NDIS_PER_PACKET_INFO_FROM_PACKET(MyPacket, Ieee8021QInfo) = 0;
break;
}
while (FALSE);
return Status;
}
#endif // IEEE_VLAN_SUPPORT