Bagikan melalui


Menulis driver klien WiFiCx

Inisialisasi perangkat dan adaptor

Selain tugas yang diperlukan NetAdapterCx untuk inisialisasi perangkat NetAdapter, driver klien WiFiCx juga harus melakukan tugas berikut dalam fungsi panggilan balik EvtDriverDeviceAdd-nya :

  1. Panggil WifiDeviceInitConfig setelah memanggil NetDeviceInitConfig tetapi sebelum memanggil WdfDeviceCreate, mereferensikan objek WDFDEVICE_INIT yang sama yang diteruskan oleh kerangka kerja.

  2. Panggil WifiDeviceInitialize untuk mendaftarkan fungsi panggilan balik khusus perangkat WiFiCx, menggunakan struktur WIFI_DEVICE_CONFIG yang diinisialisasi dan objek WDFDEVICE yang diperoleh dari WdfDeviceCreate.

Contoh berikut menunjukkan cara menginisialisasi perangkat WiFiCx. Penanganan kesalahan telah ditinggalkan untuk kejelasan.

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

Diagram alur pesan ini menunjukkan proses inisialisasi.

Diagram memperlihatkan proses inisialisasi driver klien WiFiCx.

Alur pembuatan adaptor default (stasiun)

Selanjutnya, driver klien harus mengatur semua Wi-Fi kemampuan perangkat tertentu, biasanya dalam fungsi panggilan balik EvtDevicePrepareHardware yang mengikutinya. Jika perangkat keras Anda perlu interupsi untuk diaktifkan untuk mengkueri kemampuan firmware, ini dapat dilakukan di EvtWdfDeviceD0EntryPostInterruptsEnabled.

Perhatikan bahwa WiFiCx tidak lagi memanggil WDI_TASK_OPEN/WDI_TASK_CLOSE untuk menginstruksikan klien untuk memuat/membongkar firmware atau akan meminta kemampuan Wi-Fi melalui perintah WDI_GET_ADAPTER_CAPABILITIES .

Tidak seperti jenis driver NetAdapterCx lainnya, driver WiFiCx tidak boleh membuat objek NETADAPTER dari dalam fungsi panggilan balik EvtDriverDeviceAdd . Sebagai gantinya, WiFiCx akan menginstruksikan driver untuk membuat NetAdapter default (stasiun) nanti menggunakan panggilan balik EvtWifiDeviceCreateAdapter (setelah panggilan balik EvtDevicePrepareHardware klien berhasil). Selain itu, WiFiCx/WDI tidak lagi memanggil perintah WDI_TASK_CREATE_PORT .

Dalam fungsi panggilan balik EvtWifiDeviceCreateAdapter , driver klien harus:

  1. Panggil NetAdapterCreate untuk membuat objek NetAdapter baru.

  2. Panggil WifiAdapterInitialize untuk menginisialisasi konteks WiFiCx dan mengaitkannya dengan objek NetAdapter ini.

  3. Panggil NetAdapterStart untuk memulai adaptor.

Jika ini berhasil, WiFiCx akan mengirim perintah inisialisasi untuk perangkat/adaptor (misalnya, SET_ADAPTER_CONFIGURATION, TASK_SET_RADIO_STATE, dll.).

Untuk contoh kode EvtWifiDeviceCreateAdapter, lihat Panggilan balik peristiwa untuk pembuatan adaptor.

Bagan alur memperlihatkan pembuatan adaptor stasiun driver klien WiFiCx.

Menangani pesan perintah WiFiCx

Pesan perintah WiFiCx didasarkan pada perintah model WDI sebelumnya untuk sebagian besar operasi jalur kontrol. Perintah ini didefinisikan dalam OID Tugas WiFiCx, OID Properti WiFiCx, dan indikasi status WiFiCx. Lihat Struktur pesan WiFiCx untuk informasi selengkapnya.

Perintah ditukar melalui serangkaian fungsi panggilan balik yang disediakan oleh driver klien dan API yang disediakan oleh WiFiCx:

  • WiFiCx mengirim pesan perintah ke driver klien dengan memanggil fungsi panggilan balik EvtWifiDeviceSendCommand - nya.

  • Untuk mengambil pesan, driver klien memanggil WifiRequestGetInOutBuffer untuk mendapatkan buffer input/output dan panjang buffer. Driver juga perlu memanggil WifiRequestGetMessageId untuk mengambil ID pesan.

  • Untuk menyelesaikan permintaan, driver mengirim M3 untuk perintah secara asinkron dengan memanggil WifiRequestComplete.

  • Jika perintah adalah perintah yang ditetapkan dan permintaan asli tidak berisi buffer yang cukup besar, klien harus memanggil WifiRequestSetBytesNeeded untuk mengatur ukuran buffer yang diperlukan lalu gagalkan permintaan dengan status BUFFER_OVERFLOW.

  • Jika perintah adalah perintah tugas, driver klien nantinya perlu mengirim indikasi M4 terkait dengan memanggil WifiDeviceReceiveIndication dan meneruskan buffer indikasi dengan header WDI yang berisi ID pesan yang sama seperti yang terkandung dalam M1.

  • Indikasi yang tidak diminta juga diberi tahu melalui WifiDeviceReceiveIndication, tetapi dengan anggota TransactionId WDI_MESSAGE_HEADER diatur ke 0.

Bagan alur memperlihatkan penanganan pesan perintah driver WiFiCx.

Dukungan Wi-Fi Direct (P2P)

Bagian berikut menjelaskan bagaimana driver WiFiCx dapat mendukung Wi-Fi Direct.

kemampuan perangkat Wi-Fi Direct

WIFI_WIFIDIRECT_CAPABILITIES mewakili semua kemampuan relevan yang sebelumnya diatur dalam WDI melaluiV WDI_P2P_CAPABILITIES dan WDI_AP_CAPABILITIES. Driver klien memanggil WifiDeviceSetWiFiDirectCapabilities untuk melaporkan kemampuan langsung Wi-Fi ke WiFiCx dalam fase kemampuan perangkat yang ditetapkan.

WIFI_WIFIDIRECT_CAPABILITIES wfdCapabilities = {};

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

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

Wi-Fi Panggilan balik peristiwa langsung untuk "WfdDevice"

Untuk Wi-Fi Direct, "WfdDevice" adalah objek kontrol tanpa kemampuan jalur data. Oleh karena itu, WiFiCx memiliki WDFObject baru bernama WIFIDIRECTDEVICE. Dalam fungsi panggilan balik EvtWifiDeviceCreateWifiDirectDevice mereka, driver klien:

Contoh ini menunjukkan cara membuat dan menginisialisasi objek 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;
}

Panggilan balik peristiwa untuk pembuatan adaptor

Driver klien membuat adaptor stasiun dan adaptor WfdRole menggunakan panggilan balik peristiwa yang sama: EvtWifiDeviceCreateAdapter.

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

dukungan Wi-Fi ExemptionAction dalam antrean Tx

ExemptionAction adalah ekstensi paket NetAdapter baru yang menunjukkan apakah paket diharapkan dikecualikan dari operasi sandi apa pun yang dilakukan oleh klien. Silakan baca dokumentasi tentang usExemptionActionType untuk detailnya.

#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 perubahan file INI/INF Langsung

Fungsi vWifi telah digantikan oleh NetAdapter. Jika Anda melakukan port dari driver berbasis WDI, INI/INF harus menghapus informasi terkait 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$

Perubahan jalur data NetAdapter

Menyiapkan beberapa antrean Tx

Secara default, NetAdapterCx akan membuat satu antrean Tx untuk semua paket yang ditujukan untuk NetAdapter.

Jika driver perlu mendukung beberapa antrean Tx untuk QOS atau perlu menyiapkan antrean yang berbeda untuk rekan yang berbeda, driver dapat melakukannya dengan menyiapkan properti DEMUX yang sesuai. Jika properti demux ditambahkan, jumlah antrean Tx adalah produk dari jumlah maksimum serekan dan jumlah maksimum tid, ditambah 1 (untuk siaran/multicast).

Beberapa antrean untuk QOS

Sebelum menggunakan objek NETADAPTER_INIT * untuk membuat NETADAPTER, driver klien harus menambahkan demux WMMINFO ke dalamnya:

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

Ini akan menyebabkan penerjemah membuat hingga antrean 8 Tx sesuai permintaan, tergantung pada nilai NBL WlanTagHeader::WMMInfo.

Driver klien harus meminta prioritas yang akan digunakan kerangka kerja untuk antrean ini dari EvtPacketQueueStart:

auto const priority = WifiTxQueueGetDemuxWmmInfo(queue);

Semua paket yang ditempatkan ke antrean ini antara EvtStart dan EvtStop akan memiliki prioritas yang diberikan.

Beberapa antrean untuk serekan

Sebelum menggunakan objek NETADAPTER_INIT * untuk membuat NETADAPTER, driver klien harus menambahkan PEER_ADDRESS demux ke dalamnya:

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

Driver klien harus meminta alamat serekan yang akan digunakan kerangka kerja untuk antrean ini dari EvtPacketQueueStart:

auto const peerAddress = WifiTxQueueGetDemuxPeerAddress(queue);

Semua paket yang ditempatkan pada antrean ini antara EvtStart dan EvtStop akan untuk serekan ini.

Antrean hanya dibuka untuk alamat serekan yang ditambahkan driver menggunakan API berikut:

WifiAdapterAddPeer: Memberi tahu WiFiCx bahwa serekan telah terhubung dengan alamat yang diberikan. WiFiCx akan menggunakan alamat ini dengan peer demultiplexing dengan mengaitkan antrean ke alamat serekan. Jumlah maksimum serekan yang dapat ditambahkan driver tidak boleh melebihi nilai rentang yang disediakan saat menambahkan info demultiplexing Tx.

WifiAdapterRemovePeer: Memberi tahu WiFiCx bahwa serekan telah terputus. Hal ini menyebabkan kerangka kerja menghentikan antrean terkait.

Masa pakai serekan

Perubahan kebijakan daya

Untuk manajemen daya, driver klien harus menggunakan objek NETPOWERSETTINGS seperti jenis driver klien NetAdapterCx lainnya.

Untuk mendukung perangkat yang menganggur saat sistem dalam status berfungsi (S0), driver harus memanggil WdfDeviceAssignS0IdleSettings dan mengatur anggota IdleTimeoutTypedari WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS ke 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);