Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Initialisation de l’appareil et de l’adaptateur
Outre les tâches requises par NetAdapterCx pour l’initialisation de l’appareil NetAdapter, un pilote client WiFiCx effectue également ces tâches dans sa fonction de rappel EvtDriverDeviceAdd :
Appelez WifiDeviceInitConfig après avoir appelé NetDeviceInitConfig , mais avant d’appeler WdfDeviceCreate, référençant le même objet WDFDEVICE_INIT passé par l’infrastructure.
Appelez WifiDeviceInitialize pour inscrire des fonctions de rappel spécifiques à l’appareil WiFiCx, à l’aide d’une structure de WIFI_DEVICE_CONFIG initialisée et de l’objet WDFDEVICE obtenu à partir de WdfDeviceCreate.
L’exemple suivant montre comment initialiser l’appareil WiFiCx. L’exemple omet la gestion des erreurs pour plus de clarté.
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);
Flux de création d’adaptateur par défaut (station)
Ensuite, le pilote client doit définir toutes les fonctionnalités d’appareil spécifiques Wi-Fi, généralement dans la fonction de rappel EvtDevicePrepareHardware qui suit. Si votre matériel a besoin que les interruptions soient activées pour consulter les capacités du microprogramme, cela peut être effectué dans EvtWdfDeviceD0EntryPostInterruptsEnabled.
WiFiCx n’appelle plus WDI_TASK_OPEN ou WDI_TASK_CLOSE pour demander aux clients de charger ou de décharger le microprogramme, et il n’interroge pas les fonctionnalités de Wi-Fi à l’aide de la commande WDI_GET_ADAPTER_CAPABILITIES .
Contrairement aux autres types de pilotes NetAdapterCx, les pilotes WiFiCx ne créent pas l’objet NETADAPTER dans la fonction de rappel EvtDriverDeviceAdd . Au lieu de cela, WiFiCx demande aux pilotes de créer le NetAdapter par défaut (station) ultérieurement à l’aide du rappel EvtWifiDeviceCreateAdapter une fois que le rappel EvtDevicePrepareHardware du client réussit. WiFiCx et WDI n’appellent plus la commande WDI_TASK_CREATE_PORT .
Dans sa fonction de rappel EvtWifiDeviceCreateAdapter , le pilote client doit :
Appelez NetAdapterCreate pour créer l’objet NetAdapter.
Appelez WifiAdapterInitialize pour initialiser le contexte WiFiCx et l’associer à cet objet NetAdapter.
Appelez NetAdapterStart pour démarrer l’adaptateur.
Si cela réussit, WiFiCx envoie des commandes d’initialisation pour l’appareil ou l’adaptateur, telles que SET_ADAPTER_CONFIGURATION et TASK_SET_RADIO_STATE.
Consultez Rappel d'événement pour la création d'un adaptateur pour un exemple de code de EvtWifiDeviceCreateAdapter.
Gestion des messages de commande WiFiCx
Les messages de commande WiFiCx sont basés sur les commandes de modèle WDI précédentes pour la plupart des opérations de chemin de contrôle. Ces commandes sont définies dans les OID de tâche WiFiCx, les OID de propriété WiFiCx et les indications d’état WiFiCx. Pour plus d’informations, consultez la structure des messages WiFiCx .
Les commandes sont échangées via un ensemble de fonctions de rappel fournies par le pilote client et les API fournies par WiFiCx :
WiFiCx envoie un message de commande au pilote client en appelant sa fonction de rappel EvtWifiDeviceSendCommand .
Pour récupérer le message, le pilote client appelle WifiRequestGetInOutBuffer pour obtenir la mémoire tampon d’entrée/sortie et les longueurs de mémoire tampon. Le pilote doit également appeler WifiRequestGetMessageId pour récupérer l’ID de message.
Pour terminer la requête, le pilote envoie le M3 pour la commande de manière asynchrone en appelant WifiRequestComplete.
Si la commande est une commande set et que la requête d’origine ne contenait pas de mémoire tampon suffisamment volumineuse, le client doit appeler WifiRequestSetBytesNeeded pour définir la taille de mémoire tampon requise, puis échouer la demande avec l’état BUFFER_OVERFLOW.
Si la commande est une commande de tâche, le pilote client doit envoyer ultérieurement l’indication M4 associée en appelant WifiDeviceReceiveIndication et passer la mémoire tampon d’indication avec un en-tête WDI qui contient le même ID de message que celui contenu dans le M1.
Les indications non sollicitées sont également signalées via WifiDeviceReceiveIndication, mais avec le membre TransactionId de WDI_MESSAGE_HEADER défini sur 0.
prise en charge de Wi-Fi Direct (P2P)
Les sections suivantes expliquent comment les pilotes WiFiCx prennent en charge Wi-Fi Direct.
Wi-Fi Fonctionnalités d’appareil direct
WIFI_WIFIDIRECT_CAPABILITIES représente toutes les fonctionnalités pertinentes précédemment définies dans WDI via les WDI_P2P_CAPABILITIES et WDI_AP_CAPABILITIES TLVs. Le pilote client appelle WifiDeviceSetWiFiDirectCapabilities pour signaler Wi-Fi fonctionnalités directes à WiFiCx dans la phase définir les fonctionnalités de l’appareil.
WIFI_WIFIDIRECT_CAPABILITIES wfdCapabilities = {};
// Set values
wfdCapabilities.ConcurrentGOCount = 1;
wfdCapabilities.ConcurrentClientCount = 1;
// Report capabilities to WiFiCx
WifiDeviceSetWiFiDirectCapabilities(Device, &wfdCapabilities);
Wi-Fi rappel d’événement direct pour « WfdDevice »
Pour Wi-Fi Direct, « WfdDevice » est un objet de contrôle sans fonctionnalités de chemin d’accès aux données. Par conséquent, WiFiCx a un nouveau WDFObject nommé WIFIDIRECTDEVICE. Dans leur fonction de rappel EvtWifiDeviceCreateWifiDirectDevice, les pilotes clients :
Appelez WifiDirectDeviceCreate pour créer l’objet WIFIDIRECTDEVICE.
Appelez WifiDirectDeviceInitialize pour initialiser l’objet.
Appelez WifiDirectDeviceGetPortId pour déterminer l’ID de port (utilisé dans les messages de commande).
Cet exemple montre comment créer et initialiser un objet 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;
}
Rappel d’événement pour la création de l’adaptateur
Les pilotes clients créent l’adaptateur de station et l’adaptateur WfdRole à l’aide du même rappel d’événement : EvtWifiDeviceCreateAdapter.
Appelez WifiAdapterGetType pour déterminer le type d’adaptateur.
Si le pilote doit interroger le type d’adaptateur à partir de l’objet NETADAPTER_INIT avant la création de l’adaptateur, appelez WifiAdapterInitGetType.
Appelez WifiAdapterGetPortId pour déterminer l’ID de port (utilisé dans les commandes de message).
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 prise en charge d’ExemptionAction dans les files d’attente Tx
ExemptionAction est une nouvelle extension de paquet NetAdapter qui indique si le paquet est censé être exempté des opérations de chiffrement effectuées par le client. Pour plus d’informations, lisez la documentation sur 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 modification du fichier INI/INF direct
Les fonctionnalités vWifi ont été remplacées par NetAdapter. Si vous effectuez un portage à partir du pilote WDI, l’INI/INF doit supprimer les informations associées à 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$
Modification du chemin de données NetAdapter
Configuration de plusieurs files d’attente Tx
Par défaut, NetAdapterCx crée une file d’attente Tx pour tous les paquets destinés à un NetAdapter.
Si un pilote doit prendre en charge plusieurs files d’attente Tx pour QOS ou configurer différentes files d’attente pour différents homologues, il le fait en configurant les propriétés DEMUX appropriées. Si vous ajoutez des propriétés demux, le nombre de files d’attente Tx est le produit du nombre maximal d’homologues et du nombre maximal de tids, plus 1 (pour la diffusion ou la multidiffusion).
Plusieurs files d’attente pour QOS
Avant d’utiliser un objet NETADAPTER_INIT * pour créer un NETADAPTER, le pilote client doit lui ajouter WMMINFO :
...
WIFI_ADAPTER_TX_DEMUX wmmInfoDemux;
WIFI_ADAPTER_TX_WMMINFO_DEMUX_INIT(&wmmInfoDemux);
WifiAdapterInitAddTxDemux(adapterInit, &wmmInfoDemux);
Ainsi, le traducteur crée jusqu’à huit files d’attente Tx à la demande, en fonction de la valeur NBL WlanTagHeader ::WMMInfo.
Le pilote client interroge la priorité utilisée par l’infrastructure pour cette file d’attente à partir d’EvtPacketQueueStart :
auto const priority = WifiTxQueueGetDemuxWmmInfo(queue);
Tous les paquets placés dans cette file d’attente entre EvtStart et EvtStop auront la priorité donnée.
Plusieurs files d’attente pour les homologues
Avant d’utiliser un objet NETADAPTER_INIT * pour créer un NETADAPTER, le pilote client doit ajouter PEER_ADDRESS demux à celui-ci :
...
WIFI_ADAPTER_TX_DEMUX peerInfoDemux;
WIFI_ADAPTER_TX_PEER_ADDRESS_DEMUX_INIT(&peerInfoDemux, maxNumOfPeers);
WifiAdapterInitAddTxDemux(adapterInit, &peerInfoDemux);
Le pilote client interroge l’adresse homologue utilisée par l’infrastructure pour cette file d’attente à partir d’EvtPacketQueueStart :
auto const peerAddress = WifiTxQueueGetDemuxPeerAddress(queue);
Tous les paquets placés dans cette file d’attente entre EvtStart et EvtStop ont la priorité donnée.
Le cadre ouvre uniquement les files d’attente pour les adresses de pairs que le pilote ajoute à l’aide des API suivantes :
WifiAdapterAddPeer : indique à WiFiCx qu’un homologue s’est connecté à l’adresse donnée. WiFiCx utilisera cette adresse avec un démultiplexage des pairs en associant une file d'attente à l'adresse du pair. Le nombre maximal d’homologues que le pilote peut ajouter ne peut pas dépasser la valeur de plage fournie lors de l’ajout d’informations de démultiplexing Tx.
WifiAdapterRemovePeer : indique à WiFiCx qu’un homologue a été déconnecté. L’infrastructure arrête la file d’attente associée.
Modifications de la politique énergétique
Pour la gestion de l’alimentation, les pilotes clients utilisent l’objet NETPOWERSETTINGS comme d’autres types de pilotes clients NetAdapterCx.
Pour prendre en charge l'idling d'appareil lorsque le système est dans son état de fonctionnement (S0), le pilote appelle WdfDeviceAssignS0IdleSettings et définit le membre IdleTimeoutType de WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS sur 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);