Scrittura di un driver client WiFiCx

Inizializzazione del dispositivo e dell'adattatore

Oltre alle attività richieste da NetAdapterCx per l'inizializzazione del dispositivo NetAdapter, un driver client WiFiCx deve anche eseguire le attività seguenti nella relativa funzione di callback EvtDriverDeviceAdd :

  1. Chiama WifiDeviceInitConfig dopo aver chiamato NetDeviceInitConfig , ma prima di chiamare WdfDeviceCreate, facendo riferimento allo stesso oggetto WDFDEVICE_INIT passato dal framework.

  2. Chiama WifiDeviceInitialize per registrare le funzioni di callback specifiche del dispositivo WiFiCx, usando una struttura di WIFI_DEVICE_CONFIG inizializzata e l'oggetto WDFDEVICE ottenuto da WdfDeviceCreate.

Nell'esempio seguente viene illustrato come inizializzare il dispositivo WiFiCx. La gestione degli errori è stata lasciata per maggiore chiarezza.

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

Questo diagramma di flusso dei messaggi mostra il processo di inizializzazione.

Diagramma che mostra il processo di inizializzazione del driver client WiFiCx.

Flusso di creazione dell'adattatore predefinito (stazione)

Successivamente, il driver client deve impostare tutte le Wi-Fi funzionalità specifiche del dispositivo, in genere nella funzione di callback EvtDevicePrepareHardware che segue. Se l'hardware richiede interruzioni da abilitare per eseguire query sulle funzionalità del firmware, questa operazione può essere eseguita in EvtWdfDeviceD0EntryPostInterruptsEnabled.

Tieni presente che WiFiCx non chiama più WDI_TASK_OPEN WDI_TASK_CLOSE/ per indicare ai client di caricare/scaricare il firmware né eseguirà una query per Wi-Fi funzionalità tramite il comando WDI_GET_ADAPTER_CAPABILITIES.

A differenza di altri tipi di driver NetAdapterCx, i driver WiFiCx non devono creare l'oggetto NETADAPTER dall'interno della funzione di callback EvtDriverDeviceAdd . WiFiCx indicherà invece ai driver di creare il NetAdapter predefinito (station) in un secondo momento usando il callback EvtWifiDeviceCreateAdapter (dopo che il callback EvtDevicePrepareHardware del client ha esito positivo). Inoltre, WiFiCx/WDI non chiama più il comando WDI_TASK_CREATE_PORT .

Nella funzione di callback EvtWifiDeviceCreateAdapter , il driver client deve:

  1. Chiamare NetAdapterCreate per creare il nuovo oggetto NetAdapter.

  2. Chiama WifiAdapterInitialize per inizializzare il contesto WiFiCx e associarlo a questo oggetto NetAdapter.

  3. Chiamare NetAdapterStart per avviare l'adapter.

Se l'operazione ha esito positivo, WiFiCx invierà comandi di inizializzazione per il dispositivo o la scheda(ad esempio, SET_ADAPTER_CONFIGURATION, TASK_SET_RADIO_STATE e così via).

Per un esempio di codice di EvtWifiDeviceCreateAdapter, vedere Callback di eventi per la creazione dell'adapter.

Diagramma di flusso che mostra la creazione dell'adattatore stazione driver client WiFiCx.

Gestione dei messaggi di comando WiFiCx

I messaggi di comando WiFiCx sono basati sui comandi del modello WDI precedenti per la maggior parte delle operazioni sul percorso di controllo. Questi comandi sono definiti nelle indicazioni di stato WiFiCx Task OID, WiFiCx Property OID e WiFiCx. Per altre informazioni, vedere Struttura dei messaggi WiFiCx .

I comandi vengono scambiati tramite un set di funzioni di callback fornite dal driver client e dalle API fornite da WiFiCx:

  • WiFiCx invia un messaggio di comando al driver client richiamando la funzione di callback EvtWifiDeviceSendCommand .

  • Per recuperare il messaggio, il driver client chiama WifiRequestGetInOutBuffer per ottenere la lunghezza del buffer di input/output e del buffer. Il driver deve anche chiamare WifiRequestGetMessageId per recuperare l'ID messaggio.

  • Per completare la richiesta, il driver invia M3 per il comando in modo asincrono chiamando WifiRequestComplete.

  • Se il comando è un comando set e la richiesta originale non contiene un buffer sufficientemente grande, il client deve chiamare WifiRequestSetBytesNeeded per impostare le dimensioni del buffer necessarie e quindi non riuscire la richiesta con stato BUFFER_OVERFLOW.

  • Se il comando è un comando task, il driver client deve inviare in seguito l'indicazione M4 associata chiamando WifiDeviceReceiveIndication e passando il buffer di indicazione con un'intestazione WDI contenente lo stesso ID messaggio contenuto in M1.

  • Le indicazioni non richieste vengono inoltre notificate tramite WifiDeviceReceiveIndication, ma con il membro TransactionId di WDI_MESSAGE_HEADER impostato su 0.

