/* * 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 */ /* 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 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; }