WiFiCx 클라이언트 드라이버 작성

디바이스 및 어댑터 초기화

NetAdapterCx에서 NetAdapter 디바이스 초기화에 필요한 작업 외에도 WiFiCx 클라이언트 드라이버는 EvtDriverDeviceAdd 콜백 함수에서 다음 작업을 수행해야 합니다.

  1. NetDeviceInitConfig를 호출한 후 WdfDeviceCreate를 호출하기 전에 프레임워크에서 전달된 동일한 WDFDEVICE_INIT 개체를 참조하여 WifiDeviceInitConfig를 호출합니다.

  2. 초기화된 WIFI_DEVICE_CONFIG 구조체와 WdfDeviceCreate에서 가져온 WDFDEVICE 개체를 사용하여 WifiDeviceInitialize를 호출하여 WiFiCx 디바이스별 콜백 함수를 등록합니다.

다음 예제에서는 WiFiCx 디바이스를 초기화하는 방법을 보여 줍니다. 명확성을 위해 오류 처리가 제외되었습니다.

status = NetDeviceInitConfig(deviceInit);
status = WifiDeviceInitConfig(deviceInit);

// Set up other callbacks such as Pnp and Power policy

status = WdfDeviceCreate(&deviceInit, &deviceAttributes, &wdfDevice);
WIFI_DEVICE_CONFIG wifiDeviceConfig;
WIFI_DEVICE_CONFIG_INIT(&wifiDeviceConfig,
                        WDI_VERSION_LATEST,
                        EvtWifiDeviceSendCommand,
                        EvtWifiDeviceCreateAdapter,
                        EvtWifiDeviceCreateWifiDirectDevice); 

status = WifiDeviceInitialize(wdfDevice, &wifiDeviceConfig);
...
// Get the TLV version that WiFiCx uses to initialize the client's TLV parser/generator
auto peerVersion = WifiDeviceGetOsWdiVersion(wdfDevice);

이 메시지 흐름 다이어그램은 초기화 프로세스를 보여줍니다.

WiFiCx 클라이언트 드라이버 초기화 프로세스를 보여 주는 다이어그램

기본 어댑터(스테이션) 만들기 흐름

다음으로, 클라이언트 드라이버는 일반적으로 다음에 오는 EvtDevicePrepareHardware 콜백 함수에서 모든 Wi-Fi 특정 디바이스 기능을 설정해야 합니다. 펌웨어 기능을 쿼리하기 위해 하드웨어에 인터럽트를 사용하도록 설정해야 하는 경우 EvtWdfDeviceD0EntryPostInterruptsEnabled에서 수행할 수 있습니다.

WiFiCx는 더 이상 WDI_TASK_OPEN/WDI_TASK_CLOSE 호출하여 클라이언트가 펌웨어를 로드/언로드하도록 지시하지 않으며 WDI_GET_ADAPTER_CAPABILITIES 명령을 통해 Wi-Fi 기능을 쿼리하지 않습니다.

다른 유형의 NetAdapterCx 드라이버와 달리 WiFiCx 드라이버는 EvtDriverDeviceAdd 콜백 함수 내에서 NETADAPTER 개체를 만들면 안됩니다. 대신 WiFiCx는 나중에 EvtWifiDeviceCreateAdapter 콜백을 사용하여 기본 NetAdapter(스테이션)를 만들도록 드라이버에 지시합니다(클라이언트의 EvtDevicePrepareHardware 콜백이 성공한 후). 또한 WiFiCx/WDI는 더 이상 WDI_TASK_CREATE_PORT 명령을 호출하지 않습니다.

EvtWifiDeviceCreateAdapter 콜백 함수에서 클라이언트 드라이버는 다음을 수행해야 합니다.

  1. NetAdapterCreate를 호출하여 새 NetAdapter 개체를 만듭니다.

  2. WifiAdapterInitialize를 호출하여 WiFiCx 컨텍스트를 초기화하고 이 NetAdapter 개체와 연결합니다.

  3. NetAdapterStart를 호출하여 어댑터를 시작합니다.

이 경우 WiFiCx는 디바이스/어댑터(예: SET_ADAPTER_CONFIGURATION, TASK_SET_RADIO_STATE 등)에 대한 초기화 명령을 보냅니다.

EvtWifiDeviceCreateAdapter의 코드 예제는 어댑터 만들기에 대한 이벤트 콜백을 참조하세요.

WiFiCx 클라이언트 드라이버 스테이션 어댑터 생성을 보여 주는 순서도입니다.

WiFiCx 명령 메시지 처리

WiFiCx 명령 메시지는 대부분의 제어 경로 작업에 대한 이전 WDI 모델 명령을 기반으로 합니다. 이러한 명령은 WiFiCx 작업 OID, WiFiCx 속성 OIDWiFiCx 상태 표시에 정의되어 있습니다. 자세한 내용은 WiFiCx 메시지 구조를 참조하세요.

명령은 클라이언트 드라이버 및 WiFiCx에서 제공하는 API에서 제공하는 콜백 함수 집합을 통해 교환됩니다.

  • WiFiCx는 EvtWifiDeviceSendCommand 콜백 함수를 호출하여 클라이언트 드라이버에 명령 메시지를 보냅니다.

  • 메시지를 검색하기 위해 클라이언트 드라이버는 WifiRequestGetInOutBuffer 를 호출하여 입력/출력 버퍼 및 버퍼 길이를 가져옵니다. 또한 드라이버는 WifiRequestGetMessageId 를 호출하여 메시지 ID를 검색해야 합니다.

  • 요청을 완료하기 위해 드라이버는 WifiRequestComplete를 호출하여 명령에 대한 M3을 비동기적으로 보냅니다.

  • 명령이 set 명령이고 원래 요청에 충분한 버퍼가 포함되지 않은 경우 클라이언트는 WifiRequestSetBytesNeeded를 호출하여 필요한 버퍼 크기를 설정한 다음 상태 BUFFER_OVERFLOW 요청을 실패해야 합니다.

  • 명령이 작업 명령인 경우 클라이언트 드라이버는 나중에 WifiDeviceReceiveIndication 을 호출하여 연결된 M4 표시를 보내고 M1에 포함된 것과 동일한 메시지 ID를 포함하는 WDI 헤더를 사용하여 표시 버퍼를 전달해야 합니다.

  • 원치 않는 표시는 WifiDeviceReceiveIndication을 통해 알림을 받지만 WDI_MESSAGE_HEADER TransactionId 멤버를 0으로 설정합니다.

WiFiCx 드라이버 명령 메시지 처리를 보여 주는 순서도입니다.

P2P(Wi-Fi Direct) 지원

다음 섹션에서는 WiFiCx 드라이버가 Wi-Fi Direct를 지원하는 방법을 설명합니다.

Wi-Fi 직접 디바이스 기능

WIFI_WIFIDIRECT_CAPABILITIES WDI_P2P_CAPABILITIES 및 WDI_AP_CAPABILITIES LLV를 통해 이전에 WDI에서 설정한 모든 관련 기능을 나타냅니다. 클라이언트 드라이버는 WifiDeviceSetWiFiDirectCapabilities 를 호출하여 디바이스 기능 설정 단계에서 Wi-Fi 직접 기능을 WiFiCx에 보고합니다.

WIFI_WIFIDIRECT_CAPABILITIES wfdCapabilities = {};

// Set values
wfdCapabilities.ConcurrentGOCount = 1;
wfdCapabilities.ConcurrentClientCount = 1;

// Report capabilities to WiFiCx
WifiDeviceSetWiFiDirectCapabilities(Device, &wfdCapabilities);

"WfdDevice"에 대한 직접 이벤트 콜백 Wi-Fi

Wi-Fi Direct의 경우 "WfdDevice"는 데이터 경로 기능이 없는 컨트롤 개체입니다. 따라서 WiFiCx에는 WIFIDIRECTDEVICE라는 새 WDFObject가 있습니다. EvtWifiDeviceCreateWifiDirectDevice 콜백 함수에서 클라이언트 드라이버는 다음과 같습니다.

이 예제에서는 WIFIDIRECTDEVICE 개체를 만들고 초기화하는 방법을 보여줍니다.

NTSTATUS
EvtWifiDeviceCreateWifiDirectDevice(
    WDFDEVICE  Device,
    WIFIDIRECT_DEVICE_INIT * WfdDeviceInit
)
{
    WDF_OBJECT_ATTRIBUTES wfdDeviceAttributes;
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&wfdDeviceAttributes, WIFI_WFDDEVICE_CONTEXT);
    wfdDeviceAttributes.EvtCleanupCallback = EvtWifiDirectDeviceContextCleanup;

    WIFIDIRECTDEVICE wfdDevice;
    NTSTATUS ntStatus = WifiDirectDeviceCreate(WfdDeviceInit, &wfdDeviceAttributes, &wfdDevice);
    if (!NT_SUCCESS(ntStatus))
    {
        TraceEvents(TRACE_LEVEL_ERROR, DBG_DEVICE, "%!FUNC!: WifiDirectDeviceCreate failed, status=0x%x\n", ntStatus);
        return ntStatus;
    }

    ntStatus = WifiDirectDeviceInitialize(wfdDevice);

    if (!NT_SUCCESS(ntStatus))
    {
        TraceEvents(TRACE_LEVEL_ERROR, DBG_DEVICE, "%!FUNC!: WifiDirectDeviceInitialize failed with %!STATUS!\n", ntStatus);
        return ntStatus;
    }

    ntStatus = ClientDriverInitWifiDirectDeviceContext(
        Device,
        wfdDevice,
        WifiDirectDeviceGetPortId(wfdDevice));
    if (!NT_SUCCESS(ntStatus))
    {
        TraceEvents(TRACE_LEVEL_ERROR, DBG_DEVICE, "%!FUNC!: ClientDriverInitWifiDirectDeviceContext failed with %!STATUS!\n", ntStatus);
        return ntStatus;
    }

    return ntStatus;
}

어댑터 만들기를 위한 이벤트 콜백

클라이언트 드라이버는 동일한 이벤트 콜백인 EvtWifiDeviceCreateAdapter를 사용하여 스테이션 어댑터 및 WfdRole 어댑터를 만듭니다.

  • WifiAdapterGetType을 호출하여 어댑터 유형을 확인합니다.
  • 어댑터를 만들기 전에 드라이버가 NETADAPTER_INIT 개체에서 어댑터 형식을 쿼리해야 하는 경우 WifiAdapterInitGetType을 호출합니다.
  • WifiAdapterGetPortId 호출은 포트 ID(메시지 명령에 사용됨)를 결정합니다.
NTSTATUS
EvtWifiDeviceCreateAdapter(
    WDFDEVICE Device,
    NETADAPTER_INIT* AdapterInit
)
{
    NET_ADAPTER_DATAPATH_CALLBACKS datapathCallbacks;
    NET_ADAPTER_DATAPATH_CALLBACKS_INIT(&datapathCallbacks,
        EvtAdapterCreateTxQueue,
        EvtAdapterCreateRxQueue);

    NetAdapterInitSetDatapathCallbacks(AdapterInit, &datapathCallbacks);

    WDF_OBJECT_ATTRIBUTES adapterAttributes;
    WDF_OBJECT_ATTRIBUTES_INIT(&adapterAttributes);
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&adapterAttributes, WIFI_NETADAPTER_CONTEXT);
    adapterAttributes.EvtCleanupCallback = EvtAdapterContextCleanup;

    NETADAPTER netAdapter;
    NTSTATUS ntStatus = NetAdapterCreate(AdapterInit, &adapterAttributes, &netAdapter);
    if (!NT_SUCCESS(ntStatus))
    {
        TraceEvents(TRACE_LEVEL_ERROR, DBG_DEVICE, "%!FUNC!: NetAdapterCreate failed, status=0x%x\n", ntStatus);
        return ntStatus;
    }

    ntStatus = WifiAdapterInitialize(netAdapter);

    if (!NT_SUCCESS(ntStatus))
    {
        TraceEvents(TRACE_LEVEL_ERROR, DBG_DEVICE, "%!FUNC!: WifiAdapterInitialize failed with %!STATUS!\n", ntStatus);
        return ntStatus;
    }

    ntStatus = ClientDriverInitDataAdapterContext(
        Device,
        netAdapter,
        WifiAdapterGetType(netAdapter) == WIFI_ADAPTER_EXTENSIBLE_STATION ? EXTSTA_PORT : EXT_P2P_ROLE_PORT,
        WifiAdapterGetPortId(netAdapter));

    if (!NT_SUCCESS(ntStatus))
    {
        TraceEvents(TRACE_LEVEL_ERROR, DBG_DEVICE, "%!FUNC!: ClientDriverInitDataAdapterContext failed with %!STATUS!\n", ntStatus);
        return ntStatus;
    }

    ntStatus = ClientDriverNetAdapterStart(netAdapter);
    if (!NT_SUCCESS(ntStatus))
    {
        TraceEvents(TRACE_LEVEL_ERROR, DBG_DEVICE, "%!FUNC!: ClientDriverNetAdapterStart failed with %!STATUS!\n", ntStatus);
        return ntStatus;
    }

    return ntStatus;
}

Tx 큐에서 Wi-Fi ExemptionAction 지원

ExemptionAction은 클라이언트에서 수행하는 암호 작업에서 패킷이 제외될 것으로 예상되는지 여부를 나타내는 새로운 NetAdapter 패킷 확장입니다. 자세한 내용은 usExemptionActionType 에 대한 설명서를 참조하세요.

#include <net/wifi/exemptionaction.h>

typedef struct _WIFI_TXQUEUE_CONTEXT
{
    WIFI_NETADAPTER_CONTEXT* NetAdapterContext;
    LONG NotificationEnabled;
    NET_RING_COLLECTION const* Rings;
    NET_EXTENSION VaExtension;
    NET_EXTENSION LaExtension;
    NET_EXTENSION ExemptionActionExtension;
    CLIENTDRIVER_TCB* PacketContext;
} WIFI_TXQUEUE_CONTEXT, * PWIFI_TXQUEUE_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(WIFI_TXQUEUE_CONTEXT, WifiGetTxQueueContext);

NTSTATUS
EvtAdapterCreateTxQueue(
    _In_ NETADAPTER NetAdapter,
    _Inout_ NETTXQUEUE_INIT* TxQueueInit
)
{
    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "-->%!FUNC!\n");

    NTSTATUS status = STATUS_SUCCESS;
    PWIFI_TXQUEUE_CONTEXT txQueueContext = NULL;
    PWIFI_NETADAPTER_CONTEXT netAdapterContext = WifiGetNetAdapterContext(NetAdapter);
    WDF_OBJECT_ATTRIBUTES txAttributes;

    WDF_OBJECT_ATTRIBUTES_INIT(&txAttributes);
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&txAttributes, WIFI_TXQUEUE_CONTEXT);

    txAttributes.EvtDestroyCallback = EvtTxQueueDestroy;

    NET_PACKET_QUEUE_CONFIG queueConfig;
    NET_PACKET_QUEUE_CONFIG_INIT(&queueConfig,
        EvtTxQueueAdvance,
        EvtTxQueueSetNotificationEnabled,
        EvtTxQueueCancel);
    queueConfig.EvtStart = EvtTxQueueStart;
    NETPACKETQUEUE txQueue;
    status =
        NetTxQueueCreate(TxQueueInit,
            &txAttributes,
            &queueConfig,
            &txQueue);

    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "NetTxQueueCreate failed, Adapter=0x%p status=0x%x\n", NetAdapter, status);
        goto Exit;
    }

    txQueueContext = WifiGetTxQueueContext(txQueue);

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT, "NetTxQueueCreate succeeded, Adapter=0x%p, TxQueue=0x%p\n", NetAdapter, txQueue);

    txQueueContext->NetAdapterContext = netAdapterContext;
    txQueueContext->Rings = NetTxQueueGetRingCollection(txQueue);
    netAdapterContext->TxQueue = txQueue;

    NET_EXTENSION_QUERY extensionQuery;
    NET_EXTENSION_QUERY_INIT(
        &extensionQuery,
        NET_FRAGMENT_EXTENSION_VIRTUAL_ADDRESS_NAME,
        NET_FRAGMENT_EXTENSION_VIRTUAL_ADDRESS_VERSION_1,
        NetExtensionTypeFragment);

    NetTxQueueGetExtension(
        txQueue,
        &extensionQuery,
        &txQueueContext->VaExtension);

    if (!txQueueContext->VaExtension.Enabled)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            DBG_INIT,
            "%!FUNC!: Required virtual address extension is missing.");

        status = STATUS_UNSUCCESSFUL;
        goto Exit;
    }

    NET_EXTENSION_QUERY_INIT(
        &extensionQuery,
        NET_FRAGMENT_EXTENSION_LOGICAL_ADDRESS_NAME,
        NET_FRAGMENT_EXTENSION_LOGICAL_ADDRESS_VERSION_1,
        NetExtensionTypeFragment);

    NetTxQueueGetExtension(
        txQueue,
        &extensionQuery,
        &txQueueContext->LaExtension);

    if (!txQueueContext->LaExtension.Enabled)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            DBG_INIT,
            "%!FUNC!: Required logical address extension is missing.");

        status = STATUS_UNSUCCESSFUL;
        goto Exit;
    }

     NET_EXTENSION_QUERY_INIT(
        &extensionQuery,
        NET_PACKET_EXTENSION_WIFI_EXEMPTION_ACTION_NAME,
        NET_PACKET_EXTENSION_WIFI_EXEMPTION_ACTION_VERSION_1,
        NetExtensionTypePacket);

    NetTxQueueGetExtension(
        txQueue,
        &extensionQuery,
        &txQueueContext->ExemptionActionExtension);

    if (!txQueueContext->ExemptionActionExtension.Enabled)
    {
        TraceEvents(
            TRACE_LEVEL_ERROR,
            DBG_INIT,
            "%!FUNC!: Required Exemption Action extension is missing.");

        status = STATUS_UNSUCCESSFUL;
        goto Exit;
    }

    status = InitializeTCBs(txQueue, txQueueContext);

    if (status != STATUS_SUCCESS)
    {
        goto Exit;
    }

Exit:
    TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "<--%!FUNC! with 0x%x\n", status);

    return status;
}

static
void
BuildTcbForPacket(
    _In_ WIFI_TXQUEUE_CONTEXT const * TxQueueContext,
    _Inout_ CLIENTDRIVER_TCB * Tcb,
    _In_ UINT32 PacketIndex,
    _In_ NET_RING_COLLECTION const * Rings
)
{
    auto const pr = NetRingCollectionGetPacketRing(Rings);
    auto const fr = NetRingCollectionGetFragmentRing(Rings);

    auto const packet = NetRingGetPacketAtIndex(pr, PacketIndex);

    auto const & vaExtension = TxQueueContext->VaExtension;
    auto const & laExtension = TxQueueContext->LaExtension;
    auto const & exemptionActionExtension = TxQueueContext->ExemptionActionExtension;



    auto const packageExemptionAction = WifiExtensionGetExemptionAction(&exemptionActionExtension, PacketIndex);
    Tcb->EncInfo.ExemptionActionType = packageExemptionAction->ExemptionAction;

}

직접 INI/INF 파일 변경 Wi-Fi

vWifi 기능이 NetAdapter로 대체되었습니다. WDI 기반 드라이버에서 포팅하는 경우 INI/INF는 vWIFI 관련 정보를 제거해야 합니다.

Characteristics = 0x84
BusType         = 5
*IfType         = 71; IF_TYPE_IEEE80211
*MediaType      = 16; NdisMediumNative802_11
*PhysicalMediaType = 9; NdisPhysicalMediumNative802_11
NumberOfNetworkInterfaces   = 5; For WIFI DIRECT DEVICE AND ROLE ADAPTER

; TODO: Set this to 0 if your device is not a physical device.
*IfConnectorPresent     = 1     ; true

; In most cases, you can keep these at their default values.
*ConnectionType         = 1     ; NET_IF_CONNECTION_DEDICATED
*DirectionType          = 0     ; NET_IF_DIRECTION_SENDRECEIVE
*AccessType             = 2     ; NET_IF_ACCESS_BROADCAST
*HardwareLoopback       = 0     ; false

[ndi.NT.Wdf]
KmdfService = %ServiceName%, wdf

[wdf]
KmdfLibraryVersion      = $KMDFVERSION$

NetAdapter 데이터 경로 변경

여러 Tx 큐 설정

기본적으로 NetAdapterCx는 NetAdapter용 모든 패킷에 대해 하나의 Tx 큐를 만듭니다.

드라이버가 QOS에 대해 여러 Tx 큐를 지원해야 하거나 다른 피어에 대해 다른 큐를 설정해야 하는 경우 적절한 DEMUX 속성을 설정하여 이 작업을 수행할 수 있습니다. demux 속성이 추가되면 Tx 큐 수는 최대 피어 수와 최대 tid 수와 1 (브로드캐스트/멀티캐스트의 경우)의 곱입니다.

QOS에 대한 여러 큐

NETADAPTER_INIT * 개체를 사용하여 NETADAPTER를 만들기 전에 클라이언트 드라이버는 WMMINFO demux를 추가해야 합니다.

...
WIFI_ADAPTER_TX_DEMUX wmmInfoDemux;
WIFI_ADAPTER_TX_WMMINFO_DEMUX_INIT(&wmmInfoDemux);
WifiAdapterInitAddTxDemux(adapterInit, &wmmInfoDemux);

이로 인해 번역기는 NBL WlanTagHeader::WMMInfo 값에 따라 요청 시 최대 8Tx 큐를 만듭니다.

클라이언트 드라이버는 EvtPacketQueueStart에서 프레임워크가 이 큐에 사용할 우선 순위를 쿼리해야 합니다.

auto const priority = WifiTxQueueGetDemuxWmmInfo(queue);

EvtStartEvtStop 사이에 이 큐에 배치된 모든 패킷은 지정된 우선 순위를 갖습니다.

피어에 대한 여러 큐

NETADAPTER_INIT * 개체를 사용하여 NETADAPTER를 만들기 전에 클라이언트 드라이버는 PEER_ADDRESS demux를 추가해야 합니다.

...
WIFI_ADAPTER_TX_DEMUX peerInfoDemux;
WIFI_ADAPTER_TX_PEER_ADDRESS_DEMUX_INIT(&peerInfoDemux, maxNumOfPeers);
WifiAdapterInitAddTxDemux(adapterInit, &peerInfoDemux);

클라이언트 드라이버는 EvtPacketQueueStart에서 프레임워크가 이 큐에 사용할 피어 주소를 쿼리해야 합니다.

auto const peerAddress = WifiTxQueueGetDemuxPeerAddress(queue);

EvtStartEvtStop 사이에 이 큐에 배치된 모든 패킷은 이 피어에 대한 것입니다.

다음 API를 사용하여 드라이버가 추가한 피어 주소에 대해서만 큐가 열립니다.

WifiAdapterAddPeer: 피어가 지정된 주소와 연결되었음을 WiFiCx에 알릴 수 있습니다. WiFiCx는 큐를 피어 주소에 연결하여 피어 제거와 함께 이 주소를 사용합니다. 드라이버에서 추가할 수 있는 최대 피어 수는 Tx demultiplexing 정보를 추가할 때 제공된 범위 값을 초과하지 않습니다.

WifiAdapterRemovePeer: 피어의 연결이 끊어졌는지 WiFiCx에 알릴 수 있습니다. 이렇게 하면 프레임워크가 연결된 큐를 중지합니다.

피어 수명

전원 정책 변경

전원 관리의 경우 클라이언트 드라이버는 다른 유형의 NetAdapterCx 클라이언트 드라이버와 같은 NETPOWERSETTINGS 개체를 사용해야 합니다.

시스템이 S0(작동) 상태일 때 디바이스 유휴 상태를 지원하려면 드라이버가 WdfDeviceAssignS0IdleSettings를 호출하고 WDF_DEVICE_POWER_POLICY_IDLE_SETTINGSIdleTimeoutType 멤버를 SystemManagedIdleTimeoutWithHint로 설정해야 합니다.

const ULONG WIFI_DEFAULT_IDLE_TIMEOUT_HINT_MS = 3u * 1000u; // 3 seconds
...
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS  idleSettings;
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings,IdleCanWakeFromS0);

idleSettings.IdleTimeout = WIFI_DEFAULT_IDLE_TIMEOUT_HINT_MS; // 3 seconds
idleSettings.IdleTimeoutType = SystemManagedIdleTimeoutWithHint;
    status = WdfDeviceAssignS0IdleSettings(DeviceContext->WdfDevice, &idleSettings);