Diagramma di flusso che mostra la gestione dei messaggi dei comandi del driver WiFiCx.

supporto di Wi-Fi Direct (P2P)

Le sezioni seguenti descrivono come i driver WiFiCx possono supportare Wi-Fi Direct.

funzionalità del dispositivo Wi-Fi Direct

WIFI_WIFIDIRECT_CAPABILITIES rappresenta tutte le funzionalità pertinenti impostate in precedenza in WDI tramite i WDI_P2P_CAPABILITIES e WDI_AP_CAPABILITIES TLV. Il driver client chiama WifiDeviceSetWiFiDirectCapabilities per segnalare Wi-Fi funzionalità dirette a WiFiCx nella fase di impostazione delle funzionalità del dispositivo.

WIFI_WIFIDIRECT_CAPABILITIES wfdCapabilities = {};

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

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

Wi-Fi callback dell'evento Direct per "WfdDevice"

Per Wi-Fi Direct, "WfdDevice" è un oggetto di controllo senza funzionalità di percorso dati. Di conseguenza, WiFiCx ha un nuovo WDFObject denominato WIFIDIRECTDEVICE. Nella funzione di callback EvtWifiDeviceCreateWifiDirectDevice driver client:

Questo esempio illustra come creare e inizializzare un oggetto 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;
}

Callback eventi per la creazione dell'adapter

I driver client creano l'adattatore di stazione e l'adattatore WfdRole usando lo stesso callback di eventi : EvtWifiDeviceCreateAdapter.

  • Chiamare WifiAdapterGetType per determinare il tipo di adattatore.
  • Se il driver deve eseguire query sul tipo di adattatore dall'oggetto NETADAPTER_INIT prima della creazione dell'adattatore, chiamare WifiAdapterInitGetType.
  • Chiamare WifiAdapterGetPortId determinare l'ID porta (usato nei comandi del messaggio).
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 supporto di ExemptionAction nelle code Tx

ExemptionAction è una nuova estensione del pacchetto NetAdapter che indica se il pacchetto deve essere esente da qualsiasi operazione di crittografia eseguita dal client. Per informazioni dettagliate, leggere la documentazione su 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 modifica del file INI/INF diretto

Le funzionalità vWifi sono state sostituite da NetAdapter. Se si esegue la conversione dal driver basato su WDI, INI/INF deve rimuovere le informazioni correlate a 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$

Modifica del percorso dati NetAdapter

Configurazione di più code Tx

Per impostazione predefinita, NetAdapterCx creerà una coda Tx per tutti i pacchetti destinati a un NetAdapter.

Se un driver deve supportare più code Tx per QOS o deve configurare code diverse per peer diversi, può farlo configurando le proprietà DEMUX appropriate. Se vengono aggiunte proprietà demux, il conteggio delle code Tx è il prodotto del numero massimo di peer e del numero massimo di tids, più 1 (per broadcast/multicast).

Più code per QOS

Prima di usare un oggetto NETADAPTER_INIT * per creare un NETADAPTER, il driver client deve aggiungervi un demux WMMINFO:

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

In questo modo il traduttore creerà fino a 8 code Tx su richiesta, a seconda del valore NBL WlanTagHeader::WMMInfo.

Il driver client deve eseguire una query sulla priorità che verrà usata dal framework per questa coda da EvtPacketQueueStart:

auto const priority = WifiTxQueueGetDemuxWmmInfo(queue);

Tutti i pacchetti inseriti in questa coda tra EvtStart e EvtStop avranno la priorità specificata.

Più code per peer

Prima di usare un oggetto NETADAPTER_INIT * per creare un NETADAPTER, il driver client deve aggiungervi PEER_ADDRESS demux:

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

Il driver client deve eseguire una query sull'indirizzo peer che verrà usato dal framework per questa coda da EvtPacketQueueStart:

auto const peerAddress = WifiTxQueueGetDemuxPeerAddress(queue);

Tutti i pacchetti inseriti in questa coda tra EvtStart e EvtStop saranno per questo peer.

Le code vengono aperte solo per gli indirizzi peer aggiunti dal driver usando le API seguenti:

WifiAdapterAddPeer: indica a WiFiCx che un peer ha connesso con l'indirizzo specificato. WiFiCx userà questo indirizzo con il demultiplexing peer associando una coda all'indirizzo peer. Il numero massimo di peer che il driver può aggiungere non deve superare il valore di intervallo specificato quando si aggiungono informazioni di demultiplexing Tx.

WifiAdapterRemovePeer: indica a WiFiCx che un peer è stato disconnesso. In questo modo il framework arresta la coda associata.

Durata peer

Modifiche ai criteri di risparmio energia

Per il risparmio energia, i driver client devono usare l'oggetto NETPOWERSETTINGS come altri tipi di driver client NetAdapterCx.

Per supportare l'inattività del dispositivo quando il sistema si trova nello stato di lavoro (S0), il driver deve chiamare WdfDeviceAssignS0IdleSettings e impostare il membro IdleTimeoutType di WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS su 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);