📄 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 **
****************************************************************************/
/* Converted to WDM - Eliyas Yakub - Feb 13, 2003 */
#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
// Software Pends request
// 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
// OS tells the SW that it is now in D0
// 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 {
USHORT UnInteresting:11;
USHORT PME_Support:5;
} MP_PM_CAP_REG;
//
// Define the PM Control/Status Register
//
typedef struct _MP_PMCSR {
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;
} 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
{
// 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;
} 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 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(
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;
ULONG ulResult;
DebugPrint(TRACE, DBG_POWER, "-->MPSetPowerLowPrivate\n");
RtlZeroMemory (&Filter, sizeof (Filter));
do
{
//
// Before issue the command to low power state, we should disable the
// interrup and ack all the pending interrupts, then set the adapter's power to
// low state.
//
NICDisableInterrupt(FdoData);
NIC_ACK_INTERRUPT(FdoData, IntStatus);
//
// If the driver should wake up the machine
//
if (FdoData->AllowWakeArming)
{
//
// Send the WakeUp Patter 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));
DebugPrint(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);
}
//
// Set the driver to lower power state by OS
//
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -