I use NDIS_WDI_RX_INORDER_DATA_IND_HANDLER and MINIPORT_WDI_RX_GET_MPDUS to indicate NBL to WDI, the format is MacHeader(24B)+LLC(8B)+Payload.
When I connect to an open AP, my driver works normally, I can ping bing, so I think my datapath is ok right?
But when it turns to a personal-WPA2 AP, my driver doesn't work.
As I know, EAPOL frame is a data frame, so I indicate it as usual. After indicating msg1, NDIS should send msg2 down to driver, but it doesn't.
So I wanna know if EAPOL frame needs special handle in my driver? Or my driver can regard EAPOL frame as normal data frame so there must be something wrong in other places?
I have tried to add QoS-control behind MacHeader(SubType=QosData) but it still no use.
PeerId=Any, TID=Unknow. (I check the WDI source code and I found that WDI will get PeerId by addr2 and get TID by QoS-control, so I think they are not the key)
WinDbg logs:
DriverEntry: ==> Build on Jun 5 2025 at 15:40:08
DriverEntry: <==
EvtAllocateAdapter: ==>
Miniport Init Params:
IfIndex: 0x00000018
NetLuid:
Value: 0x0047008000000000
Info:
Index: 0x0000000000008000
IfType: IF_TYPE_IEEE80211
DefaultPortAuthStates:
SendControlState: NdisPortControlStateUncontrolled
RcvControlState: NdisPortControlStateUncontrolled
SendAuthorizationState: NdisPortAuthorized
RcvAuthorizationState: NdisPortAuthorized
CreateUsbDevice: 1 interface and 4 pipes detected
CreateUsbDevice: USB 2.0 (High Speed) device detected
InitThread: Entry
RunThread: Entry
NdisThreadCallback: RxHandle thread is started
NdisThreadCallback: TxHandle thread is started
RxHandleThreadCallback: Entry
TxHandleThreadCallback: Entry
EvtAllocateAdapter: <==
EvtOpenAdapter: ==>
EvtOpenAdapter: <==
EvtTalTxRxInitialize: ==>
EvtTalTxRxInitialize: <==
WdiGetAdapterCapabilities: ==>
WdiGetAdapterCapabilities: <==
WdiSetAdapterConfiguration: ==>
WdiSetAdapterConfiguration: <==
EvtTalTxRxStart: ==>
Wifi TxRx Configuration:
TxRxParams:
TxRxCapabilities:
InterconnectType: WDI_INTERCONNECT_MESSAGE_BASED
TransmitCapabilities:
TargetPriorityQueueing: TRUE
MaxScatterGatherElementsPerFrame: 255
ExplicitSendCompleteFlagRequired: TRUE
MinEffectiveSize: 0
FrameSizeGranularity: 1
ReceiveCapabilities:
RxTxForwarding: FALSE
MaxThroughput: 1200
MaxNumPorts: 5
MaxNumPeers: 255
EvtTalTxRxStart: <==
WdiTaskCreatePort: ==>
Task Create Port Params:
CreatePortParameters:
OpModeMask: STA_OP_MODE
NdisPortNumber: 0
MacAddress: (not present)
WdiTaskCreatePort: Create Port[0] successfully, MacAddress=22-23-24-d4-cf-d8
WdiTaskCreatePort: <==
EvtTalTxRxAddPort: TxRx Add Port[0], works on STA_OP_MODE
EvtTalTxRxAddPort: Add Peer[0] for Port[0] to broadcast frame
EvtTalTxRxResetPort: TxRx Reset on Port[0]
WdiTaskDot11Reset: ==>
Task Dot11 Reset Params:
SetDefaultMIB: TRUE
ResetMACAddress: (not present)
WdiTaskDot11Reset: <==
EvtDevicePnPEventNotify: ==>
NetDevicePnPEvent:
PortNumber: 0
DevicePnPEvent: NdisDevicePnPEventPowerProfileChanged
InformationBufferLength: 4
EvtDevicePnPEventNotify: <==
EvtRxRestart: Rx Restart on port[0]
EvtTxAbort: Tx Abort on Port[0]
EvtRxStop: Rx Stop on port[0]
EvtRxFlush: Rx Flush on port[0]
EvtTalTxRxResetPort: TxRx Reset on Port[0]
WdiTaskDot11Reset: ==>
Task Dot11 Reset Params:
SetDefaultMIB: TRUE
ResetMACAddress: (not present)
WdiTaskDot11Reset: <==
WdiGetStatistics: ==>
WdiGetStatistics: <==
WdiSetReceivePacketFilter: ==>
Set Receive Packet Filter Params:
Filter WDI_PACKET_FILTER_DIRECTED
WdiSetReceivePacketFilter: <==
WdiSetMulticastList: ==>
WdiSetMulticastList: <==
EvtRxRestart: Rx Restart on port[0]
WdiSetMulticastList: ==>
WdiSetMulticastList: <==
WdiTaskScan: ==>
WdiTaskScan: <==
WdiTaskScan: ==>
WdiTaskScan: <==
EvtTxAbort: Tx Abort on Port[0]
EvtRxStop: Rx Stop on port[0]
EvtRxFlush: Rx Flush on port[0]
EvtTalTxRxResetPort: TxRx Reset on Port[0]
EvtRxRestart: Rx Restart on port[0]
WdiTaskDot11Reset: ==>
Task Dot11 Reset Params:
SetDefaultMIB: TRUE
ResetMACAddress: (not present)
WdiTaskDot11Reset: <==
WdiGetStatistics: ==>
WdiGetStatistics: <==
WdiSetPrivacyExemptionList: ==>
Set Privacy ExemptionList Params:
PrivacyExemptionEntry[0]:
EtherType: 0x8e88
ExemptionActionType: WDI_EXEMPT_ON_KEY_MAPPING_KEY_UNAVAILABLE
ExemptionPacketType: WDI_EXEMPT_PACKET_TYPE_UNICAST
WdiSetPrivacyExemptionList: <==
WdiTaskConnect: ==>
Connect Request Parameters:
ConnectionSettings:
RoamRequest: FALSE
HiddenNetwork: FALSE
ExcludeUnencrypted: TRUE
MFPEnabled: TRUE
HostFIPSModeEnabled: FALSE
RoamNeededReason: 0
RoamTrigger: 0
BSSTransitionSupported: FALSE
SSIDList:
SSID[0]: Xiaomi_AX1500
AuthenticationAlgorithms:
Algorithms[0]: WDI_AUTH_ALGO_RSNA_PSK
MulticastCipherAlgorithms:
Algorithms[0]: WDI_CIPHER_ALGO_GCMP
Algorithms[1]: WDI_CIPHER_ALGO_CCMP
Algorithms[2]: WDI_CIPHER_ALGO_NONE
UnicastCipherAlgorithms:
Algorithms[0]: WDI_CIPHER_ALGO_CCMP
HESSIDInfo: (not present)
AssociationRequestVendorIE: (not present)
ActivePhyTypeList: (not present)
DisallowedBSSIDs: (not present)
AllowedBSSIDs:
BSSID[0]: ff-ff-ff-ff-ff-ff
OWEDHIE: (not present)
PreferredBSSEntryList[0]:
BSSID: 90-fb-5d-58-55-75
ProbeResponseFrame: 316 byte
BeaconFrame: 236 byte
SignalInfo:
RSSI: 10
LinkQuality: 100
ChannelInfo:
BandId: WDI_BAND_ID_2400
ChannelNumber: 13
DeviceSpecificContext: 0 byte
PMKID: 0 byte
AssociationRequestVendorIE: 0 byte
FTInitialAssocParameters:
MDE: 0 byte
FTReAssocParameters:
MDE: 0 byte
FTE: 0 byte
PMKR0Name: 0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0
BSSSelectionParameters: WDI_BSS_SELECTION_HOST_PREFERRED
UnsolicitedIndicateAssociationResult: Associates 90:fb:5d:58:55:75 with Peer[1]
EvtTalTxRxPeerConfig: TxRx Config Peer[1] to QosCapable for Port[0]
UnsolicitedIndicateLinkStateChanged: Peer 90:fb:5d:58:55:75 update TxLinkSpeed=600000, RxLinkSpeed=600000, LinkQuality=100
WdiTaskConnect: <==
WdiGetStatistics: ==>
WdiGetStatistics: <==
WdiSetReceivePacketFilter: ==>
Set Receive Packet Filter Params:
Filter WDI_PACKET_FILTER_DIRECTED
Filter WDI_PACKET_FILTER_MULTICAST
Filter WDI_PACKET_FILTER_BROADCAST
WdiSetReceivePacketFilter: <==
WdiGetStatistics: ==>
WdiGetStatistics: <==
WdiSetReceivePacketFilter: ==>
Set Receive Packet Filter Params:
Filter WDI_PACKET_FILTER_DIRECTED
Filter WDI_PACKET_FILTER_MULTICAST
Filter WDI_PACKET_FILTER_BROADCAST
WdiSetReceivePacketFilter: <==
WdiGetStatistics: ==>
WdiGetStatistics: <==
WdiGetStatistics: ==>
WdiGetStatistics: <==
RxHandleThreadCallback: Rx EAPoL Frame, Ethtype=0x8e88
RxHandleThreadCallback: Rx EAPoL Frame, Ethtype=0x8e88
RxHandleThreadCallback: Rx EAPoL Frame, Ethtype=0x8e88
WdiTaskDisconnect: ==>
Disconnect Request Parameters:
MacAddress: 90-fb-5d-58-55-75
Disassociation80211Reason: LEAVING_NETWORK_DEAUTH (Deauthenticated because sending STA is leaving (or has left) the BSS)
WdiTaskDisconnect: <==
EvtTxAbort: Tx Abort on Port[0]
EvtRxStop: Rx Stop on port[0]
EvtRxFlush: Rx Flush on port[0]
EvtTalTxRxResetPort: TxRx Reset on Port[0]
EvtRxRestart: Rx Restart on port[0]
WdiTaskDot11Reset: ==>
Task Dot11 Reset Params:
SetDefaultMIB: FALSE
ResetMACAddress: (not present)
WdiTaskDot11Reset: <==
WdiSetReceivePacketFilter: ==>
Set Receive Packet Filter Params:
Filter WDI_PACKET_FILTER_DIRECTED
WdiSetReceivePacketFilter: <==
WdiTaskScan: ==>
WdiTaskScan: <==
.
It is the raw data of msg1 I indicate to WDI (with QoS filed, I also have try without QoS, see the following code):
88 02 7A 01 22 23 24 35 DC DC 90 FB 5D 58 55 75
90 FB 5D 58 55 75 10 00 07 00 AA AA 03 00 00 00
88 8E 02 03 00 5F 02 00 8A 00 10 00 00 00 00 00
00 00 02 17 ED 4C 7E 4C 0B 5C 9D 20 10 72 5D AB
2F 2F A9 9B B6 BB A9 E2 DC E1 CF BB 7A 8E 67 E2
DC 1D E8 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00
Source Code:
VOID
RxHandleThreadCallback(
_In_ PTHREAD_INFO pThreadInfo
) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: Entry \n", __FUNCTION__));
NTSTATUS status;
PADAPTER pAdapter = pThreadInfo->pAdapter;
PMGMT_INFO pMgmtInfo = pAdapter->pMgmtInfo;
PTX_RX_MANAGER pTxRxManager = &pMgmtInfo->TxRxManager;
PRFD pRfd;
ETHERNET_HEADER EthHeader = { 0 };
DOT11_DATA_SHORT_HEADER Dot11Header = { 0 };
LLC_HEADER LLCHeader = { .DSAP = 0xAA, .SSAP = 0xAA, .Control = 0x03 };
UCHAR QosControl[2] = { 0 };
PNET_BUFFER_LIST pNetBufferList;
PMDL pMdl;
PWDI_FRAME_METADATA pWdiFrameMeta;
WDI_RX_INDICATION_LEVEL IndicationLevel = WDI_RX_INDICATION_PASSIVE;
WDI_PEER_ID PeerId = WDI_PEER_ANY;
WDI_EXTENDED_TID ExTid = WDI_EXT_TID_UNKNOWN;
BOOLEAN bRxFrameClassificationSupported = FALSE;// TODO
while (TRUE) {
status = KeWaitForSingleObject(&pTxRxManager->RfdSemaphore, Executive, KernelMode, TRUE, NULL);
if (!NT_SUCCESS(status)) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: %s thread waits for semaphore failed with status=%#x \n", __FUNCTION__, pThreadInfo->ThreadName, status));
break;
}
NdisAcquireSpinLock(&pTxRxManager->RfdListLock);
pRfd = (PRFD)RemoveHeadList(&pTxRxManager->RfdListHead);
NdisReleaseSpinLock(&pTxRxManager->RfdListLock);
struct wlan_sw_rx_data_hdr* rx_hdr = (struct wlan_sw_rx_data_hdr*)pRfd->Buffer;
struct hw_rxhdr* hw_rxhdr = (struct hw_rxhdr*)(pRfd->Buffer + sizeof(struct wlan_sw_rx_data_hdr));
PUCHAR ptr = pRfd->Buffer + sizeof(struct hw_rxhdr) + sizeof(struct wlan_sw_rx_data_hdr);
ASSERT(hw_rxhdr->hwvect.len == pRfd->BufferLength - sizeof(struct hw_rxhdr) - sizeof(struct wlan_sw_rx_data_hdr));
//
// RxEngine can choose how to handle incoming data while paused.
// If possible, it should just buffer the data.
// Dropping data is also acceptable.
//
NdisAcquireSpinLock(&pTxRxManager->RxDataPathStateLock);
if (pTxRxManager->RxDataPathState == RX_PAUSE) {
NdisReleaseSpinLock(&pTxRxManager->RxDataPathStateLock);
goto ReturnRfd;
}
NdisReleaseSpinLock(&pTxRxManager->RxDataPathStateLock);
//
// Since the firmware was originally designed for the Linux system
// ptr is point to a 802.3 header
//
NdisMoveMemory(&EthHeader, ptr, sizeof(ETHERNET_HEADER));
if (EthHeader.Protocol == 0x888e || EthHeader.Protocol == 0x8e88) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: Rx EAPoL Frame, Ethtype=%#x \n", __FUNCTION__, EthHeader.Protocol));
}
//
// Construct 802.11 Header
//
Dot11Header.FrameControl.Type = DOT11_FRAME_TYPE_DATA;
Dot11Header.FrameControl.Subtype = DOT11_DATA_SUBTYPE_DATA;
Dot11Header.FrameControl.FromDS = TRUE;
NdisMoveMemory(Dot11Header.Address1, EthHeader.DestMAC, 6);
NdisMoveMemory(Dot11Header.Address2, pMgmtInfo->AssociationResult.BSSID, 6);
NdisMoveMemory(Dot11Header.Address3, EthHeader.SrcMAC, 6);
LLCHeader.EtherType = EthHeader.Protocol;
NdisMoveMemory(pRfd->Header, &Dot11Header, MAC_HEADER_LENGTH);
NdisMoveMemory(pRfd->Header + MAC_HEADER_LENGTH, &LLCHeader, LLC_HEADER_LENGTH);
pRfd->HeaderLength = MAC_HEADER_LENGTH + LLC_HEADER_LENGTH;
Dot11Header.SequenceControl.SequenceNumber++;
pRfd->Payload = ptr + sizeof(ETHERNET_HEADER);
pRfd->PayloadLength = hw_rxhdr->hwvect.len - sizeof(ETHERNET_HEADER);
pMdl = NdisAllocateMdl(
pAdapter->NdisMiniportHandle,
pRfd->Header,
pRfd->HeaderLength
);
if (pMdl == NULL) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: NdisAllocateMdl for header failed \n", __FUNCTION__));
goto ReturnRfd;
}
pMdl->Next = NdisAllocateMdl(
pAdapter->NdisMiniportHandle,
pRfd->Payload,
pRfd->PayloadLength
);
if (pMdl->Next == NULL) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: NdisAllocateMdl for payload failed \n", __FUNCTION__));
NdisFreeMdl(pMdl);
goto ReturnRfd;
}
pNetBufferList = NdisAllocateNetBufferAndNetBufferList(
pAdapter->RxNetBufferListPool,
0,
0,
pMdl,
0, // DataOffset
pRfd->HeaderLength + pRfd->PayloadLength
);
if (pNetBufferList == NULL) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: NdisAllocateNetBufferAndNetBufferList failed \n", __FUNCTION__));
NdisFreeMdl(pMdl->Next);
NdisFreeMdl(pMdl);
goto ReturnRfd;
}
pNetBufferList->SourceHandle = pAdapter->NdisMiniportHandle;
pRfd->pNetBufferList = pNetBufferList;
pWdiFrameMeta = pAdapter->WdiDataApi.AllocateWiFiFrameMetaData(pAdapter->NdisMiniportDataPathHandle);
if (pWdiFrameMeta == NULL) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: AllocateWiFiFrameMetaData failed \n", __FUNCTION__));
NdisFreeNetBufferList(pNetBufferList);
NdisFreeMdl(pMdl->Next);
NdisFreeMdl(pMdl);
goto ReturnRfd;
}
pWdiFrameMeta->u.rxMetaData.PayloadType = WDI_FRAME_MSDU;
pWdiFrameMeta->pNBL = pNetBufferList;
NET_BUFFER_LIST_MINIPORT_RESERVED(pNetBufferList)[0] = pWdiFrameMeta;
NET_BUFFER_LIST_MINIPORT_RESERVED(pNetBufferList)[1] = pRfd;
NdisAcquireSpinLock(&pTxRxManager->ToIndRfdListLock);
InsertTailList(&pTxRxManager->ToIndRfdListHead, &pRfd->ListEntry);
NdisReleaseSpinLock(&pTxRxManager->ToIndRfdListLock);
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
IndicationLevel = WDI_RX_INDICATION_PASSIVE;
}
else if (KeGetCurrentIrql() == DISPATCH_LEVEL) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: TODO: Irql=DISPATCH_LEVEL \n", __FUNCTION__));
}
else {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: TODO: Irql=%d \n", __FUNCTION__, KeGetCurrentIrql()));
}
if (!bRxFrameClassificationSupported) {
PeerId = WDI_PEER_ANY;
ExTid = WDI_EXT_TID_UNKNOWN;
}
pAdapter->WdiDataApi.RxInorderDataIndication(
pAdapter->NdisMiniportDataPathHandle,
IndicationLevel,
PeerId,
ExTid,
NULL,
&status
);
if (status == NDIS_STATUS_PAUSED) {
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%s: Rx Manager pause frame and wait for it to be ready \n", __FUNCTION__));
NdisAcquireSpinLock(&pTxRxManager->RxDataPathStateLock);
pAdapter->pMgmtInfo->TxRxManager.RxDataPathState = RX_PAUSE;
NdisReleaseSpinLock(&pTxRxManager->RxDataPathStateLock);
}
continue;
ReturnRfd:
pRfd->BufferLength = 0;
NdisAcquireSpinLock(&pTxRxManager->IdleRfdListLock);
InsertTailList(&pTxRxManager->IdleRfdListHead, &pRfd->ListEntry);
NdisReleaseSpinLock(&pTxRxManager->IdleRfdListLock);
}
}
_Use_decl_annotations_
VOID
EvtRxGetMpdus(
_In_ TAL_TXRX_HANDLE MiniportTalTxRxContext,
_In_ WDI_PEER_ID PeerId,
_In_ WDI_EXTENDED_TID ExTid,
_Out_ PNET_BUFFER_LIST* ppNbl
) {
PADAPTER pAdapter = MiniportTalTxRxContext;
PTX_RX_MANAGER pTxRxManager = &pAdapter->pMgmtInfo->TxRxManager;
NdisAcquireSpinLock(&pTxRxManager->ToIndRfdListLock);
PRFD pRfd = (PRFD)RemoveHeadList(&pTxRxManager->ToIndRfdListHead);
NdisReleaseSpinLock(&pTxRxManager->ToIndRfdListLock);
*ppNbl = pRfd->pNetBufferList;
}