Schreiben eines WiFiCx-Clienttreibers

Geräte- und Adapterinitialisierung

Zusätzlich zu den Aufgaben, die NetAdapterCx für die NetAdapter-Geräteinitialisierung erfordert, muss ein WiFiCx-Clienttreiber auch die folgenden Aufgaben in seiner EvtDriverDeviceAdd-Rückruffunktion ausführen:

  1. Rufen Sie WifiDeviceInitConfig nach dem Aufruf von NetDeviceInitConfig , aber vor dem Aufruf von WdfDeviceCreate auf, wobei auf dasselbe WDFDEVICE_INIT objekt verwiesen wird, das vom Framework übergeben wurde.

  2. Rufen Sie WifiDeviceInitialize auf, um gerätespezifische WiFiCx-Rückruffunktionen mithilfe einer initialisierten WIFI_DEVICE_CONFIG-Struktur und des WDFDEVICE-Objekts zu registrieren, das von WdfDeviceCreate abgerufen wurde.

Im folgenden Beispiel wird veranschaulicht, wie das WiFiCx-Gerät initialisiert wird. Die Fehlerbehandlung wurde aus Gründen der Übersichtlichkeit ausgelassen.

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);

Dieses Nachrichtenflussdiagramm zeigt den Initialisierungsprozess.

Diagramm, das den Initialisierungsprozess des WiFiCx-Clienttreibers zeigt.

Erstellungsflow für Standardadapter (Station)

Als Nächstes muss der Clienttreiber alle Wi-Fi spezifischen Gerätefunktionen festlegen, in der Regel in der folgenden Rückruffunktion EvtDevicePrepareHardware . Wenn Ihre Hardware Interrupts zum Abfragen von Firmwarefunktionen benötigt, kann dies in EvtWdfDeviceD0EntryPostInterruptsEnabled erfolgen.

Beachten Sie, dass WiFiCx nicht mehr WDI_TASK_OPEN/WDI_TASK_CLOSE aufruft, um Clients zum Laden/Entladen der Firmware anzuweisen, noch fragt wiFiCx Wi-Fi Funktionen über den Befehl WDI_GET_ADAPTER_CAPABILITIES ab.

Im Gegensatz zu anderen Typen von NetAdapterCx-Treibern dürfen WiFiCx-Treiber das NETADAPTER-Objekt nicht aus der Rückruffunktion EvtDriverDeviceAdd erstellen. Stattdessen weist WiFiCx Treiber an, den Standardmäßigen NetAdapter (Station) später mithilfe des EvtWifiDeviceCreateAdapter-Rückrufs zu erstellen (nachdem der EvtDevicePrepareHardware-Rückruf des Clients erfolgreich war). Darüber hinaus ruft WiFiCx/WDI den Befehl WDI_TASK_CREATE_PORT nicht mehr auf.

In der Rückruffunktion EvtWifiDeviceCreateAdapter muss der Clienttreiber Folgendes ausführen:

  1. Rufen Sie NetAdapterCreate auf , um das neue NetAdapter-Objekt zu erstellen.

  2. Rufen Sie WifiAdapterInitialize auf, um den WiFiCx-Kontext zu initialisieren und diesem NetAdapter-Objekt zuzuordnen.

  3. Rufen Sie NetAdapterStart auf, um den Adapter zu starten.

Wenn dies erfolgreich ist, sendet WiFiCx Initialisierungsbefehle für das Gerät/adapter (z. B. SET_ADAPTER_CONFIGURATION, TASK_SET_RADIO_STATE usw.).

Ein Codebeispiel für EvtWifiDeviceCreateAdapter finden Sie unter Ereignisrückruf für die Adaptererstellung.

Flussdiagramm, das die Erstellung des WiFiCx-Clienttreiberstationsadapters zeigt.

Behandeln von WiFiCx-Befehlsmeldungen

WiFiCx-Befehlsmeldungen basieren für die meisten Steuerungspfadvorgänge auf den vorherigen WDI-Modellbefehlen. Diese Befehle werden in WiFiCx-Task-OIDs, WiFiCx-Eigenschafts-OIDs und WiFiCx-status-Angaben definiert. Weitere Informationen finden Sie unter WiFiCx-Nachrichtenstruktur .

Befehle werden über eine Reihe von Rückruffunktionen ausgetauscht, die vom Clienttreiber und von WiFiCx bereitgestellten APIs bereitgestellt werden:

  • WiFiCx sendet eine Befehlsnachricht an den Clienttreiber, indem seine EvtWifiDeviceSendCommand-Rückruffunktion aufgerufen wird.

  • Um die Nachricht abzurufen, ruft der Clienttreiber WifiRequestGetInOutBuffer auf, um den Eingabe-/Ausgabepuffer und die Pufferlängen abzurufen. Der Treiber muss auch WifiRequestGetMessageId aufrufen, um die Nachrichten-ID abzurufen.

  • Um die Anforderung abzuschließen, sendet der Treiber den M3 für den Befehl asynchron durch Aufrufen von WifiRequestComplete.

  • Wenn der Befehl ein set-Befehl ist und die ursprüngliche Anforderung keinen ausreichend großen Puffer enthielt, sollte der Client WifiRequestSetBytesNeeded aufrufen, um die erforderliche Puffergröße festzulegen, und dann schlägt die Anforderung mit status BUFFER_OVERFLOW fehl.

  • Wenn es sich bei dem Befehl um einen Taskbefehl handelt, muss der Clienttreiber später die zugeordnete M4-Anzeige senden, indem er WifiDeviceReceiveIndication aufruft und den Anzeigepuffer mit einem WDI-Header übergeben, der dieselbe Nachrichten-ID wie in M1 enthält.

  • Nicht angeforderte Hinweise werden auch über WifiDeviceReceiveIndication benachrichtigt, jedoch mit dem TransactionId-Element von WDI_MESSAGE_HEADER auf 0 festgelegt.

Flussdiagramm, das die Behandlung von WiFiCx-Treiberbefehlsmeldungen zeigt.

unterstützung für Wi-Fi Direct (P2P)

In den folgenden Abschnitten wird beschrieben, wie WiFiCx-Treiber Wi-Fi Direct unterstützen können.

Wi-Fi Direct-Gerätefunktionen

WIFI_WIFIDIRECT_CAPABILITIES stellt alle relevanten Funktionen dar, die zuvor in WDI über die WDI_P2P_CAPABILITIES und WDI_AP_CAPABILITIES TLVs festgelegt wurden. Der Clienttreiber ruft WifiDeviceSetWiFiDirectCapabilities auf, um wiFiCx in der Phase "Gerätefunktionen festlegen" Wi-Fi direkten Funktionen zu melden.

WIFI_WIFIDIRECT_CAPABILITIES wfdCapabilities = {};

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

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

Wi-Fi Direct-Ereignisrückruf für "WfdDevice"

Für Wi-Fi Direct ist "WfdDevice" ein Steuerelementobjekt ohne Datenpfadfunktionen. Daher verfügt WiFiCx über ein neues WDFObject namens WIFIDIRECTDEVICE. Clienttreiber in ihrer EvtWifiDeviceCreateWifiDirectDevice-Rückruffunktion :

In diesem Beispiel wird gezeigt, wie sie ein WIFIDIRECTDEVICE-Objekt erstellen und initialisieren.

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;
}

Ereignisrückruf für die Adaptererstellung

Clienttreiber erstellen den Stationsadapter und den WfdRole-Adapter mithilfe desselben Ereignisrückrufs: EvtWifiDeviceCreateAdapter.

  • Rufen Sie WifiAdapterGetType auf, um den Adaptertyp zu bestimmen.
  • Wenn der Treiber den Adaptertyp aus dem NETADAPTER_INIT-Objekt abfragen muss, bevor der Adapter erstellt wird, rufen Sie WifiAdapterInitGetType auf.
  • Rufen Sie WifiAdapterGetPortId auf, um die Port-ID (verwendet in Nachrichtenbefehlen) zu ermitteln.
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;
}

Wi-Fi ExemptionAction-Unterstützung in Tx-Warteschlangen

