Sample Code
Windows Driver Samples/ PCIDRV - WDF Driver for PCI Device/ C++/ kmdf/ HW/ nic_pm.c/
/**************************************************************************** ** COPYRIGHT (C) 1994-1997 INTEL CORPORATION ** ** DEVELOPED FOR MICROSOFT BY INTEL CORP., HILLSBORO, OREGON ** ** HTTP://WWW.INTEL.COM/ ** ** THIS FILE IS PART OF THE INTEL ETHEREXPRESS PRO/100B(TM) AND ** ** ETHEREXPRESS PRO/100+(TM) NDIS 5.0 MINIPORT SAMPLE DRIVER ** ****************************************************************************/ #include "precomp.h" #if defined(EVENT_TRACING) #include "nic_pm.tmh" #endif // Things to note: // PME_ena bit should be active before the 82558 is set into low power mode // Default for WOL should generate wake up event after a HW Reset // Fixed Packet Filtering // Need to verify that the micro code is loaded and Micro Machine is active // Clock signal is active on PCI clock // Address Matching // Need to enable IAMatch_Wake_En bit and the MCMatch_Wake_En bit is set // ARP Wakeup // Need to set BRCST DISABL bet to 0 (broadcast enable) // To handle VLAN set the VLAN_ARP bit // IP address needs to be configured with 16 least significant bits // Set the IP Address in the IP_Address configuration word. // Fixed WakeUp Filters: // There are 3ight different fixed WakeUp Filters // ( Unicast, Multicast, Arp. etc). // Link Status Event // Set Link_Status_Wakeup Enable bit. // Flexible filtering: // Supports: ARP packets, Directed, Magic Packet and Link Event // Flexible Filtering Overview: // driver should program micro-code before setting card into low power // Incoming packets are compared against the loadable microcode. If PME is // is enabled then, the system is woken up. // Segments are defined in book - but not implemented here. // WakeUp Packet -that causes the machine to wake up will be stored // in the Micro Machine temporary storage area so that the driver can read it. // Software Work: // Power Down: // OS requests the driver to go to a low power state // SW sets CU and RU to idle by issuing a Selective Reset to the device // 3rd portion .- Wake Up Segments defintion // The above three segments are loaded as on chain. The last CB must have // its EL bit set. // Device can now be powered down. // Software driver completes OS request // OS then physically switches the Device to low power state // // Power Up: // OS powers up the Device // driver should NOT initialize the Device. It should NOT issue a Self Test // Driver Initiates a PORT DUMP command // Device dumps its internal registers including the wakeup frame storage area // SW reads the PME register // SW reads the WakeUp Frame Data, analyzes it and acts accordingly // SW restores its cvonfiguration and and resumes normal operation. // // // Power Management definitions from the Intel Handbook // // // Definitions from Table 4.2, Pg 4.9 // of the 10/100 Mbit Ethernet Family Software Technical // Reference Manual // #define PMC_Offset 0xDE #define E100_PMC_WAKE_FROM_D0 0x1 #define E100_PMC_WAKE_FROM_D1 0x2 #define E100_PMC_WAKE_FROM_D2 0x4 #define E100_PMC_WAKE_FROM_D3HOT 0x8 #define E100_PMC_WAKE_FROM_D3_AUX 0x10 // // Load Programmable filter definintions. // Taken from C-19 from the Software Reference Manual. // It has examples too. The opcode used for load is 0x80000 // #define BIT_15_13 0xA000 #define CB_LOAD_PROG_FILTER BIT_3 #define CU_LOAD_PROG_FILTER_EL BIT_7 #define CU_SUCCEED_LOAD_PROG_FILTER BIT_15_13 #define CB_FILTER_EL BIT_7 #define CB_FILTER_PREDEFINED_FIX BIT_6 #define CB_FILTER_ARP_WAKEUP BIT_3 #define CB_FILTER_IA_WAKEUP BIT_1 #define CU_SCB_NULL ((UINT)-1) #pragma pack( push, enter_include1, 1 ) // // Define the PM Capabilities register in the device // portion of the PCI config space // typedef struct _MP_PM_CAP_REG { #pragma warning(disable:4214) // bit field types other than int warning USHORT UnInteresting:11; USHORT PME_Support:5; #pragma warning(default:4214) } MP_PM_CAP_REG; // // Define the PM Control/Status Register // typedef struct _MP_PMCSR { #pragma warning(disable:4214) // bit field types other than int warning USHORT PowerState:2; // Power State; USHORT Res:2; // reserved USHORT DynData:1; // Ignored USHORT Res1:3; // Reserved USHORT PME_En:1; // Enable device to set the PME Event; USHORT DataSel:4; // Unused USHORT DataScale:2; // Data Scale - Unused USHORT PME_Status:1; // PME Status - Sticky bit; #pragma warning(default:4214) } MP_PMCSR ; typedef struct _MP_PM_PCI_SPACE { UCHAR Stuff[PMC_Offset]; // PM capabilites MP_PM_CAP_REG PMCaps; // PM Control Status Register MP_PMCSR PMCSR; } MP_PM_PCI_SPACE , *PMP_PM_PCI_SPACE ; // // This is the Programmable Filter Command Structure // typedef struct _MP_PROG_FILTER_COMM_STRUCT { // CB Status Word USHORT CBStatus; // CB Command Word USHORT CBCommand; //Next CB PTR == ffff ffff ULONG NextCBPTR; //Programmable Filters ULONG FilterData[16]; } MP_PROG_FILTER_COMM_STRUCT,*PMP_PROG_FILTER_COMM_STRUCT; typedef struct _MP_PMDR { #pragma warning(disable:4214) // bit field types other than int warning // Status of the PME bit UCHAR PMEStatus:1; // Is the TCO busy UCHAR TCORequest:1; // Force TCO indication UCHAR TCOForce:1; // Is the TCO Ready UCHAR TCOReady:1; // Reserved UCHAR Reserved:1; // Has an InterestingPacket been received UCHAR InterestingPacket:1; // Has a Magic Packet been received UCHAR MagicPacket:1; // Has the Link Status been changed UCHAR LinkStatus:1; #pragma warning(default:4214) } MP_PMDR , *PMP_PMDR; //------------------------------------------------------------------------- // Structure used to set up a programmable filter. // This is overlayed over the Control/Status Register (CSR) //------------------------------------------------------------------------- typedef struct _CSR_FILTER_STRUC { // Status- used to verify if the load prog filter command // has been accepted .set to 0xa000 USHORT ScbStatus; // SCB Status register // Set to an opcode of 0x8 // UCHAR ScbCommandLow; // SCB Command register (low byte) // 80. Low + High gives the required opcode 0x80080000 UCHAR ScbCommandHigh; // SCB Command register (high byte) // Set to NULL ff ff ff ff ULONG NextPointer; // SCB General pointer // Set to a hardcoded filter, Arp + IA Match, + IP address union { ULONG u32; struct { UCHAR IPAddress[2]; UCHAR Reserved; UCHAR Set; }PreDefined; }Programmable; // Wake UP Filter union } CSR_FILTER_STRUC, *PCSR_FILTER_STRUC; #pragma pack( pop, enter_include1 ) #define MP_CLEAR_PMDR(pPMDR) (*pPMDR) = ((*pPMDR) | 0xe0); // clear the 3 uppermost bits in the PMDR //------------------------------------------------------------------------- // L O C A L P R O T O T Y P E S //------------------------------------------------------------------------- __inline NTSTATUS MPIssueScbPoMgmtCommand( IN PFDO_DATA Adapter, IN PCSR_FILTER_STRUC pFilter, IN BOOLEAN WaitForScb ); VOID MPCreateProgrammableFilter ( IN PMP_WAKE_PATTERN pMpWakePattern , IN PUCHAR pFilter, IN OUT PULONG pNext ); //------------------------------------------------------------------------- // P O W E R M G M T F U N C T I O N S //------------------------------------------------------------------------- PUCHAR HwReadPowerPMDR( IN PFDO_DATA Adapter ) /*++ Routine Description: This routine will read Hardware's PM registers Arguments: Adapter Pointer to our adapter Return Value: STATUS_SUCCESS NTSTATUS_HARD_ERRORS --*/ { UCHAR PMDR =0; PUCHAR pPMDR = NULL; #define CSR_SIZE sizeof (*Adapter->CSRAddress) ASSERT (CSR_SIZE == 0x18); pPMDR = 0x18 + (PUCHAR)Adapter->CSRAddress ; PMDR = *pPMDR; return pPMDR; } NTSTATUS MpClearPME_En ( IN PFDO_DATA FdoData, IN MP_PMCSR PMCSR ) { NTSTATUS status; UINT ulResult; PMCSR.PME_En = 0; ulResult = FdoData->BusInterface.SetBusData( FdoData->BusInterface.Context, PCI_WHICHSPACE_CONFIG, (PVOID)&PMCSR, FIELD_OFFSET(MP_PM_PCI_SPACE, PMCSR), sizeof(PMCSR)); ASSERT (ulResult == sizeof(PMCSR)); if (ulResult == sizeof(PMCSR)) { status = STATUS_SUCCESS; } else { status = STATUS_UNSUCCESSFUL; } return status; } VOID MPSetPowerLowPrivate( WDFINTERRUPT WdfInterrupt, PFDO_DATA FdoData ) /*++ Routine Description: The section follows the steps mentioned in Section C.2.6.2 of the Reference Manual. Arguments: Adapter Pointer to our adapter Return Value: --*/ { CSR_FILTER_STRUC Filter; USHORT IntStatus; MP_PMCSR PMCSR = {0}; ULONG ulResult; UNREFERENCED_PARAMETER( WdfInterrupt ); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "-->MPSetPowerLowPrivate\n"); RtlZeroMemory (&Filter, sizeof (Filter)); // // Before issue the command to low power state, we should ack all the // pending interrupts, then set the adapter's power to low state. // NIC_ACK_INTERRUPT(FdoData, IntStatus); // // If the driver should wake up the machine // if (FdoData->AllowWakeArming) { // // Send the WakeUp Pattern to the nic MPIssueScbPoMgmtCommand(FdoData, &Filter, TRUE); // // Section C.2.6.2 - The driver needs to wait for the CU to idle // The above function already waits for the CU to idle // ASSERT((FdoData->CSRAddress->ScbStatus & SCB_CUS_MASK) == SCB_CUS_IDLE); } else { ulResult = FdoData->BusInterface.GetBusData( FdoData->BusInterface.Context, PCI_WHICHSPACE_CONFIG, (PVOID)&PMCSR, FIELD_OFFSET(MP_PM_PCI_SPACE, PMCSR), sizeof(PMCSR)); if(ulResult != sizeof(PMCSR)){ ASSERT(ulResult == sizeof(PMCSR)); TraceEvents(TRACE_LEVEL_ERROR, DBG_POWER, "GetBusData for PMCSR failed\n"); return; } if (PMCSR.PME_En == 1) { // // PME is enabled. Clear the PME_En bit. // So that it is not asserted // MpClearPME_En (FdoData,PMCSR); } } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<--MPSetPowerLowPrivate\n"); } NTSTATUS MPSetPowerD0Private ( IN PFDO_DATA FdoData ) { PUCHAR pPMDR; NTSTATUS status; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "-->MPSetPowerD0Private\n"); // Dump the packet if necessary //Cause of Wake Up pPMDR = HwReadPowerPMDR(FdoData); status = NICInitializeAdapter(FdoData); // Clear the PMDR MP_CLEAR_PMDR(pPMDR); NICIssueSelectiveReset(FdoData); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<--MPSetPowerD0Private\n"); return status; } VOID HwSetWakeUpConfigure( IN PFDO_DATA FdoData, PUCHAR pPoMgmtConfigType, UINT WakeUpParameter ) { UNREFERENCED_PARAMETER( WakeUpParameter ); if (IsPoMgmtSupported( FdoData) == TRUE) { (*pPoMgmtConfigType)= ((*pPoMgmtConfigType) | CB_WAKE_ON_LINK_BYTE9 | CB_WAKE_ON_ARP_PKT_BYTE9 ); } } NTSTATUS MPSetUpFilterCB( IN PFDO_DATA FdoData ) { NTSTATUS status = STATUS_SUCCESS; PCB_HEADER_STRUC NonTxCmdBlockHdr = (PCB_HEADER_STRUC)FdoData->NonTxCmdBlock; PFILTER_CB_STRUC pFilterCb = (PFILTER_CB_STRUC)NonTxCmdBlockHdr; ULONG Curr = 0; ULONG Next = 0; PLIST_ENTRY pPatternEntry = ListNext(&FdoData->PoMgmt.PatternList) ; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "--> MPSetUpFilterCB\n"); RtlZeroMemory (pFilterCb, sizeof(*pFilterCb)); // Individual Address Setup NonTxCmdBlockHdr->CbStatus = 0; NonTxCmdBlockHdr->CbCommand = CB_EL_BIT | CB_LOAD_PROG_FILTER; NonTxCmdBlockHdr->CbLinkPointer = DRIVER_NULL; // go through each filter in the list. while (pPatternEntry != (&FdoData->PoMgmt.PatternList)) { PMP_WAKE_PATTERN pWakeUpPattern = NULL; //PNDIS_PM_PACKET_PATTERN pCurrPattern = NULL;; // initialize local variables pWakeUpPattern = CONTAINING_RECORD(pPatternEntry, MP_WAKE_PATTERN, linkListEntry); // increment the iterator pPatternEntry = ListNext (pPatternEntry); // Update the Curr Array Pointer Curr = Next; // Create the Programmable filter for this device. MPCreateProgrammableFilter (pWakeUpPattern , (PUCHAR)&pFilterCb->Pattern[Curr], &Next); if (Next >=16) { break; } } { // Set the EL bit on the last pattern PUCHAR pLastPattern = (PUCHAR) &pFilterCb->Pattern[Curr]; // Get to bit 31 pLastPattern[3] |= CB_FILTER_EL ; } ASSERT(FdoData->CSRAddress->ScbCommandLow == 0); // Wait for the CU to Idle before giving it this command if(!WaitScb(FdoData)) { status = STATUS_DEVICE_DATA_ERROR; } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<-- MPSetUpFilterCB\n"); return status; } NTSTATUS MPIssueScbPoMgmtCommand( IN PFDO_DATA FdoData, IN PCSR_FILTER_STRUC pNewFilter, IN BOOLEAN WaitForScb ) { NTSTATUS status = STATUS_UNSUCCESSFUL; UNREFERENCED_PARAMETER( pNewFilter ); UNREFERENCED_PARAMETER( WaitForScb ); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "--> MPIssueScbPoMgmtCommand\n"); do { // Set up SCB to issue this command status = MPSetUpFilterCB(FdoData); if (status != STATUS_SUCCESS) { break; } // Submit the configure command to the chip, and wait for // it to complete. FdoData->CSRAddress->ScbGeneralPointer = FdoData->NonTxCmdBlockPhys; status = D100SubmitCommandBlockAndWait(FdoData); if(status != STATUS_SUCCESS) { status = STATUS_DEVICE_DATA_ERROR; break; } } WHILE (FALSE); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<-- MPIssueScbPoMgmtCommand %x\n", status); return status; } NTSTATUS MPCalculateE100PatternForFilter ( IN PUCHAR pFrame, IN ULONG FrameLength, IN PUCHAR pMask, IN ULONG MaskLength, OUT PULONG pSignature ) /*++ Routine Description: This function outputs the E100 specific Pattern Signature used to wake up the machine. Section C.2.4 - CRC word calculation of a Flexible Filer Arguments: pFrame - Pattern Set by the protocols FrameLength - Length of the Pattern pMask - Mask set by the Protocols MaskLength - Length of the Mask pSignature - caller allocated return structure Return Value: Returns Success Failure - if the Pattern is greater than 129 bytes --*/ { const ULONG Coefficients = 0x04c11db7; ULONG Signature = 0; ULONG n = 0; ULONG i= 0; PUCHAR pCurrentMaskByte = pMask - 1; // init to -1 ULONG MaskOffset = 0; ULONG BitOffsetInMask = 0; ULONG MaskBit = 0; ULONG ShiftBy = 0; UCHAR FrameByte = 0; NTSTATUS status = STATUS_UNSUCCESSFUL; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "--> MPCalculateE100PatternForFilter\n"); *pSignature = 0; do { if (FrameLength > 128) { status = STATUS_UNSUCCESSFUL; break; } // The E100 driver can only accept 3 DWORDS of Mask in a single pattern if (MaskLength > (3*sizeof(ULONG))) { status = STATUS_UNSUCCESSFUL; break; } for (n=i=0;(n<128) && (n < FrameLength); ++n) { // The first half deals with the question - // Is the nth Frame byte to be included in the Filter // BitOffsetInMask = (n % 8); if (BitOffsetInMask == 0) { // // We need to move to a new byte. // [0] for 0th byte, [1] for 8th byte, [2] for 16th byte, etc. // MaskOffset = n/8; // This is the new byte we need to go // // if (MaskOffset == MaskLength) { break; } pCurrentMaskByte ++; ASSERT (*pCurrentMaskByte == pMask[n/8]); } // Now look at the actual bit in the mask MaskBit = 1 << BitOffsetInMask ; // If the current Mask Bit is set in the Mask then // we need to use it in the CRC calculation, otherwise we ignore it if (! (MaskBit & pCurrentMaskByte[0])) { continue; } // We are suppossed to take in the current byte as part of the CRC calculation // Initialize the variables FrameByte = pFrame[n]; ShiftBy = (i % 3 ) * 8; ASSERT (ShiftBy!= 24); // Bit 24 is never used if (Signature & 0x80000000) { Signature = ((Signature << 1) ^ ( FrameByte << ShiftBy) ^ Coefficients); } else { Signature = ((Signature << 1 ) ^ (FrameByte << ShiftBy)); } ++i; } // Clear bits 22-31 Signature &= 0x00ffffff; // Update the result *pSignature = Signature; // We have succeeded status = STATUS_SUCCESS; } WHILE (FALSE); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<-- MPCalculateE100PatternForFilter\n"); return status; } VOID MPCreateProgrammableFilter ( IN PMP_WAKE_PATTERN pMpWakePattern , IN PUCHAR pFilter, IN OUT PULONG pNext ) /*++ Routine Description: This function outputs the E100 specific Pattern Signature used to wake up the machine. Section C.2.4 - Load Programmable Filter page C.20 Arguments: pMpWakePattern - Filter will be created for this pattern, pFilter - Filter will be stored here, pNext - Used for validation . This Ulong will also be incremented by the size of the filter (in ulongs) Return Value: --*/ { PUCHAR pCurrentByte = pFilter; ULONG NumBytesWritten = 0; PULONG pCurrentUlong = (PULONG)pFilter; PNDIS_PM_PACKET_PATTERN pNdisPattern = (PNDIS_PM_PACKET_PATTERN)(&pMpWakePattern->Pattern[0]); ULONG LengthOfFilter = 0; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "--> MPCreateProgrammableFilter\n"); // Is there enough room for this pattern // { // Length in DWORDS LengthOfFilter = pNdisPattern->MaskSize /4; if (pNdisPattern->MaskSize % 4 != 0) { LengthOfFilter++; } // Increment LengthOfFilter to account for the 1st DWORD LengthOfFilter++; // We are only allowed 16 DWORDS in a filter if (*pNext + LengthOfFilter >= 16) { // Failure - early exit return; } } // Clear the Predefined bit; already cleared in the previous function. // first , initialize - *pCurrentUlong = 0; // Mask Length goes into Bits 27-29 of the 1st DWORD. MaskSize is measured in DWORDs { ULONG dwMaskSize = pNdisPattern->MaskSize /4; ULONG dwMLen = 0; // If there is a remainder a remainder then increment if (pNdisPattern->MaskSize % 4 != 0) { dwMaskSize++; } // // If we fail this assertion, it means our // MaskSize is greater than 16 bytes. // This filter should have been failed upfront at the time of the request // ASSERT (0 < dwMaskSize && dwMaskSize < 5); // // In the Spec, 0 - Single DWORD maske, 001 - 2 DWORD mask, // 011 - 3 DWORD mask, 111 - 4 Dword Mask. // if (dwMaskSize == 1) dwMLen = 0; if (dwMaskSize == 2) dwMLen = 1; if (dwMaskSize == 3) dwMLen = 3; if (dwMaskSize == 4) dwMLen = 7; // Adjust the Mlen, so it is in the correct position dwMLen = (dwMLen << 3); if (dwMLen != 0) { ASSERT (dwMLen <= 0x38 && dwMLen >= 0x08); } // These go into bits 27,28,29 (bits 3,4 and 5 of the 4th byte) pCurrentByte[3] |= dwMLen ; } // Add the signature to bits 0-23 of the 1st DWORD { PUCHAR pSignature = (PUCHAR)&pMpWakePattern->Signature; // Bits 0-23 are also the 1st three bytes of the DWORD pCurrentByte[0] = pSignature[0]; pCurrentByte[1] = pSignature[1]; pCurrentByte[2] = pSignature[2]; } // Lets move to the next DWORD. Init variables pCurrentByte += 4 ; NumBytesWritten = 4; pCurrentUlong = (PULONG)pCurrentByte; // We Copy in the Mask over here { // The Mask is at the end of the pattern PUCHAR pMask = (PUCHAR)pNdisPattern + sizeof(*pNdisPattern); //Dump (pMask,pNdisPattern->MaskSize, 0,1); RtlMoveMemory (pCurrentByte, pMask, pNdisPattern->MaskSize); NumBytesWritten += pNdisPattern->MaskSize; } // Update the output value { ULONG NumUlongs = (NumBytesWritten /4); if ((NumBytesWritten %4) != 0) { NumUlongs ++; } ASSERT (NumUlongs == LengthOfFilter); *pNext = *pNext + NumUlongs; } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<-- MPCreateProgrammableFilter\n"); return; } NTSTATUS MPSetPowerD0( PFDO_DATA FdoData ) /*++ Routine Description: This routine is called when the adapter receives a SetPower to D0. Arguments: Adapter Pointer to the adapter structure PowerState NewPowerState Return Value: --*/ { NTSTATUS status; //KIRQL oldIrql; // // MPSetPowerD0Private Initializes the adapte, issues a selective reset. // MPSetPowerD0Private (FdoData); ASSERT(FdoData->DevicePowerState == PowerDeviceD0); // // Set up the packet filter // WdfSpinLockAcquire(FdoData->Lock); status = NICSetPacketFilter( FdoData, FdoData->OldPacketFilter); // // If Set Packet Filter succeeds, restore the old packet filter // if (status == STATUS_SUCCESS) { FdoData->PacketFilter = FdoData->OldPacketFilter; } WdfSpinLockRelease(FdoData->Lock); // // Set up the multicast list address // WdfSpinLockAcquire(FdoData->RcvLock); status = NICSetMulticastList(FdoData); NICStartRecv(FdoData); WdfSpinLockRelease(FdoData->RcvLock); return status; } NTSTATUS MPSetPowerLow( PFDO_DATA FdoData, WDF_POWER_DEVICE_STATE PowerState ) /*++ Routine Description: This routine is called when the FdoData receives a SetPower to a PowerState > D0 Arguments: FdoData Pointer to the FdoData structure PowerState NewPowerState Return Value: NDIS_STATUS_SUCCESS NDIS_STATUS_PENDING STATUS_DEVICE_DATA_ERROR --*/ { NTSTATUS status = STATUS_SUCCESS; UNREFERENCED_PARAMETER( PowerState ); // // Stop sending packets. Create a new flag and make it part // of the Send Fail Mask. TODO: Does something need to happen here? // // // Stop hardware from receiving packets - Set the RU to idle. // TODO: Does something need to happen here? // // // Check the current status of the receive unit // if ((FdoData->CSRAddress->ScbStatus & SCB_RUS_MASK) != SCB_RUS_IDLE) { // // Issue an RU abort. Since an interrupt will be issued, the // RU will be started by the DPC. // status = D100IssueScbCommand(FdoData, SCB_RUC_ABORT, TRUE); } if (!NT_SUCCESS(status)) { return status; } // // MPSetPowerLowPrivate first disables the interrupt, acknowledges all the pending // interrupts and sets FdoData->DevicePowerState to the given low power state // then starts Hardware specific part of the transition to low power state // Setting up wake-up patterns, filters, wake-up events etc // // Interrupt is disabled and disconnected before entering D0Exit, so no need to // sychronize. // MPSetPowerLowPrivate(NULL, FdoData); return STATUS_SUCCESS; } BOOLEAN MPAreTwoPatternsEqual( IN PNDIS_PM_PACKET_PATTERN pNdisPattern1, IN PNDIS_PM_PACKET_PATTERN pNdisPattern2 ) /*++ Routine Description: This routine will compare two wake up patterns to see if they are equal Arguments: pNdisPattern1 - Pattern1 pNdisPattern2 - Pattern 2 Return Value: True - if patterns are equal False - Otherwise --*/ { BOOLEAN bEqual = FALSE; // Local variables used later in the compare section of this function PUCHAR pMask1, pMask2; PUCHAR pPattern1, pPattern2; UINT MaskSize, PatternSize; do { bEqual = (BOOLEAN)(pNdisPattern1->Priority == pNdisPattern2->Priority); if (bEqual == FALSE) { break; } bEqual = (BOOLEAN)(pNdisPattern1->MaskSize == pNdisPattern2->MaskSize); if (bEqual == FALSE) { break; } // // Verify the Mask // MaskSize = pNdisPattern1->MaskSize ; pMask1 = (PUCHAR) pNdisPattern1 + sizeof (NDIS_PM_PACKET_PATTERN); pMask2 = (PUCHAR) pNdisPattern2 + sizeof (NDIS_PM_PACKET_PATTERN); bEqual = (BOOLEAN)RtlEqualMemory (pMask1, pMask2, MaskSize); if (bEqual == FALSE) { break; } // // Verify the Pattern // bEqual = (BOOLEAN)(pNdisPattern1->PatternSize == pNdisPattern2->PatternSize); if (bEqual == FALSE) { break; } PatternSize = pNdisPattern2->PatternSize; pPattern1 = (PUCHAR) pNdisPattern1 + pNdisPattern1->PatternOffset; pPattern2 = (PUCHAR) pNdisPattern2 + pNdisPattern2->PatternOffset; bEqual = (BOOLEAN)RtlEqualMemory (pPattern1, pPattern2, PatternSize ); if (bEqual == FALSE) { break; } } WHILE (FALSE); return bEqual; } VOID NICExtractPMInfoFromPciSpace( PFDO_DATA FdoData, PUCHAR pPciConfig ) /*++ Routine Description: Looks at the PM information in the device specific section of the PCI Config space. Interprets the register values and stores it in the adapter structure Definitions from Table 4.2 & 4.3, Pg 4-9 & 4-10 of the 10/100 Mbit Ethernet Family Software Technical Reference Manual Arguments: Adapter Pointer to our adapter pPciConfig Pointer to Common Pci Space Return Value: --*/ { PMP_PM_PCI_SPACE pPmPciConfig = (PMP_PM_PCI_SPACE )pPciConfig; MP_PMCSR PMCSR; // // First interpret the PM Capabities register // { MP_PM_CAP_REG PmCaps; PmCaps = pPmPciConfig->PMCaps; if(PmCaps.PME_Support & E100_PMC_WAKE_FROM_D0) { FdoData->PoMgmt.bWakeFromD0 = TRUE; } if(PmCaps.PME_Support & E100_PMC_WAKE_FROM_D1) { FdoData->PoMgmt.bWakeFromD1 = TRUE; } if(PmCaps.PME_Support & E100_PMC_WAKE_FROM_D2) { FdoData->PoMgmt.bWakeFromD2 = TRUE; } if(PmCaps.PME_Support & E100_PMC_WAKE_FROM_D3HOT) { FdoData->PoMgmt.bWakeFromD3Hot = TRUE; } if(PmCaps.PME_Support & E100_PMC_WAKE_FROM_D3_AUX) { FdoData->PoMgmt.bWakeFromD3Aux = TRUE; } } // // Interpret the PM Control/Status Register // { PMCSR = pPmPciConfig->PMCSR; if (PMCSR.PME_En == 1) { // // PME is enabled. Clear the PME_En bit. // So that it is not asserted // MpClearPME_En (FdoData,PMCSR); } } } NTSTATUS NICSetPower( PFDO_DATA FdoData , WDF_POWER_DEVICE_STATE PowerState ) /*++ Routine Description: This routine is called when the FdoData receives a SetPower request. It redirects the call to an appropriate routine to Set the New PowerState Arguments: FdoData Pointer to the FdoData structure PowerState NewPowerState Return Value: NTSTATUS Code --*/ { NTSTATUS status = STATUS_SUCCESS; if(IsPoMgmtSupported(FdoData)){ if (PowerState == PowerDeviceD0) { TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "Entering fully on state\n"); MPSetPowerD0 (FdoData); } else { TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "Entering a deeper sleep state\n"); status = MPSetPowerLow (FdoData, PowerState); } } return status; } NTSTATUS NICAddWakeUpPattern( IN PFDO_DATA FdoData, IN PVOID InformationBuffer, IN UINT InformationBufferLength, OUT PULONG BytesRead, OUT PULONG BytesNeeded ) /*++ Routine Description: This routine will allocate a local memory structure, copy the pattern, insert the pattern into a linked list and return success We are gauranteed that we wll get only one request at a time, so this is implemented without locks. Arguments: FdoData FdoData structure InformationBuffer Wake up Pattern InformationBufferLength Wake Up Pattern Length Return Value: STATUS_Success - if successful. STATUS_UNSUCCESSFUL - if memory allocation fails. --*/ { NTSTATUS status = STATUS_UNSUCCESSFUL; PMP_WAKE_PATTERN pWakeUpPattern = NULL; ULONG AllocationLength = 0; PNDIS_PM_PACKET_PATTERN pPmPattern = NULL; ULONG Signature = 0; ULONG CopyLength = 0; ULONG safeAddResult; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "--> NICAddWakeUpPattern\n"); do { if(!FdoData->AllowWakeArming) { status = STATUS_NOT_SUPPORTED; break; } pPmPattern = (PNDIS_PM_PACKET_PATTERN) InformationBuffer; if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN)) { status = STATUS_BUFFER_TOO_SMALL; *BytesNeeded = sizeof(NDIS_PM_PACKET_PATTERN); break; } // // safeAddResult = pPmPattern->PatternOffset + pPmPattern->PatternSize // status = RtlULongAdd( pPmPattern->PatternOffset, pPmPattern->PatternSize, &safeAddResult) ; if (!NT_SUCCESS(status)) { break; } if (InformationBufferLength < safeAddResult) { status = STATUS_BUFFER_TOO_SMALL; *BytesNeeded = safeAddResult; break; } *BytesRead = safeAddResult; // // Calculate the e100 signature // status = MPCalculateE100PatternForFilter ( (PUCHAR)pPmPattern+ pPmPattern->PatternOffset, pPmPattern->PatternSize, (PUCHAR)pPmPattern +sizeof(NDIS_PM_PACKET_PATTERN), pPmPattern->MaskSize, &Signature ); if ( status != STATUS_SUCCESS) { break; } CopyLength = safeAddResult; // // Allocate the memory to hold the WakeUp Pattern // // AllocationLength = sizeof (MP_WAKE_PATTERN) + CopyLength; // status = RtlULongAdd( sizeof(MP_WAKE_PATTERN), CopyLength, &AllocationLength); if (!NT_SUCCESS(status)) { break; } pWakeUpPattern = ExAllocatePoolWithTag(NonPagedPool, AllocationLength, PCIDRV_POOL_TAG); if (!pWakeUpPattern) { break; } // // Initialize pWakeUpPattern // RtlZeroMemory (pWakeUpPattern, AllocationLength); pWakeUpPattern->AllocationSize = AllocationLength; pWakeUpPattern->Signature = Signature; // // Copy the pattern into local memory // RtlMoveMemory (&pWakeUpPattern->Pattern[0], InformationBuffer, CopyLength); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); // // Insert the pattern into the list // /* ExInterlockedInsertHeadList (&FdoData->PoMgmt.PatternList, &pWakeUpPattern->linkListEntry, &FdoData->Lock); */ WdfSpinLockAcquire(FdoData->Lock); InsertHeadList(&FdoData->PoMgmt.PatternList,&pWakeUpPattern->linkListEntry ); WdfSpinLockRelease(FdoData->Lock); status = STATUS_SUCCESS; } WHILE (FALSE); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<-- NICAddWakeUpPattern\n"); return status; } NTSTATUS NICRemoveWakeUpPattern( IN PFDO_DATA FdoData, IN PVOID InformationBuffer, IN UINT InformationBufferLength, OUT PULONG BytesRead, OUT PULONG BytesNeeded ) /*++ Routine Description: This routine will walk the list of wake up pattern and attempt to match the wake up pattern. If it finds a copy , it will remove that WakeUpPattern Arguments: FdoData FdoData structure InformationBuffer Wake up Pattern InformationBufferLength Wake Up Pattern Length Return Value: Success - if successful. STATUS_UNSUCCESSFUL - if memory allocation fails. --*/ { NTSTATUS status = STATUS_UNSUCCESSFUL; PNDIS_PM_PACKET_PATTERN pReqPattern = (PNDIS_PM_PACKET_PATTERN)InformationBuffer; PLIST_ENTRY pPatternEntry = ListNext(&FdoData->PoMgmt.PatternList) ; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "--> NICRemoveWakeUpPattern\n"); do { if(!FdoData->AllowWakeArming) { status = STATUS_NOT_SUPPORTED; break; } if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN)) { status = STATUS_BUFFER_TOO_SMALL; *BytesNeeded = sizeof(NDIS_PM_PACKET_PATTERN); break; } if (InformationBufferLength < pReqPattern->PatternOffset + pReqPattern->PatternSize) { status = STATUS_BUFFER_TOO_SMALL; *BytesNeeded = pReqPattern->PatternOffset + pReqPattern->PatternSize; break; } *BytesRead = pReqPattern->PatternOffset + pReqPattern->PatternSize; while (pPatternEntry != (&FdoData->PoMgmt.PatternList)) { BOOLEAN bIsThisThePattern = FALSE; PMP_WAKE_PATTERN pWakeUpPattern = NULL; PNDIS_PM_PACKET_PATTERN pCurrPattern = NULL;; // // initialize local variables // pWakeUpPattern = CONTAINING_RECORD(pPatternEntry, MP_WAKE_PATTERN, linkListEntry); pCurrPattern = (PNDIS_PM_PACKET_PATTERN)&pWakeUpPattern->Pattern[0]; // // increment the iterator // pPatternEntry = ListNext (pPatternEntry); // // Begin Check : Is (pCurrPattern == pReqPattern) // bIsThisThePattern = MPAreTwoPatternsEqual(pReqPattern, pCurrPattern); if (bIsThisThePattern == TRUE) { // // we have a match - remove the entry // RemoveEntryList (&pWakeUpPattern->linkListEntry); // // Free the entry // ExFreePoolWithTag(pWakeUpPattern, PCIDRV_POOL_TAG); status = STATUS_SUCCESS; break; } } } WHILE (FALSE); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<-- NICRemoveWakeUpPattern\n"); return status; } VOID NICRemoveAllWakeUpPatterns( PFDO_DATA FdoData ) /*++ Routine Description: This routine will walk the list of wake up pattern and free it Arguments: FdoData FdoData structure Return Value: Success - if successful. --*/ { PLIST_ENTRY pPatternEntry = ListNext(&FdoData->PoMgmt.PatternList) ; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "--> NICRemoveAllWakeUpPatterns\n"); while (pPatternEntry != (&FdoData->PoMgmt.PatternList)) { PMP_WAKE_PATTERN pWakeUpPattern = NULL; // // initialize local variables // pWakeUpPattern = CONTAINING_RECORD(pPatternEntry, MP_WAKE_PATTERN,linkListEntry); // // increment the iterator // pPatternEntry = ListNext (pPatternEntry); // // Remove the entry from the list // RemoveEntryList (&pWakeUpPattern->linkListEntry); // // Free the memory // ExFreePoolWithTag(pWakeUpPattern, PCIDRV_POOL_TAG); } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "<-- NICRemoveAllWakeUpPatterns\n"); } NTSTATUS NICConfigureForWakeUp( IN PFDO_DATA FdoData, IN BOOLEAN AddPattern ) /*++ Routine Description: Arguments: FdoData FdoData structure Return Value: Success - if successful. --*/ { #define MAX_WAKEUP_PATTERN_LENGTH 128 UCHAR Buffer[sizeof(NDIS_PM_PACKET_PATTERN) + MAX_WAKEUP_PATTERN_LENGTH]; PCHAR patternBuffer, nextMask, nextPattern; ULONG maskLen; PNDIS_PM_PACKET_PATTERN ndisPattern; ULONG bufLen; NTSTATUS status; ULONG unUsed; CHAR wakePattern[]={0xff,0xff,0xff,0xff,0xff,0xff}; //broadcast address patternBuffer = (PCHAR)&Buffer[0]; ndisPattern = (PNDIS_PM_PACKET_PATTERN)patternBuffer; RtlZeroMemory(ndisPattern, sizeof(NDIS_PM_PACKET_PATTERN)); ndisPattern->PatternSize = sizeof(wakePattern); maskLen = (ndisPattern->PatternSize-1)/8 + 1; nextMask = (PCHAR)patternBuffer + sizeof(NDIS_PM_PACKET_PATTERN); nextPattern = nextMask + maskLen; *nextMask = 0x3f; ndisPattern->MaskSize = maskLen; ndisPattern->PatternOffset = (ULONG) ((ULONG_PTR) nextPattern - (ULONG_PTR) patternBuffer); bufLen = sizeof(NDIS_PM_PACKET_PATTERN) + maskLen + ndisPattern->PatternSize; RtlCopyMemory(nextPattern, FdoData->CurrentAddress, ETHERNET_ADDRESS_LENGTH); if(AddPattern){ status = NICAddWakeUpPattern(FdoData, Buffer, bufLen, &unUsed, &unUsed); if(!NT_SUCCESS(status)){ TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "NICAddWakeupPattern failed %x\n", status); } }else{ status = NICRemoveWakeUpPattern(FdoData, Buffer, bufLen, &unUsed, &unUsed); if(!NT_SUCCESS(status)){ TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "NICRemoveWakeUpPattern failed %x\n", status); } } return status; } #if 0 NTSTATUS NICConfigureForWakeUp( IN PFDO_DATA FdoData, IN BOOLEAN AddPattern ) { #define MAX_WAKEUP_PATTERN_LENGTH 128 #define ETHER_IP_ICMP_HEADER_SIZE 14+20+8 UCHAR Buffer[sizeof(NDIS_PM_PACKET_PATTERN) + MAX_WAKEUP_PATTERN_LENGTH]; PCHAR patternBuffer, nextMask, nextPattern; ULONG maskLen; PNDIS_PM_PACKET_PATTERN ndisPattern; ULONG bufLen; NTSTATUS status; ULONG unUsed; CHAR pingPattern[]={'a','b','c','d','e','f','g','h'}; patternBuffer = (PCHAR)&Buffer[0]; ndisPattern = (PNDIS_PM_PACKET_PATTERN)patternBuffer; RtlZeroMemory(ndisPattern, sizeof(NDIS_PM_PACKET_PATTERN)); ndisPattern->PatternSize = ETHER_IP_ICMP_HEADER_SIZE + sizeof(pingPattern); maskLen = (ndisPattern->PatternSize-1)/8 + 1; nextMask = (PCHAR)patternBuffer + sizeof(NDIS_PM_PACKET_PATTERN); nextPattern = nextMask + maskLen; *nextMask = 0x0;nextMask++; *nextMask = 0x0;nextMask++; *nextMask = 0x0;nextMask++; *nextMask = 0x0;nextMask++; *nextMask = 0x0;nextMask++; *nextMask = 0x3f;nextMask++; *nextMask = 0x0C; ndisPattern->MaskSize = maskLen; ndisPattern->PatternOffset = (ULONG) ((ULONG_PTR) nextPattern - (ULONG_PTR) patternBuffer); bufLen = sizeof(NDIS_PM_PACKET_PATTERN) + maskLen + ndisPattern->PatternSize; RtlZeroMemory(nextPattern, ETHER_IP_ICMP_HEADER_SIZE); nextPattern += ETHER_IP_ICMP_HEADER_SIZE; RtlCopyMemory(nextPattern, pingPattern, sizeof(pingPattern)); if(AddPattern){ status = MPAddWakeUpPattern(FdoData, Buffer, bufLen, &unUsed, &unUsed); if(!NT_SUCCESS(status)){ TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "MpAddWakeupPattern failed %x\n", status); } }else{ status = MPRemoveWakeUpPattern(FdoData, Buffer, bufLen, &unUsed, &unUsed); if(!NT_SUCCESS(status)){ TraceEvents(TRACE_LEVEL_VERBOSE, DBG_POWER, "MPRemoveWakeUpPattern failed %x\n", status); } } return status; } #endif
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials