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 :
Panggil WifiDeviceInitConfig setelah memanggil NetDeviceInitConfig tetapi sebelum memanggil WdfDeviceCreate, mereferensikan objek WDFDEVICE_INIT yang sama yang diteruskan oleh kerangka kerja.
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.
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:
Panggil NetAdapterCreate untuk membuat objek NetAdapter baru.
Panggil WifiAdapterInitialize untuk menginisialisasi konteks WiFiCx dan mengaitkannya dengan objek NetAdapter ini.
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.
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.
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:
- Panggil WifiDirectDeviceCreate untuk membuat objek WIFIDIRECTDEVICE.
- Panggil WifiDirectDeviceInitialize untuk menginisialisasi objek.
- Panggil WifiDirectDeviceGetPortId untuk menentukan id port (yang digunakan dalam pesan perintah).
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.
- Panggil WifiAdapterGetType untuk menentukan jenis adaptor.
- Jika driver perlu mengkueri jenis adaptor dari objek NETADAPTER_INIT sebelum adaptor dibuat, panggil WifiAdapterInitGetType.
- Panggil WifiAdapterGetPortId menentukan ID port (digunakan dalam perintah pesan).
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.
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);