You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1090 lines
26 KiB
1090 lines
26 KiB
|
|
|
|
|
|
#include <ndis.h>
|
|
#include <e100_equ.h>
|
|
#include <e100_557.h>
|
|
#include <e100_def.h>
|
|
#include <mp_def.h>
|
|
#include <mp_cmn.h>
|
|
#include <mp.h>
|
|
#include <mp_nic.h>
|
|
#include <mp_dbg.h>
|
|
#include <e100_sup.h>
|
|
// 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
|
|
NDIS_STATUS
|
|
MPIssueScbPoMgmtCommand(
|
|
IN PMP_ADAPTER Adapter,
|
|
IN PCSR_FILTER_STRUC pFilter,
|
|
IN BOOLEAN WaitForScb
|
|
);
|
|
|
|
|
|
VOID
|
|
MPCreateProgrammableFilter (
|
|
IN PMP_WAKE_PATTERN pMpWakePattern ,
|
|
IN PUCHAR pFilter,
|
|
IN OUT PULONG pNext
|
|
);
|
|
|
|
|
|
//
|
|
// Macros used to walk a doubly linked list. Only macros that are not defined in ndis.h
|
|
// The List Next macro will work on Single and Doubly linked list as Flink is a common
|
|
// field name in both
|
|
//
|
|
|
|
/*
|
|
PLIST_ENTRY
|
|
ListNext (
|
|
IN PLIST_ENTRY
|
|
);
|
|
|
|
PSINGLE_LIST_ENTRY
|
|
ListNext (
|
|
IN PSINGLE_LIST_ENTRY
|
|
);
|
|
*/
|
|
#define ListNext(_pL) (_pL)->Flink
|
|
|
|
/*
|
|
PLIST_ENTRY
|
|
ListPrev (
|
|
IN LIST_ENTRY *
|
|
);
|
|
*/
|
|
#define ListPrev(_pL) (_pL)->Blink
|
|
|
|
//-------------------------------------------------------------------------
|
|
// P O W E R M G M T F U N C T I O N S
|
|
//-------------------------------------------------------------------------
|
|
|
|
PUCHAR
|
|
HwReadPowerPMDR(
|
|
IN PMP_ADAPTER Adapter
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
This routine will Hardware's PM registers
|
|
|
|
Arguments:
|
|
|
|
Adapter Pointer to our adapter
|
|
|
|
Return Value:
|
|
|
|
NDIS_STATUS_SUCCESS
|
|
NDIS_STATUS_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
NDIS_STATUS
|
|
MPWritePciSlotInfo(
|
|
PMP_ADAPTER pAdapter,
|
|
ULONG Offset,
|
|
PVOID pValue,
|
|
ULONG SizeofValue
|
|
)
|
|
{
|
|
ULONG ulResult;
|
|
NDIS_STATUS Status;
|
|
|
|
ulResult = NdisWritePciSlotInformation(
|
|
pAdapter->AdapterHandle,
|
|
0,
|
|
Offset,
|
|
pValue,
|
|
SizeofValue);
|
|
|
|
ASSERT (ulResult == SizeofValue);
|
|
|
|
// What do we do in case of failure;
|
|
//
|
|
if (ulResult == SizeofValue)
|
|
{
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Status = NDIS_STATUS_FAILURE;
|
|
}
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
NDIS_STATUS
|
|
MPReadPciSlotInfo(
|
|
PMP_ADAPTER pAdapter,
|
|
ULONG Offset,
|
|
PVOID pValue,
|
|
ULONG SizeofValue
|
|
)
|
|
{
|
|
ULONG ulResult;
|
|
NDIS_STATUS Status;
|
|
|
|
ulResult = NdisReadPciSlotInformation(
|
|
pAdapter->AdapterHandle,
|
|
0,
|
|
Offset,
|
|
pValue,
|
|
SizeofValue);
|
|
|
|
ASSERT (ulResult == SizeofValue);
|
|
|
|
// What do we do in case of failure;
|
|
//
|
|
if (ulResult == SizeofValue)
|
|
{
|
|
Status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Status = NDIS_STATUS_FAILURE;
|
|
}
|
|
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
NDIS_STATUS
|
|
MpClearPME_En (
|
|
IN PMP_ADAPTER pAdapter,
|
|
IN MP_PMCSR PMCSR
|
|
)
|
|
{
|
|
NDIS_STATUS Status;
|
|
UINT ulResult;
|
|
|
|
PMCSR.PME_En = 0;
|
|
|
|
Status = MPWritePciSlotInfo( pAdapter,
|
|
FIELD_OFFSET(MP_PM_PCI_SPACE, PMCSR),
|
|
(PVOID)&PMCSR,
|
|
sizeof(PMCSR));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
VOID MpExtractPMInfoFromPciSpace(
|
|
PMP_ADAPTER pAdapter,
|
|
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;
|
|
PMP_POWER_MGMT pPoMgmt = &pAdapter->PoMgmt;
|
|
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)
|
|
{
|
|
pAdapter->PoMgmt.bWakeFromD0 = TRUE;
|
|
}
|
|
|
|
if(PmCaps.PME_Support & E100_PMC_WAKE_FROM_D1)
|
|
{
|
|
pAdapter->PoMgmt.bWakeFromD1 = TRUE;
|
|
}
|
|
|
|
if(PmCaps.PME_Support & E100_PMC_WAKE_FROM_D2)
|
|
{
|
|
pAdapter->PoMgmt.bWakeFromD2 = TRUE;
|
|
}
|
|
|
|
if(PmCaps.PME_Support & E100_PMC_WAKE_FROM_D3HOT)
|
|
{
|
|
pAdapter->PoMgmt.bWakeFromD3Hot = TRUE;
|
|
}
|
|
|
|
if(PmCaps.PME_Support & E100_PMC_WAKE_FROM_D3_AUX)
|
|
{
|
|
pAdapter->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 (pAdapter,PMCSR);
|
|
|
|
}
|
|
|
|
|
|
//pPoMgmt->PowerState = PMCSR.PowerState;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
MPSetPowerLowPrivate(
|
|
PMP_ADAPTER pAdapter
|
|
)
|
|
/*++
|
|
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;
|
|
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
|
|
USHORT IntStatus;
|
|
MP_PMCSR PMCSR;
|
|
|
|
NdisZeroMemory (&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(pAdapter);
|
|
NIC_ACK_INTERRUPT(pAdapter, IntStatus);
|
|
pAdapter->CurrentPowerState = pAdapter->NextPowerState;
|
|
|
|
//
|
|
// If the driver should wake up the machine
|
|
//
|
|
if (pAdapter->WakeUpEnable != 0)
|
|
{
|
|
//
|
|
// Send the WakeUp Patter to the nic
|
|
MPIssueScbPoMgmtCommand(pAdapter, &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 ((pAdapter->CSRAddress->ScbStatus & SCB_CUS_MASK) == SCB_CUS_IDLE);
|
|
}
|
|
else
|
|
{
|
|
|
|
MPReadPciSlotInfo(pAdapter,
|
|
FIELD_OFFSET(MP_PM_PCI_SPACE, PMCSR),
|
|
(PVOID)&PMCSR,
|
|
sizeof(PMCSR));
|
|
if (PMCSR.PME_En == 1)
|
|
{
|
|
//
|
|
// PME is enabled. Clear the PME_En bit.
|
|
// So that it is not asserted
|
|
//
|
|
MpClearPME_En (pAdapter,PMCSR);
|
|
|
|
}
|
|
|
|
//
|
|
// Set the driver to lower power state by OS
|
|
//
|
|
}
|
|
|
|
|
|
|
|
} while (FALSE);
|
|
|
|
|
|
}
|
|
|
|
NDIS_STATUS
|
|
MPSetPowerD0Private (
|
|
IN MP_ADAPTER* pAdapter
|
|
)
|
|
{
|
|
PUCHAR pPMDR;
|
|
NDIS_STATUS Status;
|
|
|
|
do
|
|
{
|
|
// Dump the packet if necessary
|
|
//Cause of Wake Up
|
|
|
|
pPMDR = HwReadPowerPMDR(pAdapter);
|
|
|
|
|
|
NICInitializeAdapter(pAdapter);
|
|
|
|
|
|
// Clear the PMDR
|
|
MP_CLEAR_PMDR(pPMDR);
|
|
|
|
NICIssueSelectiveReset(pAdapter);
|
|
|
|
} while (FALSE);
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
MPSetPowerWorkItem(
|
|
IN PNDIS_WORK_ITEM pWorkItem,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
|
|
//
|
|
// Call the appropriate function
|
|
//
|
|
|
|
|
|
|
|
|
|
//
|
|
// Complete the original request
|
|
//
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
HwSetWakeUpConfigure(
|
|
IN PMP_ADAPTER pAdapter,
|
|
PUCHAR pPoMgmtConfigType,
|
|
UINT WakeUpParameter
|
|
)
|
|
{
|
|
|
|
|
|
if (MPIsPoMgmtSupported( pAdapter) == TRUE)
|
|
{
|
|
(*pPoMgmtConfigType)= ((*pPoMgmtConfigType)| CB_WAKE_ON_LINK_BYTE9 |CB_WAKE_ON_ARP_PKT_BYTE9 );
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
NDIS_STATUS
|
|
MPSetUpFilterCB(
|
|
IN PMP_ADAPTER pAdapter
|
|
)
|
|
{
|
|
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
|
|
PCB_HEADER_STRUC NonTxCmdBlockHdr = (PCB_HEADER_STRUC)pAdapter->NonTxCmdBlock;
|
|
PFILTER_CB_STRUC pFilterCb = (PFILTER_CB_STRUC)NonTxCmdBlockHdr;
|
|
ULONG Curr = 0;
|
|
ULONG Next = 0;
|
|
PLIST_ENTRY pPatternEntry = ListNext(&pAdapter->PoMgmt.PatternList) ;
|
|
|
|
DBGPRINT(MP_TRACE, ("--> HwSetupIAAddress\n"));
|
|
|
|
NdisZeroMemory (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 != (&pAdapter->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(pAdapter->CSRAddress->ScbCommandLow == 0)
|
|
|
|
// Wait for the CU to Idle before giving it this command
|
|
if(!WaitScb(pAdapter))
|
|
{
|
|
Status = NDIS_STATUS_HARD_ERRORS;
|
|
}
|
|
|
|
|
|
return Status;
|
|
|
|
|
|
}
|
|
|
|
NDIS_STATUS
|
|
MPIssueScbPoMgmtCommand(
|
|
IN PMP_ADAPTER pAdapter,
|
|
IN PCSR_FILTER_STRUC pNewFilter,
|
|
IN BOOLEAN WaitForScb
|
|
)
|
|
{
|
|
NDIS_STATUS Status = NDIS_STATUS_FAILURE;
|
|
|
|
do
|
|
{
|
|
// Set up SCB to issue this command
|
|
|
|
Status = MPSetUpFilterCB(pAdapter);
|
|
|
|
if (Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Submit the configure command to the chip, and wait for it to complete.
|
|
|
|
pAdapter->CSRAddress->ScbGeneralPointer = pAdapter->NonTxCmdBlockPhys;
|
|
|
|
Status = D100SubmitCommandBlockAndWait(pAdapter);
|
|
|
|
if(Status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
Status = NDIS_STATUS_NOT_ACCEPTED;
|
|
break;
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NDIS_STATUS
|
|
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;
|
|
BOOLEAN fIgnoreCurrentByte = FALSE;
|
|
ULONG ShiftBy = 0;
|
|
UCHAR FrameByte = 0;
|
|
NDIS_STATUS Status = NDIS_STATUS_FAILURE;
|
|
|
|
*pSignature = 0;
|
|
|
|
do
|
|
{
|
|
if (FrameLength > 128)
|
|
{
|
|
Status = NDIS_STATUS_FAILURE;
|
|
break;
|
|
}
|
|
|
|
// The E100 driver can only accept 3 DWORDS of Mask in a single pattern
|
|
if (MaskLength > (3*sizeof(ULONG)))
|
|
{
|
|
Status = NDIS_STATUS_FAILURE;
|
|
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
|
|
fIgnoreCurrentByte = ! (MaskBit & pCurrentMaskByte[0]);
|
|
|
|
if (fIgnoreCurrentByte)
|
|
{
|
|
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 = NDIS_STATUS_SUCCESS;
|
|
|
|
} while (FALSE);
|
|
|
|
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;
|
|
|
|
// 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 <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);
|
|
|
|
NdisMoveMemory (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;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|