ExemptionAction ist eine neue NetAdapter-Paketerweiterung, die angibt, ob das Paket von allen vom Client ausgeführten Verschlüsselungsvorgängen ausgenommen werden soll. Weitere Informationen finden Sie in der Dokumentation zu 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;

}

Wi-Fi Änderung der direkten INI-/INF-Datei

vWifi-Funktionen wurden durch den NetAdapter ersetzt. Wenn Sie vom WDI-basierten Treiber portieren, sollte der INI/INF die vWIFI-bezogenen Informationen entfernen.

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$

Änderung des NetAdapter-Datenpfads

Einrichten mehrerer Tx-Warteschlangen

Standardmäßig erstellt NetAdapterCx eine Tx-Warteschlange für alle Pakete, die für einen NetAdapter vorgesehen sind.

Wenn ein Treiber mehrere Tx-Warteschlangen für QOS unterstützen muss oder unterschiedliche Warteschlangen für verschiedene Peers einrichten muss, kann er dazu die entsprechenden DEMUX-Eigenschaften einrichten. Wenn demux-Eigenschaften hinzugefügt werden, ist die Tx-Warteschlangenanzahl das Produkt der maximalen Anzahl von Peers und der maximalen Anzahl von Tids plus 1 (für Broadcast/Multicast).

Mehrere Warteschlangen für QOS

Vor der Verwendung eines NETADAPTER_INIT *-Objekts zum Erstellen eines NETADAPTERs sollte der Clienttreiber WMMINFO-Demux hinzufügen:

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

Dies führt dazu, dass der Übersetzer je nach NBL WlanTagHeader::WMMInfo-Wert bei Bedarf bis zu 8 Tx-Warteschlangen erstellt.

Der Clienttreiber sollte die Priorität abfragen, die das Framework für diese Warteschlange von EvtPacketQueueStart verwendet:

auto const priority = WifiTxQueueGetDemuxWmmInfo(queue);

Alle Pakete, die in dieser Warteschlange zwischen EvtStart und EvtStop platziert werden, haben die angegebene Priorität.

Mehrere Warteschlangen für Peers

Bevor Sie ein NETADAPTER_INIT *-Objekt zum Erstellen eines NETADAPTER verwenden, sollte der Clienttreiber PEER_ADDRESS Demux hinzufügen:

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

Der Clienttreiber sollte die Peeradresse abfragen, die das Framework für diese Warteschlange aus EvtPacketQueueStart verwendet:

auto const peerAddress = WifiTxQueueGetDemuxPeerAddress(queue);

Alle Pakete, die in dieser Warteschlange zwischen EvtStart und EvtStop platziert werden, sind für diesen Peer vorgesehen.

Warteschlangen werden nur für Peeradressen geöffnet, die der Treiber mithilfe der folgenden APIs hinzugefügt hat:

WifiAdapterAddPeer: Teilt WiFiCx mit, dass ein Peer eine Verbindung mit der angegebenen Adresse hergestellt hat. WiFiCx verwendet diese Adresse beim Peerdemultiplexing, indem der Peeradresse eine Warteschlange zugeordnet wird. Die maximale Anzahl von Peers, die der Treiber hinzufügen darf, darf den Bereichswert nicht überschreiten, der beim Hinzufügen von Tx-Demultiplexing-Informationen angegeben wird.

WifiAdapterRemovePeer: Teilt WiFiCx mit, dass ein Peer getrennt wurde. Dies bewirkt, dass das Framework die zugeordnete Warteschlange beendet.

Peerlebensdauer

Änderungen der Energierichtlinie

Für die Energieverwaltung sollten Clienttreiber das NETPOWERSETTINGS-Objekt wie andere Typen von NetAdapterCx-Clienttreibern verwenden.

Um den Leerlauf von Geräten zu unterstützen, wenn sich das System im Arbeitszustand (S0) befindet, muss der Treiber WdfDeviceAssignS0IdleSettings aufrufen und das IdleTimeoutType-Element von WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS auf SystemManagedIdleTimeoutWithHint festlegen:

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);