reactos/drivers/network/dd/nvnet/requests.c
Dmitry Borisov b79fbe2333 [NVNET] Add driver for nForce-based NICs
The driver supports all nVidia chipset models from 2001 until 2010, starting from nForce.
All NICs are compatible with x86 and amd64 devices only.

Tested by Daniel Reimer on OG Xbox and by me on MCP board.

CORE-15872 CORE-16216
2022-04-16 11:54:24 +03:00

1506 lines
44 KiB
C

/*
* PROJECT: ReactOS nVidia nForce Ethernet Controller Driver
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Miniport information callbacks
* COPYRIGHT: Copyright 2021-2022 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "nvnet.h"
#define NDEBUG
#include "debug.h"
/* GLOBALS ********************************************************************/
static const NDIS_OID NvpSupportedOidList[] =
{
OID_GEN_SUPPORTED_LIST,
OID_GEN_CURRENT_PACKET_FILTER,
OID_GEN_HARDWARE_STATUS,
OID_GEN_MEDIA_SUPPORTED,
OID_GEN_MEDIA_IN_USE,
OID_GEN_MAXIMUM_LOOKAHEAD,
OID_GEN_MAXIMUM_FRAME_SIZE,
OID_GEN_MAXIMUM_SEND_PACKETS,
OID_GEN_LINK_SPEED,
OID_GEN_TRANSMIT_BUFFER_SPACE,
OID_GEN_RECEIVE_BUFFER_SPACE,
OID_GEN_RECEIVE_BLOCK_SIZE,
OID_GEN_TRANSMIT_BLOCK_SIZE,
OID_GEN_VENDOR_ID,
OID_GEN_VENDOR_DESCRIPTION,
OID_GEN_VENDOR_DRIVER_VERSION,
OID_GEN_CURRENT_LOOKAHEAD,
OID_GEN_DRIVER_VERSION,
OID_GEN_MAXIMUM_TOTAL_SIZE,
OID_GEN_MAC_OPTIONS,
OID_GEN_MEDIA_CONNECT_STATUS,
OID_GEN_VLAN_ID,
OID_802_3_PERMANENT_ADDRESS,
OID_802_3_CURRENT_ADDRESS,
OID_802_3_MULTICAST_LIST,
OID_802_3_MAXIMUM_LIST_SIZE,
/* Statistics */
OID_GEN_XMIT_OK,
OID_GEN_RCV_OK,
OID_GEN_XMIT_ERROR,
OID_GEN_RCV_ERROR,
OID_GEN_RCV_NO_BUFFER,
OID_GEN_DIRECTED_FRAMES_RCV,
OID_GEN_RCV_CRC_ERROR,
OID_GEN_TRANSMIT_QUEUE_LENGTH,
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,
/* Offload */
OID_TCP_TASK_OFFLOAD,
/* Power management */
OID_PNP_CAPABILITIES,
OID_PNP_SET_POWER,
OID_PNP_QUERY_POWER,
OID_PNP_ADD_WAKE_UP_PATTERN,
OID_PNP_REMOVE_WAKE_UP_PATTERN,
OID_PNP_ENABLE_WAKE_UP
};
/* FUNCTIONS ******************************************************************/
static
ULONG
NvNetGetLinkSpeed(
_In_ PNVNET_ADAPTER Adapter)
{
ULONG LinkSpeedMbps;
switch (Adapter->LinkSpeed)
{
case NVREG_LINKSPEED_10:
LinkSpeedMbps = 10;
break;
case NVREG_LINKSPEED_100:
LinkSpeedMbps = 100;
break;
case NVREG_LINKSPEED_1000:
LinkSpeedMbps = 1000;
break;
default:
UNREACHABLE;
break;
}
return LinkSpeedMbps;
}
static
ULONG
PacketFilterToMask(
_In_ ULONG PacketFilter)
{
ULONG FilterMask = NVREG_PFF_ALWAYS | NVREG_PFF_MYADDR | NVREG_PFF_PROMISC;
if (PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS)
{
FilterMask &= ~NVREG_PFF_MYADDR;
}
if (PacketFilter & NDIS_PACKET_TYPE_BROADCAST)
{
FilterMask &= ~NVREG_PFF_PROMISC;
}
return FilterMask;
}
static
DECLSPEC_NOINLINE /* Called from pageable code */
VOID
NvNetApplyPacketFilter(
_In_ PNVNET_ADAPTER Adapter)
{
UCHAR Address[ETH_LENGTH_OF_ADDRESS];
UCHAR Mask[ETH_LENGTH_OF_ADDRESS];
ULONG FilterMask;
BOOLEAN RestartReceiver;
if (Adapter->PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST)
{
NdisZeroMemory(Address, sizeof(Address));
NdisZeroMemory(Mask, sizeof(Mask));
Address[0] |= NVREG_MCASTADDRA_FORCE;
Mask[0] |= NVREG_MCASTADDRA_FORCE;
}
else if (Adapter->PacketFilter & NDIS_PACKET_TYPE_MULTICAST)
{
if (Adapter->MulticastListSize > 0)
{
ULONG i, j;
NdisFillMemory(Address, sizeof(Address), 0xFF);
NdisFillMemory(Mask, sizeof(Mask), 0xFF);
for (i = 0; i < Adapter->MulticastListSize; ++i)
{
PUCHAR MacAddress = Adapter->MulticastList[i].MacAddress;
for (j = 0; j < ETH_LENGTH_OF_ADDRESS; ++j)
{
Address[j] &= MacAddress[j];
Mask[j] &= ~MacAddress[j];
}
}
for (j = 0; j < ETH_LENGTH_OF_ADDRESS; ++j)
{
Mask[j] |= Address[j];
}
}
else
{
NdisZeroMemory(Address, sizeof(Address));
NdisZeroMemory(Mask, sizeof(Mask));
}
}
else
{
NdisZeroMemory(Address, sizeof(Address));
NdisFillMemory(Mask, sizeof(Mask), 0xFF);
}
FilterMask = NV_READ(Adapter, NvRegPacketFilterFlags) & NVREG_PFF_PAUSE_RX;
FilterMask |= PacketFilterToMask(Adapter->PacketFilter);
NdisAcquireSpinLock(&Adapter->Receive.Lock);
RestartReceiver = !!(NV_READ(Adapter, NvRegReceiverControl) & NVREG_RCVCTL_START);
if (RestartReceiver)
{
NvNetStopReceiver(Adapter);
}
NV_WRITE(Adapter, NvRegMulticastAddrA,
Address[3] << 24 | Address[2] << 16 | Address[1] << 8 | Address[0]);
NV_WRITE(Adapter, NvRegMulticastAddrB, Address[5] << 8 | Address[4]);
NV_WRITE(Adapter, NvRegMulticastMaskA,
Mask[3] << 24 | Mask[2] << 16 | Mask[1] << 8 | Mask[0]);
NV_WRITE(Adapter, NvRegMulticastMaskB, Mask[5] << 8 | Mask[4]);
NV_WRITE(Adapter, NvRegPacketFilterFlags, FilterMask);
if (RestartReceiver)
{
NvNetStartReceiver(Adapter);
}
NdisReleaseSpinLock(&Adapter->Receive.Lock);
}
static
VOID
NvNetReadStatistics(
_In_ PNVNET_ADAPTER Adapter)
{
Adapter->Statistics.HwTxCnt += NV_READ(Adapter, NvRegTxCnt);
Adapter->Statistics.HwTxZeroReXmt += NV_READ(Adapter, NvRegTxZeroReXmt);
Adapter->Statistics.HwTxOneReXmt += NV_READ(Adapter, NvRegTxOneReXmt);
Adapter->Statistics.HwTxManyReXmt += NV_READ(Adapter, NvRegTxManyReXmt);
Adapter->Statistics.HwTxLateCol += NV_READ(Adapter, NvRegTxLateCol);
Adapter->Statistics.HwTxUnderflow += NV_READ(Adapter, NvRegTxUnderflow);
Adapter->Statistics.HwTxLossCarrier += NV_READ(Adapter, NvRegTxLossCarrier);
Adapter->Statistics.HwTxExcessDef += NV_READ(Adapter, NvRegTxExcessDef);
Adapter->Statistics.HwTxRetryErr += NV_READ(Adapter, NvRegTxRetryErr);
Adapter->Statistics.HwRxFrameErr += NV_READ(Adapter, NvRegRxFrameErr);
Adapter->Statistics.HwRxExtraByte += NV_READ(Adapter, NvRegRxExtraByte);
Adapter->Statistics.HwRxLateCol += NV_READ(Adapter, NvRegRxLateCol);
Adapter->Statistics.HwRxRunt += NV_READ(Adapter, NvRegRxRunt);
Adapter->Statistics.HwRxFrameTooLong += NV_READ(Adapter, NvRegRxFrameTooLong);
Adapter->Statistics.HwRxOverflow += NV_READ(Adapter, NvRegRxOverflow);
Adapter->Statistics.HwRxFCSErr += NV_READ(Adapter, NvRegRxFCSErr);
Adapter->Statistics.HwRxFrameAlignErr += NV_READ(Adapter, NvRegRxFrameAlignErr);
Adapter->Statistics.HwRxLenErr += NV_READ(Adapter, NvRegRxLenErr);
Adapter->Statistics.HwRxUnicast += NV_READ(Adapter, NvRegRxUnicast);
Adapter->Statistics.HwRxMulticast += NV_READ(Adapter, NvRegRxMulticast);
Adapter->Statistics.HwRxBroadcast += NV_READ(Adapter, NvRegRxBroadcast);
if (Adapter->Features & DEV_HAS_STATISTICS_V2)
{
Adapter->Statistics.HwTxDef += NV_READ(Adapter, NvRegTxDef);
Adapter->Statistics.HwTxFrame += NV_READ(Adapter, NvRegTxFrame);
Adapter->Statistics.HwRxCnt += NV_READ(Adapter, NvRegRxCnt);
Adapter->Statistics.HwTxPause += NV_READ(Adapter, NvRegTxPause);
Adapter->Statistics.HwRxPause += NV_READ(Adapter, NvRegRxPause);
Adapter->Statistics.HwRxDropFrame += NV_READ(Adapter, NvRegRxDropFrame);
}
if (Adapter->Features & DEV_HAS_STATISTICS_V3)
{
Adapter->Statistics.HwTxUnicast += NV_READ(Adapter, NvRegTxUnicast);
Adapter->Statistics.HwTxMulticast += NV_READ(Adapter, NvRegTxMulticast);
Adapter->Statistics.HwTxBroadcast += NV_READ(Adapter, NvRegTxBroadcast);
}
}
static
VOID
NvNetQueryHwCounter(
_In_ PNVNET_ADAPTER Adapter,
_In_ NDIS_OID Oid,
_Out_ PULONG64 Counter)
{
switch (Oid)
{
case OID_GEN_XMIT_OK:
*Counter = (Adapter->Features & DEV_HAS_STATISTICS_V2)
? Adapter->Statistics.HwTxFrame
: (Adapter->Statistics.HwTxZeroReXmt +
Adapter->Statistics.HwTxOneReXmt +
Adapter->Statistics.HwTxManyReXmt);
break;
case OID_GEN_RCV_OK:
*Counter = (Adapter->Statistics.HwRxUnicast +
Adapter->Statistics.HwRxMulticast +
Adapter->Statistics.HwRxBroadcast);
break;
case OID_GEN_XMIT_ERROR:
*Counter = (Adapter->Statistics.HwTxRetryErr +
Adapter->Statistics.HwTxLateCol +
Adapter->Statistics.HwTxUnderflow +
Adapter->Statistics.HwTxLossCarrier +
Adapter->Statistics.HwTxExcessDef);
break;
case OID_GEN_RCV_ERROR:
*Counter = (Adapter->Statistics.HwRxFrameAlignErr +
Adapter->Statistics.HwRxLenErr +
Adapter->Statistics.HwRxRunt +
Adapter->Statistics.HwRxFrameTooLong +
Adapter->Statistics.HwRxFCSErr +
Adapter->Statistics.HwRxFrameErr +
Adapter->Statistics.HwRxExtraByte +
Adapter->Statistics.HwRxLateCol);
break;
case OID_GEN_RCV_NO_BUFFER:
*Counter = (Adapter->Statistics.HwRxDropFrame +
Adapter->Statistics.HwRxOverflow +
Adapter->Statistics.ReceiveIrqNoBuffers);
break;
case OID_GEN_DIRECTED_FRAMES_RCV:
*Counter = Adapter->Statistics.HwRxUnicast;
break;
case OID_GEN_RCV_CRC_ERROR:
*Counter = Adapter->Statistics.HwRxFCSErr;
break;
case OID_802_3_RCV_ERROR_ALIGNMENT:
*Counter = Adapter->Statistics.HwRxFrameErr;
break;
case OID_802_3_XMIT_ONE_COLLISION:
*Counter = Adapter->Statistics.HwTxOneReXmt;
break;
case OID_802_3_XMIT_MORE_COLLISIONS:
*Counter = Adapter->Statistics.HwTxManyReXmt;
break;
case OID_802_3_XMIT_DEFERRED:
*Counter = Adapter->Statistics.HwTxDef;
break;
case OID_802_3_XMIT_MAX_COLLISIONS:
*Counter = Adapter->Statistics.HwTxRetryErr;
break;
case OID_802_3_RCV_OVERRUN:
*Counter = Adapter->Statistics.HwRxOverflow;
break;
case OID_802_3_XMIT_UNDERRUN:
*Counter = Adapter->Statistics.HwTxUnderflow;
break;
case OID_802_3_XMIT_HEARTBEAT_FAILURE:
*Counter = Adapter->Statistics.HwTxZeroReXmt;
break;
case OID_802_3_XMIT_TIMES_CRS_LOST:
*Counter = Adapter->Statistics.HwTxLossCarrier;
break;
case OID_802_3_XMIT_LATE_COLLISIONS:
*Counter = Adapter->Statistics.HwTxLateCol;
break;
default:
UNREACHABLE;
break;
}
}
static
VOID
NvNetQuerySoftwareCounter(
_In_ PNVNET_ADAPTER Adapter,
_In_ NDIS_OID Oid,
_Out_ PULONG64 Counter)
{
switch (Oid)
{
case OID_GEN_XMIT_OK:
*Counter = Adapter->Statistics.TransmitOk;
break;
case OID_GEN_RCV_OK:
*Counter = Adapter->Statistics.ReceiveOk;
break;
case OID_GEN_XMIT_ERROR:
*Counter = Adapter->Statistics.TransmitErrors;
break;
case OID_GEN_RCV_ERROR:
*Counter = Adapter->Statistics.ReceiveErrors;
break;
case OID_GEN_RCV_NO_BUFFER:
*Counter = Adapter->Statistics.ReceiveNoBuffers;
break;
case OID_GEN_DIRECTED_FRAMES_RCV:
*Counter = 0;
break;
case OID_GEN_RCV_CRC_ERROR:
*Counter = Adapter->Statistics.ReceiveCrcErrors;
break;
case OID_802_3_RCV_ERROR_ALIGNMENT:
*Counter = Adapter->Statistics.ReceiveAlignmentErrors;
break;
case OID_802_3_XMIT_ONE_COLLISION:
*Counter = Adapter->Statistics.TransmitOneRetry;
break;
case OID_802_3_XMIT_MORE_COLLISIONS:
*Counter = (Adapter->Statistics.TransmitOk -
Adapter->Statistics.TransmitOneRetry -
Adapter->Statistics.TransmitZeroRetry);
break;
case OID_802_3_XMIT_DEFERRED:
*Counter = Adapter->Statistics.TransmitDeferred;
break;
case OID_802_3_XMIT_MAX_COLLISIONS:
*Counter = Adapter->Statistics.TransmitExcessiveCollisions;
break;
case OID_802_3_RCV_OVERRUN:
*Counter = Adapter->Statistics.ReceiveOverrunErrors;
break;
case OID_802_3_XMIT_UNDERRUN:
*Counter = Adapter->Statistics.TransmitUnderrunErrors;
break;
case OID_802_3_XMIT_HEARTBEAT_FAILURE:
*Counter = Adapter->Statistics.TransmitZeroRetry;
break;
case OID_802_3_XMIT_TIMES_CRS_LOST:
*Counter = Adapter->Statistics.TransmitLostCarrierSense;
break;
case OID_802_3_XMIT_LATE_COLLISIONS:
*Counter = Adapter->Statistics.TransmitLateCollisions;
break;
default:
UNREACHABLE;
break;
}
}
static
NDIS_STATUS
NvNetFillPowerManagementCapabilities(
_In_ PNVNET_ADAPTER Adapter,
_Out_ PNDIS_PNP_CAPABILITIES Capabilities)
{
Capabilities->WakeUpCapabilities.MinMagicPacketWakeUp =
Capabilities->WakeUpCapabilities.MinPatternWakeUp =
Capabilities->WakeUpCapabilities.MinLinkChangeWakeUp = NdisDeviceStateD3;
/* All hardware is PM-aware */
return NDIS_STATUS_SUCCESS;
}
static
ULONG
BuildFrameSignature(
_In_ PNVNET_WAKE_FRAME WakeFrame)
{
ULONG i, j, Crc;
Crc = 0xFFFFFFFF;
for (i = 0; i < sizeof(WakeFrame->WakeUpPattern); ++i)
{
if (WakeFrame->PatternMask.AsUCHAR[i / 8] & (1 << (i % 8)))
{
Crc ^= WakeFrame->WakeUpPattern[i];
for (j = 8; j > 0; --j)
{
Crc = (Crc >> 1) ^ (-(LONG)(Crc & 1) & 0xEDB88320);
}
}
}
return ~Crc;
}
static
VOID
WriteWakeFrame(
_In_ PNVNET_ADAPTER Adapter,
_In_ PNVNET_WAKE_FRAME WakeFrame,
_In_ ULONG FrameNumber)
{
ULONG Offset = FrameNumber * 5 * sizeof(ULONG);
if (FrameNumber >= NV_WAKEUPPATTERNS)
{
Offset += NV_PATTERN_V2_OFFSET;
}
NV_WRITE(Adapter, NvRegPatternCrc + Offset, BuildFrameSignature(WakeFrame));
NV_WRITE(Adapter, NvRegPatternMask0 + Offset, WakeFrame->PatternMask.AsULONG[0]);
NV_WRITE(Adapter, NvRegPatternMask1 + Offset, WakeFrame->PatternMask.AsULONG[1]);
NV_WRITE(Adapter, NvRegPatternMask2 + Offset, WakeFrame->PatternMask.AsULONG[2]);
NV_WRITE(Adapter, NvRegPatternMask3 + Offset, WakeFrame->PatternMask.AsULONG[3]);
}
static
ULONG
FrameNumberToWakeUpMask(
_In_ ULONG FrameNumber)
{
if (FrameNumber < 5)
return 0x10000 << FrameNumber;
else
return 0;
}
static
ULONG
FrameNumberToPowerMask(
_In_ ULONG FrameNumber)
{
switch (FrameNumber)
{
case 5:
return NVREG_POWERSTATE2_WAKEUPPAT_5;
case 6:
return NVREG_POWERSTATE2_WAKEUPPAT_6;
case 7:
return NVREG_POWERSTATE2_WAKEUPPAT_7;
default:
return 0;
}
}
VOID
NvNetSetPowerState(
_In_ PNVNET_ADAPTER Adapter,
_In_ NDIS_DEVICE_POWER_STATE NewPowerState,
_In_ ULONG WakeFlags)
{
ULONG i, PowerState, PowerState2, WakeUpFlags;
NV_READ(Adapter, NvRegPowerCap);
WakeUpFlags = 0;
PowerState2 = 0;
if (Adapter->Features & DEV_HAS_POWER_CNTRL)
{
PowerState2 = NV_READ(Adapter, NvRegPowerState2);
PowerState2 &= ~(NVREG_POWERSTATE2_WAKEUPPAT_5 |
NVREG_POWERSTATE2_WAKEUPPAT_6 |
NVREG_POWERSTATE2_WAKEUPPAT_7);
}
if (NewPowerState != NdisDeviceStateD0)
{
ULONG FramesEnabled = 0;
if (WakeFlags & NDIS_PNP_WAKE_UP_PATTERN_MATCH)
WakeUpFlags |= NVREG_WAKEUPFLAGS_ENABLE_MAGPAT;
if (WakeFlags & NDIS_PNP_WAKE_UP_LINK_CHANGE)
WakeUpFlags |= NVREG_WAKEUPFLAGS_ENABLE_LINKCHANGE;
if (WakeFlags & NDIS_PNP_WAKE_UP_MAGIC_PACKET)
{
WakeUpFlags |= NVREG_WAKEUPFLAGS_ENABLE_WAKEUPPAT;
for (i = 0; i < RTL_NUMBER_OF(Adapter->WakeFrames); ++i)
{
PNVNET_WAKE_FRAME WakeFrame = Adapter->WakeFrames[i];
if (!WakeFrame)
continue;
WriteWakeFrame(Adapter, WakeFrame, i);
PowerState2 |= FrameNumberToPowerMask(i);
WakeUpFlags |= FrameNumberToWakeUpMask(i);
++FramesEnabled;
}
}
if (WakeUpFlags)
{
if (!(Adapter->Flags & NV_MAC_IN_USE))
{
PowerState2 &= ~NVREG_POWERSTATE2_GATE_CLOCKS;
PowerState2 |= NVREG_POWERSTATE2_GATE_CLOCK_3;
if (!FramesEnabled && (WakeUpFlags & NVREG_WAKEUPFLAGS_ENABLE_LINKCHANGE))
PowerState2 |= NVREG_POWERSTATE2_GATE_CLOCK_1;
if (FramesEnabled < NV_WAKEUPMASKENTRIES)
PowerState2 |= NVREG_POWERSTATE2_GATE_CLOCK_2;
}
NvNetStartReceiver(Adapter);
}
else
{
if (!(Adapter->Flags & NV_MAC_IN_USE))
PowerState2 |= NVREG_POWERSTATE2_GATE_CLOCKS;
}
}
NdisStallExecution(NV_POWER_STALL);
NV_WRITE(Adapter, NvRegWakeUpFlags, WakeUpFlags);
if (Adapter->Features & DEV_HAS_POWER_CNTRL)
{
NV_WRITE(Adapter, NvRegPowerState2, PowerState2);
}
NV_WRITE(Adapter, NvRegPowerState,
NV_READ(Adapter, NvRegPowerState) | NVREG_POWERSTATE_POWEREDUP);
for (i = 0; i < NV_POWER_ATTEMPTS; ++i)
{
ULONG State = NV_READ(Adapter, NvRegPowerState);
if (!(State & NVREG_POWERSTATE_POWEREDUP))
break;
NV_WRITE(Adapter, NvRegPowerState, State | NVREG_POWERSTATE_POWEREDUP);
NdisStallExecution(NV_POWER_DELAY);
}
PowerState = NewPowerState - 1;
if (WakeUpFlags)
{
PowerState |= NVREG_POWERSTATE_VALID;
}
NV_WRITE(Adapter, NvRegPowerState, PowerState);
}
static
CODE_SEG("PAGE")
VOID
NTAPI
NvNetPowerWorker(
_In_ PNDIS_WORK_ITEM WorkItem,
_In_opt_ PVOID Context)
{
PNVNET_ADAPTER Adapter = Context;
UNREFERENCED_PARAMETER(WorkItem);
PAGED_CODE();
if (Adapter->PowerStatePending == NdisDeviceStateD0)
{
NvNetSetPowerState(Adapter, NdisDeviceStateD0, 0);
NT_VERIFY(NvNetInitNIC(Adapter, TRUE) == NDIS_STATUS_SUCCESS);
NvNetStartAdapter(Adapter);
NvNetApplyPacketFilter(Adapter);
}
else
{
NvNetPauseProcessing(Adapter);
NvNetStopAdapter(Adapter);
NvNetIdleTransmitter(Adapter, FALSE);
NvNetStopTransmitter(Adapter);
NvNetStopReceiver(Adapter);
NV_WRITE(Adapter, NvRegTxRxControl,
Adapter->TxRxControl | NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET);
NdisStallExecution(NV_TXRX_RESET_DELAY);
NvNetFlushTransmitQueue(Adapter, NDIS_STATUS_FAILURE);
NvNetSetPowerState(Adapter, Adapter->PowerStatePending, Adapter->WakeFlags);
}
NdisMSetInformationComplete(Adapter->AdapterHandle, NDIS_STATUS_SUCCESS);
}
static
NDIS_STATUS
NvNetSetPower(
_In_ PNVNET_ADAPTER Adapter,
_In_ NDIS_DEVICE_POWER_STATE NewPowerState)
{
Adapter->PowerStatePending = NewPowerState;
NdisInitializeWorkItem(&Adapter->PowerWorkItem, NvNetPowerWorker, Adapter);
NdisScheduleWorkItem(&Adapter->PowerWorkItem);
return NDIS_STATUS_PENDING;
}
static
NDIS_STATUS
NvNetAddWakeUpPattern(
_In_ PNVNET_ADAPTER Adapter,
_In_ PNDIS_PM_PACKET_PATTERN Pattern)
{
ULONG FrameNumber;
NDIS_STATUS Status;
PNVNET_WAKE_FRAME WakeFrame;
if (!_BitScanForward(&FrameNumber, Adapter->WakeFrameBitmap))
{
return NDIS_STATUS_RESOURCES;
}
Status = NdisAllocateMemoryWithTag((PVOID*)&WakeFrame, sizeof(*WakeFrame), NVNET_TAG);
if (Status != NDIS_STATUS_SUCCESS)
{
return NDIS_STATUS_RESOURCES;
}
Adapter->WakeFrameBitmap &= ~(1 << FrameNumber);
NdisZeroMemory(WakeFrame, sizeof(*WakeFrame));
NdisMoveMemory(&WakeFrame->PatternMask,
(PUCHAR)Pattern + sizeof(NDIS_PM_PACKET_PATTERN),
min(Pattern->MaskSize, 16));
NdisMoveMemory(&WakeFrame->WakeUpPattern,
(PUCHAR)Pattern + Pattern->PatternOffset,
min(Pattern->PatternSize, 128));
Adapter->WakeFrames[FrameNumber] = WakeFrame;
/* TODO: VLAN frame translation */
return NDIS_STATUS_SUCCESS;
}
static
BOOLEAN
NvEqualMemory(
_In_reads_bytes_(Length) PVOID Destination,
_In_reads_bytes_(Length) PVOID Source,
_In_ ULONG Length)
{
ULONG i;
PUCHAR Src, Dest;
Src = Source;
Dest = Destination;
for (i = 0; i < Length; ++i)
{
if (Src[i] != Dest[i])
return FALSE;
}
return TRUE;
}
/* 'memcmp' is unavailable for some reason */
#undef NdisEqualMemory
#define NdisEqualMemory NvEqualMemory
static
NDIS_STATUS
NvNetRemoveWakeUpPattern(
_In_ PNVNET_ADAPTER Adapter,
_In_ PNDIS_PM_PACKET_PATTERN Pattern)
{
ULONG i;
for (i = 0; i < RTL_NUMBER_OF(Adapter->WakeFrames); ++i)
{
PNVNET_WAKE_FRAME WakeFrame = Adapter->WakeFrames[i];
if (!WakeFrame)
continue;
if (!NdisEqualMemory(&WakeFrame->PatternMask,
(PUCHAR)Pattern + sizeof(NDIS_PM_PACKET_PATTERN),
min(Pattern->MaskSize, 16)))
{
continue;
}
if (!NdisEqualMemory(&WakeFrame->WakeUpPattern,
(PUCHAR)Pattern + Pattern->PatternOffset,
min(Pattern->PatternSize, 128)))
{
continue;
}
NdisFreeMemory(WakeFrame, sizeof(*WakeFrame), 0);
Adapter->WakeFrameBitmap |= (1 << i);
Adapter->WakeFrames[i] = NULL;
return NDIS_STATUS_SUCCESS;
}
return NDIS_STATUS_INVALID_DATA;
}
static
ULONG
NvNetGetWakeUp(
_In_ PNVNET_ADAPTER Adapter)
{
return Adapter->WakeFlags & (NDIS_PNP_WAKE_UP_MAGIC_PACKET |
NDIS_PNP_WAKE_UP_PATTERN_MATCH |
NDIS_PNP_WAKE_UP_LINK_CHANGE);
}
static
VOID
NvNetEnableWakeUp(
_In_ PNVNET_ADAPTER Adapter,
_In_ ULONG Flags)
{
Adapter->WakeFlags = Flags;
}
static
NDIS_STATUS
NvNetGetTcpTaskOffload(
_In_ PNVNET_ADAPTER Adapter,
_In_ PNDIS_TASK_OFFLOAD_HEADER TaskOffloadHeader,
_In_ ULONG InformationBufferLength,
_Out_ PULONG BytesWritten,
_Out_ PULONG BytesNeeded)
{
ULONG InfoLength;
PNDIS_TASK_OFFLOAD TaskOffload;
if (!(Adapter->Flags & (NV_SEND_CHECKSUM | NV_SEND_LARGE_SEND)))
{
*BytesWritten = 0;
*BytesNeeded = 0;
return NDIS_STATUS_NOT_SUPPORTED;
}
InfoLength = sizeof(NDIS_TASK_OFFLOAD_HEADER);
if (Adapter->Flags & NV_SEND_CHECKSUM)
{
InfoLength += FIELD_OFFSET(NDIS_TASK_OFFLOAD, TaskBuffer) +
sizeof(NDIS_TASK_TCP_IP_CHECKSUM);
}
if (Adapter->Flags & NV_SEND_LARGE_SEND)
{
InfoLength += FIELD_OFFSET(NDIS_TASK_OFFLOAD, TaskBuffer) +
sizeof(NDIS_TASK_TCP_LARGE_SEND);
}
if (InformationBufferLength < InfoLength)
{
*BytesWritten = 0;
*BytesNeeded = InfoLength;
return NDIS_STATUS_BUFFER_TOO_SHORT;
}
if ((TaskOffloadHeader->EncapsulationFormat.Encapsulation != IEEE_802_3_Encapsulation) &&
(TaskOffloadHeader->EncapsulationFormat.Encapsulation != UNSPECIFIED_Encapsulation ||
TaskOffloadHeader->EncapsulationFormat.EncapsulationHeaderSize != sizeof(ETH_HEADER)))
{
*BytesWritten = 0;
*BytesNeeded = 0;
return NDIS_STATUS_NOT_SUPPORTED;
}
if (TaskOffloadHeader->Version != NDIS_TASK_OFFLOAD_VERSION)
{
*BytesWritten = 0;
*BytesNeeded = 0;
return NDIS_STATUS_NOT_SUPPORTED;
}
TaskOffloadHeader->OffsetFirstTask = sizeof(NDIS_TASK_OFFLOAD_HEADER);
TaskOffload = (PNDIS_TASK_OFFLOAD)(TaskOffloadHeader + 1);
if (Adapter->Flags & NV_SEND_CHECKSUM)
{
PNDIS_TASK_TCP_IP_CHECKSUM ChecksumTask;
TaskOffload->Size = sizeof(NDIS_TASK_OFFLOAD);
TaskOffload->Version = NDIS_TASK_OFFLOAD_VERSION;
TaskOffload->Task = TcpIpChecksumNdisTask;
TaskOffload->TaskBufferLength = sizeof(NDIS_TASK_TCP_IP_CHECKSUM);
TaskOffload->OffsetNextTask = FIELD_OFFSET(NDIS_TASK_OFFLOAD, TaskBuffer) +
sizeof(NDIS_TASK_TCP_IP_CHECKSUM);
ChecksumTask = (PNDIS_TASK_TCP_IP_CHECKSUM)TaskOffload->TaskBuffer;
NdisZeroMemory(ChecksumTask, sizeof(*ChecksumTask));
ChecksumTask->V4Transmit.IpOptionsSupported = 1;
ChecksumTask->V4Transmit.TcpOptionsSupported = 1;
ChecksumTask->V4Transmit.TcpChecksum = 1;
ChecksumTask->V4Transmit.UdpChecksum = 1;
ChecksumTask->V4Transmit.IpChecksum = 1;
ChecksumTask->V4Receive.IpOptionsSupported = 1;
ChecksumTask->V4Receive.TcpOptionsSupported = 1;
ChecksumTask->V4Receive.TcpChecksum = 1;
ChecksumTask->V4Receive.UdpChecksum = 1;
ChecksumTask->V4Receive.IpChecksum = 1;
TaskOffload = (PNDIS_TASK_OFFLOAD)(ChecksumTask + 1);
}
if (Adapter->Flags & NV_SEND_LARGE_SEND)
{
PNDIS_TASK_TCP_LARGE_SEND LargeSendTask;
TaskOffload->Size = sizeof(NDIS_TASK_OFFLOAD);
TaskOffload->Version = NDIS_TASK_OFFLOAD_VERSION;
TaskOffload->Task = TcpLargeSendNdisTask;
TaskOffload->TaskBufferLength = sizeof(NDIS_TASK_TCP_LARGE_SEND);
TaskOffload->OffsetNextTask = 0;
LargeSendTask = (PNDIS_TASK_TCP_LARGE_SEND)TaskOffload->TaskBuffer;
LargeSendTask->Version = NDIS_TASK_TCP_LARGE_SEND_V0;
LargeSendTask->MinSegmentCount = NVNET_MINIMUM_LSO_SEGMENT_COUNT;
LargeSendTask->MaxOffLoadSize = NVNET_MAXIMUM_LSO_FRAME_SIZE;
LargeSendTask->IpOptions = TRUE;
LargeSendTask->TcpOptions = TRUE;
}
TaskOffload->OffsetNextTask = 0;
*BytesWritten = InfoLength;
*BytesNeeded = 0;
return NDIS_STATUS_SUCCESS;
}
static
NDIS_STATUS
NvNetSetTcpTaskOffload(
_Inout_ PNVNET_ADAPTER Adapter,
_In_ PNDIS_TASK_OFFLOAD_HEADER TaskOffloadHeader,
_In_ PULONG BytesRead)
{
ULONG Offset;
PNDIS_TASK_OFFLOAD TaskOffload;
if (TaskOffloadHeader->Version != NDIS_TASK_OFFLOAD_VERSION)
{
return NDIS_STATUS_NOT_SUPPORTED;
}
Adapter->IpHeaderOffset = TaskOffloadHeader->EncapsulationFormat.EncapsulationHeaderSize;
TaskOffload = (PNDIS_TASK_OFFLOAD)TaskOffloadHeader;
Offset = TaskOffloadHeader->OffsetFirstTask;
while (Offset)
{
*BytesRead += FIELD_OFFSET(NDIS_TASK_OFFLOAD, TaskBuffer);
TaskOffload = (PNDIS_TASK_OFFLOAD)((PUCHAR)TaskOffload + Offset);
switch (TaskOffload->Task)
{
case TcpIpChecksumNdisTask:
{
PNDIS_TASK_TCP_IP_CHECKSUM Task;
*BytesRead += sizeof(NDIS_TASK_TCP_IP_CHECKSUM);
if (!(Adapter->Flags & NV_SEND_CHECKSUM))
{
return NDIS_STATUS_NOT_SUPPORTED;
}
Task = (PNDIS_TASK_TCP_IP_CHECKSUM)TaskOffload->TaskBuffer;
Adapter->Offload.SendTcpChecksum = Task->V4Transmit.TcpChecksum;
Adapter->Offload.SendUdpChecksum = Task->V4Transmit.UdpChecksum;
Adapter->Offload.SendIpChecksum = Task->V4Transmit.IpChecksum;
Adapter->Offload.ReceiveTcpChecksum = Task->V4Receive.TcpChecksum;
Adapter->Offload.ReceiveUdpChecksum = Task->V4Receive.UdpChecksum;
Adapter->Offload.ReceiveIpChecksum = Task->V4Receive.IpChecksum;
break;
}
case TcpLargeSendNdisTask:
{
PNDIS_TASK_TCP_LARGE_SEND Task;
if (!(Adapter->Flags & NV_SEND_LARGE_SEND))
{
return NDIS_STATUS_NOT_SUPPORTED;
}
if ((TaskOffloadHeader->
EncapsulationFormat.Encapsulation != IEEE_802_3_Encapsulation) &&
(TaskOffloadHeader->
EncapsulationFormat.Encapsulation != UNSPECIFIED_Encapsulation ||
TaskOffloadHeader->
EncapsulationFormat.EncapsulationHeaderSize != sizeof(ETH_HEADER)))
{
return NDIS_STATUS_NOT_SUPPORTED;
}
*BytesRead += sizeof(NDIS_TASK_TCP_LARGE_SEND);
Task = (PNDIS_TASK_TCP_LARGE_SEND)TaskOffload->TaskBuffer;
if (Task->MinSegmentCount != NVNET_MINIMUM_LSO_SEGMENT_COUNT)
return NDIS_STATUS_NOT_SUPPORTED;
if (Task->MaxOffLoadSize > NVNET_MAXIMUM_LSO_FRAME_SIZE)
return NDIS_STATUS_NOT_SUPPORTED;
/* Nothing to do */
break;
}
default:
break;
}
Offset = TaskOffload->OffsetNextTask;
}
NdisAcquireSpinLock(&Adapter->Send.Lock);
if (Adapter->Offload.ReceiveTcpChecksum ||
Adapter->Offload.ReceiveUdpChecksum ||
Adapter->Offload.ReceiveIpChecksum)
{
Adapter->TxRxControl |= NVREG_TXRXCTL_RXCHECK;
}
else
{
Adapter->TxRxControl &= ~NVREG_TXRXCTL_RXCHECK;
}
NV_WRITE(Adapter, NvRegTxRxControl, Adapter->TxRxControl);
NdisReleaseSpinLock(&Adapter->Send.Lock);
return NDIS_STATUS_SUCCESS;
}
NDIS_STATUS
NTAPI
MiniportQueryInformation(
_In_ NDIS_HANDLE MiniportAdapterContext,
_In_ NDIS_OID Oid,
_In_ PVOID InformationBuffer,
_In_ ULONG InformationBufferLength,
_Out_ PULONG BytesWritten,
_Out_ PULONG BytesNeeded)
{
PNVNET_ADAPTER Adapter = (PNVNET_ADAPTER)MiniportAdapterContext;
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
ULONG InfoLength;
PVOID InfoPtr;
union _GENERIC_INFORMATION
{
USHORT Ushort;
ULONG Ulong;
ULONG64 Ulong64;
NDIS_MEDIUM Medium;
NDIS_HARDWARE_STATUS Status;
NDIS_DEVICE_POWER_STATE PowerState;
} GenericInfo;
InfoLength = sizeof(ULONG);
InfoPtr = &GenericInfo;
switch (Oid)
{
case OID_GEN_SUPPORTED_LIST:
InfoPtr = (PVOID)&NvpSupportedOidList;
InfoLength = sizeof(NvpSupportedOidList);
break;
case OID_GEN_HARDWARE_STATUS:
InfoLength = sizeof(NDIS_HARDWARE_STATUS);
GenericInfo.Status = NdisHardwareStatusReady;
break;
case OID_GEN_MEDIA_SUPPORTED:
case OID_GEN_MEDIA_IN_USE:
{
InfoLength = sizeof(NDIS_MEDIUM);
GenericInfo.Medium = NdisMedium802_3;
break;
}
case OID_GEN_CURRENT_LOOKAHEAD:
case OID_GEN_MAXIMUM_LOOKAHEAD:
{
GenericInfo.Ulong = Adapter->MaximumFrameSize - sizeof(ETH_HEADER);
break;
}
case OID_GEN_MAXIMUM_FRAME_SIZE:
{
GenericInfo.Ulong = Adapter->MaximumFrameSize;
break;
}
case OID_GEN_LINK_SPEED:
{
GenericInfo.Ulong = NvNetGetLinkSpeed(Adapter) * 10000;
break;
}
case OID_GEN_TRANSMIT_BUFFER_SPACE:
{
/* TODO: Change this later, once the driver can handle multipacket sends */
GenericInfo.Ulong = Adapter->MaximumFrameSize;
break;
}
case OID_GEN_RECEIVE_BUFFER_SPACE:
{
GenericInfo.Ulong = Adapter->MaximumFrameSize * NVNET_RECEIVE_DESCRIPTORS;
}
case OID_GEN_MAXIMUM_TOTAL_SIZE:
case OID_GEN_TRANSMIT_BLOCK_SIZE:
case OID_GEN_RECEIVE_BLOCK_SIZE:
{
GenericInfo.Ulong = Adapter->MaximumFrameSize;
break;
}
case OID_GEN_VENDOR_ID:
{
GenericInfo.Ulong = 0;
GenericInfo.Ulong |= (Adapter->PermanentMacAddress[0] << 16);
GenericInfo.Ulong |= (Adapter->PermanentMacAddress[1] << 8);
GenericInfo.Ulong |= (Adapter->PermanentMacAddress[2] & 0xFF);
break;
}
case OID_GEN_DRIVER_VERSION:
{
InfoLength = sizeof(USHORT);
GenericInfo.Ushort = (NDIS_MINIPORT_MAJOR_VERSION << 8) | NDIS_MINIPORT_MINOR_VERSION;
break;
}
case OID_GEN_VENDOR_DESCRIPTION:
{
static const CHAR VendorDesc[] = "nVidia nForce Ethernet Controller";
InfoPtr = (PVOID)&VendorDesc;
InfoLength = sizeof(VendorDesc);
break;
}
case OID_GEN_CURRENT_PACKET_FILTER:
{
GenericInfo.Ulong = Adapter->PacketFilter;
break;
}
case OID_GEN_MAC_OPTIONS:
{
GenericInfo.Ulong = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA |
NDIS_MAC_OPTION_TRANSFERS_NOT_PEND |
NDIS_MAC_OPTION_NO_LOOPBACK;
if (Adapter->Flags & NV_PACKET_PRIORITY)
GenericInfo.Ulong |= NDIS_MAC_OPTION_8021P_PRIORITY;
if (Adapter->Flags & NV_VLAN_TAGGING)
GenericInfo.Ulong |= NDIS_MAC_OPTION_8021Q_VLAN;
break;
}
case OID_GEN_MEDIA_CONNECT_STATUS:
{
GenericInfo.Ulong = Adapter->Connected ? NdisMediaStateConnected
: NdisMediaStateDisconnected;
break;
}
case OID_GEN_MAXIMUM_SEND_PACKETS:
{
/* TODO: Multipacket sends */
GenericInfo.Ulong = 1;
break;
}
case OID_GEN_VENDOR_DRIVER_VERSION:
{
/* 1.0.0 */
GenericInfo.Ulong = 0x100;
break;
}
case OID_GEN_XMIT_OK:
case OID_GEN_RCV_OK:
case OID_GEN_XMIT_ERROR:
case OID_GEN_RCV_ERROR:
case OID_GEN_RCV_NO_BUFFER:
case OID_GEN_DIRECTED_FRAMES_RCV:
case OID_GEN_RCV_CRC_ERROR:
case OID_802_3_RCV_ERROR_ALIGNMENT:
case OID_802_3_XMIT_ONE_COLLISION:
case OID_802_3_XMIT_MORE_COLLISIONS:
case OID_802_3_XMIT_DEFERRED:
case OID_802_3_XMIT_MAX_COLLISIONS:
case OID_802_3_RCV_OVERRUN:
case OID_802_3_XMIT_UNDERRUN:
case OID_802_3_XMIT_HEARTBEAT_FAILURE:
case OID_802_3_XMIT_TIMES_CRS_LOST:
case OID_802_3_XMIT_LATE_COLLISIONS:
{
if (Adapter->Features & DEV_HAS_STATISTICS_COUNTERS)
{
NvNetReadStatistics(Adapter);
NvNetQueryHwCounter(Adapter, Oid, &GenericInfo.Ulong64);
}
else
{
NvNetQuerySoftwareCounter(Adapter, Oid, &GenericInfo.Ulong64);
}
*BytesNeeded = sizeof(ULONG64);
if (InformationBufferLength < sizeof(ULONG))
{
*BytesWritten = 0;
return NDIS_STATUS_BUFFER_TOO_SHORT;
}
if (InformationBufferLength >= sizeof(ULONG64))
{
*BytesWritten = sizeof(ULONG64);
NdisMoveMemory(InformationBuffer, InfoPtr, sizeof(ULONG64));
}
else
{
*BytesWritten = sizeof(ULONG);
NdisMoveMemory(InformationBuffer, InfoPtr, sizeof(ULONG));
}
return NDIS_STATUS_SUCCESS;
}
case OID_GEN_TRANSMIT_QUEUE_LENGTH:
{
GenericInfo.Ulong = NVNET_TRANSMIT_BLOCKS - Adapter->Send.TcbSlots;
break;
}
case OID_802_3_PERMANENT_ADDRESS:
{
InfoPtr = Adapter->PermanentMacAddress;
InfoLength = ETH_LENGTH_OF_ADDRESS;
break;
}
case OID_802_3_CURRENT_ADDRESS:
{
InfoPtr = Adapter->CurrentMacAddress;
InfoLength = ETH_LENGTH_OF_ADDRESS;
break;
}
case OID_802_3_MULTICAST_LIST:
{
InfoPtr = Adapter->MulticastList;
InfoLength = Adapter->MulticastListSize * ETH_LENGTH_OF_ADDRESS;
break;
}
case OID_802_3_MAXIMUM_LIST_SIZE:
{
GenericInfo.Ulong = NVNET_MULTICAST_LIST_SIZE;
break;
}
case OID_TCP_TASK_OFFLOAD:
{
return NvNetGetTcpTaskOffload(Adapter,
InformationBuffer,
InformationBufferLength,
BytesWritten,
BytesWritten);
}
case OID_PNP_ENABLE_WAKE_UP:
{
GenericInfo.Ulong = NvNetGetWakeUp(Adapter);
break;
}
case OID_PNP_CAPABILITIES:
{
InfoLength = sizeof(NDIS_PNP_CAPABILITIES);
if (InformationBufferLength < InfoLength)
{
*BytesWritten = 0;
*BytesNeeded = InfoLength;
return NDIS_STATUS_BUFFER_TOO_SHORT;
}
*BytesWritten = InfoLength;
*BytesNeeded = 0;
return NvNetFillPowerManagementCapabilities(Adapter, InformationBuffer);
}
case OID_PNP_QUERY_POWER:
{
return NDIS_STATUS_SUCCESS;
}
case OID_GEN_VLAN_ID:
{
/* TODO: Implement software VLAN support */
if (!(Adapter->Flags & NV_VLAN_TAGGING))
{
Status = NDIS_STATUS_NOT_SUPPORTED;
break;
}
GenericInfo.Ulong = Adapter->VlanId;
break;
}
default:
Status = NDIS_STATUS_INVALID_OID;
break;
}
if (Status == NDIS_STATUS_SUCCESS)
{
if (InfoLength > InformationBufferLength)
{
*BytesWritten = 0;
*BytesNeeded = InfoLength;
Status = NDIS_STATUS_BUFFER_TOO_SHORT;
}
else
{
NdisMoveMemory(InformationBuffer, InfoPtr, InfoLength);
*BytesWritten = InfoLength;
*BytesNeeded = 0;
}
}
else
{
*BytesWritten = 0;
*BytesNeeded = 0;
}
return Status;
}
NDIS_STATUS
NTAPI
MiniportSetInformation(
_In_ NDIS_HANDLE MiniportAdapterContext,
_In_ NDIS_OID Oid,
_In_ PVOID InformationBuffer,
_In_ ULONG InformationBufferLength,
_Out_ PULONG BytesRead,
_Out_ PULONG BytesNeeded)
{
PNVNET_ADAPTER Adapter = (PNVNET_ADAPTER)MiniportAdapterContext;
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
ULONG GenericUlong;
*BytesRead = 0;
*BytesNeeded = 0;
switch (Oid)
{
case OID_802_3_MULTICAST_LIST:
{
if (InformationBufferLength % ETH_LENGTH_OF_ADDRESS)
{
*BytesNeeded = (InformationBufferLength / ETH_LENGTH_OF_ADDRESS) *
ETH_LENGTH_OF_ADDRESS;
Status = NDIS_STATUS_INVALID_LENGTH;
break;
}
if (InformationBufferLength > sizeof(Adapter->MulticastList))
{
*BytesNeeded = sizeof(Adapter->MulticastList);
Status = NDIS_STATUS_MULTICAST_FULL;
break;
}
*BytesRead = InformationBufferLength;
NdisMoveMemory(Adapter->MulticastList, InformationBuffer, InformationBufferLength);
Adapter->MulticastListSize = InformationBufferLength / ETH_LENGTH_OF_ADDRESS;
NvNetApplyPacketFilter(Adapter);
break;
}
case OID_GEN_CURRENT_PACKET_FILTER:
{
if (InformationBufferLength < sizeof(ULONG))
{
*BytesNeeded = sizeof(ULONG);
Status = NDIS_STATUS_INVALID_LENGTH;
break;
}
*BytesRead = sizeof(ULONG);
NdisMoveMemory(&GenericUlong, InformationBuffer, sizeof(ULONG));
if (GenericUlong & ~NVNET_PACKET_FILTERS)
{
Status = NDIS_STATUS_NOT_SUPPORTED;
break;
}
/* Do not check to see if the filter is the same filter */
Adapter->PacketFilter = GenericUlong;
NvNetApplyPacketFilter(Adapter);
break;
}
case OID_GEN_CURRENT_LOOKAHEAD:
{
if (InformationBufferLength < sizeof(ULONG))
{
*BytesNeeded = sizeof(ULONG);
Status = NDIS_STATUS_INVALID_LENGTH;
break;
}
/* Nothing to do */
*BytesRead = sizeof(ULONG);
break;
}
case OID_GEN_VLAN_ID:
{
if (InformationBufferLength < sizeof(ULONG))
{
*BytesNeeded = sizeof(ULONG);
Status = NDIS_STATUS_INVALID_LENGTH;
break;
}
if (!(Adapter->Flags & NV_VLAN_TAGGING))
{
Status = NDIS_STATUS_NOT_SUPPORTED;
break;
}
*BytesRead = sizeof(ULONG);
NdisMoveMemory(&GenericUlong, InformationBuffer, sizeof(ULONG));
if (GenericUlong > NVNET_MAXIMUM_VLAN_ID)
{
Status = NDIS_STATUS_FAILURE;
break;
}
Adapter->VlanId = GenericUlong;
break;
}
case OID_TCP_TASK_OFFLOAD:
{
if (InformationBufferLength < sizeof(NDIS_TASK_OFFLOAD_HEADER))
{
*BytesNeeded = sizeof(NDIS_TASK_OFFLOAD_HEADER);
Status = NDIS_STATUS_INVALID_LENGTH;
break;
}
*BytesRead = InformationBufferLength;
Status = NvNetSetTcpTaskOffload(Adapter, InformationBuffer, BytesRead);
break;
}
case OID_PNP_ADD_WAKE_UP_PATTERN:
{
if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN))
{
*BytesNeeded = sizeof(NDIS_PM_PACKET_PATTERN);
Status = NDIS_STATUS_INVALID_LENGTH;
break;
}
*BytesRead = sizeof(NDIS_PM_PACKET_PATTERN);
Status = NvNetAddWakeUpPattern(Adapter, InformationBuffer);
break;
}
case OID_PNP_REMOVE_WAKE_UP_PATTERN:
{
if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN))
{
*BytesNeeded = sizeof(NDIS_PM_PACKET_PATTERN);
Status = NDIS_STATUS_INVALID_LENGTH;
break;
}
*BytesRead = sizeof(NDIS_PM_PACKET_PATTERN);
Status = NvNetRemoveWakeUpPattern(Adapter, InformationBuffer);
break;
}
case OID_PNP_ENABLE_WAKE_UP:
{
if (InformationBufferLength < sizeof(ULONG))
{
*BytesNeeded = sizeof(ULONG);
Status = NDIS_STATUS_INVALID_LENGTH;
break;
}
*BytesRead = sizeof(ULONG);
NdisMoveMemory(&GenericUlong, InformationBuffer, sizeof(ULONG));
NvNetEnableWakeUp(Adapter, GenericUlong);
break;
}
case OID_PNP_SET_POWER:
{
if (InformationBufferLength < sizeof(NDIS_DEVICE_POWER_STATE))
{
*BytesNeeded = sizeof(NDIS_DEVICE_POWER_STATE);
Status = NDIS_STATUS_INVALID_LENGTH;
break;
}
*BytesRead = sizeof(ULONG);
NdisMoveMemory(&GenericUlong, InformationBuffer, sizeof(ULONG));
if (GenericUlong < NdisDeviceStateD0 || GenericUlong > NdisDeviceStateD3)
{
Status = NDIS_STATUS_INVALID_DATA;
break;
}
Status = NvNetSetPower(Adapter, GenericUlong);
break;
}
default:
Status = NDIS_STATUS_NOT_SUPPORTED;
break;
}
return Status;
}