Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Inicialização de dispositivo e adaptador
Além das tarefas que o NetAdapterCx requer para a inicialização do dispositivo NetAdapter, um driver cliente WiFiCx também executa essas tarefas em sua função de retorno de chamada EvtDriverDeviceAdd :
Chame WifiDeviceInitConfig depois de chamar NetDeviceInitConfig , mas antes de chamar WdfDeviceCreate, referenciando o mesmo objeto WDFDEVICE_INIT passado pela estrutura.
Chame WifiDeviceInitialize para registrar funções de retorno de chamada específicas do dispositivo WiFiCx, usando uma estrutura de WIFI_DEVICE_CONFIG inicializada e o objeto WDFDEVICE obtido do WdfDeviceCreate.
O exemplo a seguir demonstra como inicializar o dispositivo WiFiCx. O exemplo omite o tratamento de erros para maior clareza.
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);
Fluxo de criação do adaptador padrão (estação)
Em seguida, o driver cliente deve definir todos os recursos de dispositivo Wi-Fi específicos, normalmente na função de retorno de chamada EvtDevicePrepareHardware a seguir. Se o hardware precisar que interrupções estejam habilitadas para consultar capacidades de firmware, isso pode ser feito em EvtWdfDeviceD0EntryPostInterruptsEnabled.
O WiFiCx não chama mais WDI_TASK_OPEN ou WDI_TASK_CLOSE para instruir os clientes a carregar ou descarregar firmware e não consulta recursos de Wi-Fi usando o comando WDI_GET_ADAPTER_CAPABILITIES .
Ao contrário de outros tipos de drivers NetAdapterCx, os drivers WiFiCx não criam o objeto NETADAPTER na função de retorno de chamada EvtDriverDeviceAdd . Em vez disso, o WiFiCx instrui os drivers a criar o NetAdapter (estação) padrão mais tarde usando o retorno de chamada EvtWifiDeviceCreateAdapter depois que o retorno de chamada EvtDevicePrepareHardware do cliente for bem-sucedido. WiFiCx e WDI não chamam mais o comando WDI_TASK_CREATE_PORT .
Em sua função de retorno de chamada EvtWifiDeviceCreateAdapter, o driver cliente deve:
Chame WifiAdapterInitialize para inicializar o contexto WiFiCx e associá-lo a esse objeto NetAdapter.
Chame netAdapterStart para iniciar o adaptador.
Se isso for bem-sucedido, o WiFiCx enviará comandos de inicialização para o dispositivo ou adaptador, como SET_ADAPTER_CONFIGURATION e TASK_SET_RADIO_STATE.
Consulte o callback de evento para a criação do adaptador para obter um exemplo de código de EvtWifiDeviceCreateAdapter.
Manipulando mensagens de comando WiFiCx
As mensagens de comando WiFiCx são baseadas nos comandos de modelo WDI anteriores para a maioria das operações de caminho de controle. Esses comandos são definidos em OIDs de Tarefa WiFiCx, OIDs de Propriedade WiFiCx e indicações de status do WiFiCx. Consulte a estrutura de mensagens WiFiCx para obter mais informações.
Os comandos são trocados por meio de um conjunto de funções de retorno de chamada fornecidas pelo driver cliente e pelas APIs fornecidas pelo WiFiCx:
O WiFiCx envia uma mensagem de comando para o driver cliente invocando sua função de retorno de chamada EvtWifiDeviceSendCommand .
Para recuperar a mensagem, o driver cliente chama WifiRequestGetInOutBuffer para obter o buffer de entrada/saída e os comprimentos do buffer. O driver também precisa chamar WifiRequestGetMessageId para recuperar a ID da mensagem.
Para concluir a solicitação, o driver envia o M3 para o comando de forma assíncrona chamando WifiRequestComplete.
Se o comando for um comando definido e a solicitação original não contiver um buffer grande o suficiente, o cliente deverá chamar WifiRequestSetBytesNeeded para definir o tamanho do buffer necessário e, em seguida, falhar a solicitação com o status BUFFER_OVERFLOW.
Se o comando for um comando de tarefa, o driver cliente precisará enviar posteriormente a indicação M4 associada chamando WifiDeviceReceiveIndication e passar o buffer de indicação com um cabeçalho WDI que contenha a mesma ID de mensagem contida no M1.
As indicações não solicitadas também são notificadas por meio de WifiDeviceReceiveIndication, mas com o membro TransactionId de WDI_MESSAGE_HEADER definido como 0.
Suporte do Wi-Fi Direct (P2P)
As seções a seguir explicam como os drivers WiFiCx dão suporte Wi-Fi Direct.
Wi-Fi recursos de dispositivo direto
WIFI_WIFIDIRECT_CAPABILITIES representa todos os recursos relevantes que foram definidos anteriormente no WDI por meio das TLVs WDI_P2P_CAPABILITIES e WDI_AP_CAPABILITIES. O driver cliente chama WifiDeviceSetWiFiDirectCapabilities para reportar as capacidades diretas Wi-Fi para o WiFiCx na fase de definição de capacidades do dispositivo.
WIFI_WIFIDIRECT_CAPABILITIES wfdCapabilities = {};
// Set values
wfdCapabilities.ConcurrentGOCount = 1;
wfdCapabilities.ConcurrentClientCount = 1;
// Report capabilities to WiFiCx
WifiDeviceSetWiFiDirectCapabilities(Device, &wfdCapabilities);
Wi-Fi Chamada direta de retorno de evento para "WfdDevice"
Para Wi-Fi Direct, o "WfdDevice" é um objeto de controle sem recursos de caminho de dados. Portanto, o WiFiCx tem um novo WDFObject chamado WIFIDIRECTDEVICE. Na função de retorno de chamada EvtWifiDeviceCreateWifiDirectDevice, os drivers de cliente:
Chame WifiDirectDeviceCreate para criar o objeto WIFIDIRECTDEVICE.
Chame WifiDirectDeviceInitialize para inicializar o objeto.
Chame WifiDirectDeviceGetPortId para determinar a ID da porta (que é usada em mensagens de comando).
Este exemplo mostra como criar e inicializar um objeto 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 de evento para criação de adaptador
Os drivers de cliente criam o adaptador de estação e o adaptador WfdRole usando o mesmo callback de evento: EvtWifiDeviceCreateAdapter.
Chame WifiAdapterGetType para determinar o tipo de adaptador.
Se o driver precisar consultar o tipo de adaptador do objeto NETADAPTER_INIT antes de o adaptador ser criado, chame WifiAdapterInitGetType.
Chamar o WifiAdapterGetPortId para determinar o ID da porta (usado em comandos de mensagem).
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 suporte a ExemptionAction em filas de transmissão Tx
ExemptionAction é uma nova extensão de pacote NetAdapter que indica se o pacote deve ser isento de quaisquer operações de criptografia executadas pelo cliente. Leia a documentação sobre usExemptionActionType para obter detalhes.
#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 alteração direta do arquivo INI/INF
As funcionalidades do vWifi foram substituídas pelo NetAdapter. Se você estiver portando do driver baseado em WDI, os arquivos INI/INF devem ter as informações relacionadas ao vWIFI removidas.
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$
Alteração do caminho de dados do NetAdapter
Configurando várias filas Tx
Por padrão, NetAdapterCx criará uma fila Tx para todos os pacotes destinados a um NetAdapter.
Se um driver precisar dar suporte a várias filas Tx para QOS ou configurar filas diferentes para diferentes pares, ele o fará configurando as propriedades apropriadas do DEMUX. Se você adicionar propriedades de demux, a contagem de filas Tx será o produto do número máximo de nós conectados (peers) e do número máximo de tids, mais 1 (para transmissão ou multicast).
Várias filas para QOS
Antes de usar um objeto NETADAPTER_INIT * para criar um NETADAPTER, o driver cliente deve adicionar o demux WMMINFO a ele:
...
WIFI_ADAPTER_TX_DEMUX wmmInfoDemux;
WIFI_ADAPTER_TX_WMMINFO_DEMUX_INIT(&wmmInfoDemux);
WifiAdapterInitAddTxDemux(adapterInit, &wmmInfoDemux);
Isso faz com que o tradutor crie até oito filas Tx sob demanda, dependendo do valor NBL WlanTagHeader::WMMInfo.
O driver cliente consulta a prioridade que o framework usa para essa fila a partir do EvtPacketQueueStart:
auto const priority = WifiTxQueueGetDemuxWmmInfo(queue);
Todos os pacotes colocados nessa fila entre EvtStart e EvtStop terão a prioridade determinada.
Várias filas para pares
Antes de usar um objeto NETADAPTER_INIT * para criar um NETADAPTER, o driver cliente deve adicionar PEER_ADDRESS demux a ele:
...
WIFI_ADAPTER_TX_DEMUX peerInfoDemux;
WIFI_ADAPTER_TX_PEER_ADDRESS_DEMUX_INIT(&peerInfoDemux, maxNumOfPeers);
WifiAdapterInitAddTxDemux(adapterInit, &peerInfoDemux);
O driver cliente consulta o endereço par que a estrutura usa para essa fila do EvtPacketQueueStart:
auto const peerAddress = WifiTxQueueGetDemuxPeerAddress(queue);
Todos os pacotes colocados nessa fila entre EvtStart e EvtStop têm a prioridade determinada.
A estrutura abre filas somente para endereços pares que o driver adiciona usando as seguintes APIs:
WifiAdapterAddPeer: informa ao WiFiCx que um dispositivo se conectou com o endereço especificado. O WiFiCx usará esse endereço com demultiplexação por par, associando uma fila ao endereço do par. O número máximo de pares que o driver pode adicionar não pode exceder o valor de intervalo fornecido ao adicionar informação de demultiplexação de Tx.
WifiAdapterRemovePeer: Informa ao WiFiCx que um dispositivo foi desconectado. A estrutura interrompe a fila associada.
Alterações na política de energia
Para o gerenciamento de energia, os drivers cliente usam o objeto NETPOWERSETTINGS como outros tipos de drivers de cliente NetAdapterCx.
Para dar suporte ao funcionamento em inatividade do dispositivo quando o sistema estiver em seu estado de trabalho (S0), o driver chama WdfDeviceAssignS0IdleSettings e define o membro IdleTimeoutType de WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS para 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);