次の方法で共有


WiFiCx クライアント ドライバーの開発

デバイスとアダプターの初期化

NetAdapterCx が NetAdapter デバイスの初期化に必要なタスクに加えて、WiFiCx クライアント ドライバーは 、EvtDriverDeviceAdd コールバック関数で次のタスクも実行します。

  1. NetDeviceInitConfig を呼び出した後、WdfDeviceCreate を呼び出す前に WifiDeviceInitConfig を呼び出し、フレームワークによって渡されたのと同じWDFDEVICE_INIT オブジェクトを参照します。

  2. WifiDeviceInitialize を呼び出して、初期化されたWIFI_DEVICE_CONFIG構造体と WdfDeviceCreate から取得した WDFDEVICE オブジェクトを使用して、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 は、クライアントの EvtDevicePrepareHardware コールバックが成功した後に EvtWifiDeviceCreateAdapter コールバックを使用して、既定の NetAdapter (ステーション) を後で作成するようにドライバーに指示します。 WiFiCx と WDI は 、WDI_TASK_CREATE_PORT コマンドを呼び出さなくなりました。

EvtWifiDeviceCreateAdapter コールバック関数では、クライアント ドライバーは次の手順を実行する必要があります。

  1. NetAdapterCreate呼び出して、新しい NetAdapter オブジェクトを作成します。

  2. WifiAdapterInitialize呼び出して WiFiCx コンテキストを初期化し、この NetAdapter オブジェクトに関連付けます。

  3. NetAdapterStart呼び出してアダプターを起動します。

これが成功した場合、WiFiCx はデバイスまたはアダプターの初期化コマンド ( SET_ADAPTER_CONFIGURATIONTASK_SET_RADIO_STATEなど) を送信します。

EvtWifiDeviceCreateAdapter のコード例については、アダプター作成のイベント コールバックを参照してください。

WiFiCx クライアント ドライバー ステーション アダプターの作成を示すフロー チャート。

WiFiCx コマンド メッセージの処理

WiFiCx コマンド メッセージは、ほとんどの制御パス操作の前の WDI モデル コマンドに基づいています。 これらのコマンドは、 WiFiCx タスク OIDWiFiCx プロパティ OIDおよび WiFiCx 状態表示で定義されます。 詳細については、 WiFiCx メッセージ構造 を参照してください。

コマンドは、クライアント ドライバーによって提供される一連のコールバック関数と、WiFiCx によって提供される API を介して交換されます。

  • WiFiCx は、 その EvtWifiDeviceSendCommand コールバック関数を呼び出すことによって、クライアント ドライバーにコマンド メッセージを送信します。

  • メッセージを取得するために、クライアント ドライバーは WifiRequestGetInOutBuffer を呼び出して、入力/出力バッファーとバッファー長を取得します。 また、ドライバーは WifiRequestGetMessageId を呼び出してメッセージ ID を取得する必要もあります。

  • 要求を完了するために、ドライバーは WifiRequestComplete を呼び出すことによって、コマンドの M3 を非同期的に送信します。

  • コマンドが set コマンドで、元の要求に十分な大きさのバッファーが含まれていない場合、クライアントは WifiRequestSetBytesNeeded を呼び出して必要なバッファー サイズを設定し、状態BUFFER_OVERFLOWで要求を失敗させる必要があります。

  • コマンドがタスク コマンドの場合、クライアント ドライバーは後で WifiDeviceReceiveIndication を呼び出して関連付けられている M4 インジケーターを送信し、M1 に含まれているのと同じメッセージ ID を含む WDI ヘッダーを持つインジケーター バッファーを渡す必要があります。

  • 未承諾の表示も WifiDeviceReceiveIndication 経由で通知されますが、TransactionId メンバーは WDI_MESSAGE_HEADER0 に設定されます。

WiFiCx ドライバー コマンド メッセージの処理を示すフローチャート。

Wi-Fi Direct (P2P) のサポート

次のセクションでは、WiFiCx ドライバーが Direct Wi-Fi をサポートする方法について説明します。

Wi-Fi ダイレクト デバイスの機能

WIFI_WIFIDIRECT_CAPABILITIES は、WDI_P2P_CAPABILITIESおよび WDI_AP_CAPABILITIES TLV を介して 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 isn't a physical device.
*IfConnectorPresent     = 1     ; true

; In most cases, 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 を対象とするすべてのパケットに対して 1 つの 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 値に応じて、トランスレーターは必要に応じて最大 8 つの Tx キューを作成します。

クライアント ドライバーは、 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 デマルチプレクシング情報を追加するときに指定された範囲の値を超えることはできません。

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