Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Inicialización de dispositivos y adaptadores
Además de las tareas que NetAdapterCx requiere para la inicialización de dispositivos NetAdapter, un controlador cliente WiFiCx también realiza estas tareas en su función de devolución de llamada EvtDriverDeviceAdd :
Llame a WifiDeviceInitConfig después de llamar a NetDeviceInitConfig, pero antes de llamar a WdfDeviceCreate, haciendo referencia al mismo objeto WDFDEVICE_INIT proporcionado por el framework.
Llame a WifiDeviceInitialize para registrar funciones de devolución de llamada específicas del dispositivo WiFiCx mediante una estructura de WIFI_DEVICE_CONFIG inicializada y el objeto WDFDEVICE obtenido de WdfDeviceCreate.
En el ejemplo siguiente se muestra cómo inicializar el dispositivo WiFiCx. En el ejemplo se omite el control de errores para mayor claridad.
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);
Flujo de creación del adaptador predeterminado (estación)
A continuación, el controlador cliente debe establecer todas las funcionalidades de dispositivo específicas Wi-Fi, normalmente en la función de devolución de llamada EvtDevicePrepareHardware que sigue. Si el hardware necesita que se habiliten las interrupciones para poder consultar las capacidades del firmware, esto se puede hacer en EvtWdfDeviceD0EntryPostInterruptsEnabled.
WiFiCx ya no llama a WDI_TASK_OPEN ni WDI_TASK_CLOSE para indicar a los clientes que carguen o descarguen el firmware, y no consulta las funcionalidades de Wi-Fi mediante el comando WDI_GET_ADAPTER_CAPABILITIES .
A diferencia de otros tipos de controladores NetAdapterCx, Los controladores WiFiCx no crean el objeto NETADAPTER en la función de devolución de llamada EvtDriverDeviceAdd. En su lugar, WiFiCx indica a los controladores que creen el NetAdapter predeterminado (estación) más adelante mediante la devolución de llamada EvtWifiDeviceCreateAdapter después de que la devolución de llamada EvtDevicePrepareHardware del cliente tenga éxito. WiFiCx y WDI ya no llaman al comando WDI_TASK_CREATE_PORT .
En su función de devolución de llamada EvtWifiDeviceCreateAdapter, el controlador cliente debe:
Llame a NetAdapterCreate para crear el nuevo objeto NetAdapter.
Llame a WifiAdapterInitialize para inicializar el contexto WiFiCx y asociarlo a este objeto NetAdapter.
Llame a netAdapterStart para iniciar el adaptador.
Si esto se realiza correctamente, WiFiCx envía comandos de inicialización para el dispositivo o adaptador, como SET_ADAPTER_CONFIGURATION y TASK_SET_RADIO_STATE.
Consulte callback de evento para la creación del adaptador para ver un ejemplo de código de EvtWifiDeviceCreateAdapter.
Control de mensajes de comandos de WiFiCx
Los mensajes de comandos de WiFiCx están basados en los comandos anteriores del modelo WDI para la mayoría de las operaciones de ruta de acceso de control. Estos comandos se definen en OID de tarea de WiFiCx, OID de propiedad de WiFiCx e indicaciones de estado de WiFiCx. Consulte Estructura de mensajes WiFiCx para obtener más información.
Los comandos se intercambian a través de un conjunto de funciones de devolución de llamada proporcionadas por el controlador del cliente y las API proporcionadas por WiFiCx.
WiFiCx envía un mensaje de comando al controlador cliente invocando su función de devolución de llamada EvtWifiDeviceSendCommand .
Para recuperar el mensaje, el controlador cliente llama a WifiRequestGetInOutBuffer para obtener el búfer de entrada/salida y las longitudes del búfer. El controlador también debe llamar a WifiRequestGetMessageId para recuperar el identificador del mensaje.
Para completar la solicitud, el controlador envía el M3 para el comando de forma asincrónica llamando a WifiRequestComplete.
Si el comando es un comando set y la solicitud original no contenía un búfer lo suficientemente grande, el cliente debe llamar a WifiRequestSetBytesNeeded para establecer el tamaño de búfer necesario y, a continuación, producir un error en la solicitud con estado BUFFER_OVERFLOW.
Si el comando es un comando de tarea, el controlador cliente debe enviar posteriormente la indicación M4 asociada llamando a WifiDeviceReceiveIndication y pasar el búfer de indicación con un encabezado WDI que contiene el mismo identificador de mensaje que el contenido en M1.
Las indicaciones no solicitadas también se notifican a través de WifiDeviceReceiveIndication, pero con el miembro TransactionId de WDI_MESSAGE_HEADER establecido en 0.
compatibilidad con Wi-Fi Direct (P2P)
En las secciones siguientes se explica cómo los controladores WiFiCx admiten Wi-Fi Direct.
Wi-Fi Las capacidades directas del dispositivo
WIFI_WIFIDIRECT_CAPABILITIES representa todas las funcionalidades pertinentes que se establecieron anteriormente en WDI a través de los WDI_P2P_CAPABILITIES y WDI_AP_CAPABILITIES TLV. El controlador cliente llama a WifiDeviceSetWiFiDirectCapabilities para notificar Wi-Fi funcionalidades directas a WiFiCx en la fase de configuración de funcionalidades del dispositivo.
WIFI_WIFIDIRECT_CAPABILITIES wfdCapabilities = {};
// Set values
wfdCapabilities.ConcurrentGOCount = 1;
wfdCapabilities.ConcurrentClientCount = 1;
// Report capabilities to WiFiCx
WifiDeviceSetWiFiDirectCapabilities(Device, &wfdCapabilities);
Wi-Fi devolución de llamada directa de eventos para "WfdDevice"
Para Wi-Fi Direct, "WfdDevice" es un objeto de control sin funcionalidades de ruta de acceso de datos. Por lo tanto, WiFiCx tiene un nuevo WDFObject denominado WIFIDIRECTDEVICE. En su función de devolución de llamada EvtWifiDeviceCreateWifiDirectDevice , los controladores de cliente:
Llame a WifiDirectDeviceCreate para crear el objeto WIFIDIRECTDEVICE.
Llame a WifiDirectDeviceInitialize para inicializar el objeto.
Llame a WifiDirectDeviceGetPortId para determinar el identificador de puerto (que se usa en los mensajes de comando).
En este ejemplo se muestra cómo crear e inicializar un 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;
}
Función de retorno de eventos para la creación del adaptador
Los controladores cliente crean el adaptador de estación y el adaptador WfdRole utilizando el mismo callback de evento: EvtWifiDeviceCreateAdapter.
Llame a WifiAdapterGetType para determinar el tipo de adaptador.
Si el controlador necesita consultar el tipo de adaptador desde el objeto NETADAPTER_INIT antes de crear el adaptador, llame a WifiAdapterInitGetType.
Llama a WifiAdapterGetPortId para determinar el identificador de puerto (usado en los comandos de mensaje).
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;
}
compatibilidad con Wi-Fi ExemptionAction en colas tx
ExemptionAction es una nueva extensión de paquete NetAdapter que indica si se espera que el paquete esté exento de las operaciones de cifrado realizadas por el cliente. Lea la documentación sobre usExemptionActionType para obtener más información.
#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 cambio de archivo INI/INF directo
Las funcionalidades de vWifi se han reemplazado por NetAdapter. Si va a migrar desde el controlador basado en WDI, el INI/INF debe quitar la información relacionada con 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$
Cambio de ruta de acceso de datos de NetAdapter
Configuración de varias colas Tx
De forma predeterminada, NetAdapterCx creará una cola Tx para todos los paquetes destinados a netAdapter.
Si un controlador necesita admitir varias colas tx para QOS o configurar diferentes colas para distintos elementos del mismo nivel, lo hace configurando las propiedades de DEMUX adecuadas. Si agrega propiedades de demux, el número de colas Tx es el producto del número máximo de pares y el número máximo de tids, más 1 (para difusión o multidifusión).
Varias colas para QOS
Antes de usar un objeto NETADAPTER_INIT * para crear un NETADAPTER, el controlador cliente debe agregar WMMINFO demux a él:
...
WIFI_ADAPTER_TX_DEMUX wmmInfoDemux;
WIFI_ADAPTER_TX_WMMINFO_DEMUX_INIT(&wmmInfoDemux);
WifiAdapterInitAddTxDemux(adapterInit, &wmmInfoDemux);
Esto hace que el traductor cree hasta ocho colas Tx a petición, en función del valor NBL WlanTagHeader::WMMInfo.
El controlador cliente consulta la prioridad que usa el marco para esta cola desde EvtPacketQueueStart:
auto const priority = WifiTxQueueGetDemuxWmmInfo(queue);
Todos los paquetes colocados en esta cola entre EvtStart y EvtStop tendrán la prioridad dada.
Varias colas para elementos del mismo nivel
Antes de usar un objeto NETADAPTER_INIT * para crear un NETADAPTER, el controlador cliente debe agregarle PEER_ADDRESS demux:
...
WIFI_ADAPTER_TX_DEMUX peerInfoDemux;
WIFI_ADAPTER_TX_PEER_ADDRESS_DEMUX_INIT(&peerInfoDemux, maxNumOfPeers);
WifiAdapterInitAddTxDemux(adapterInit, &peerInfoDemux);
El controlador cliente consulta la dirección del par que usa el framework para esta cola desde EvtPacketQueueStart:
auto const peerAddress = WifiTxQueueGetDemuxPeerAddress(queue);
Todos los paquetes colocados en esta cola entre EvtStart y EvtStop tienen la prioridad dada.
El marco abre las colas solo para las direcciones del mismo nivel que agrega el controlador mediante las SIGUIENTES API:
WifiAdapterAddPeer: indica a WiFiCx que un elemento del mismo nivel se ha conectado con la dirección especificada. WiFiCx usará esta dirección con desmultiplexación entre pares, asociando una cola a la dirección del par. El número máximo de pares que el controlador puede agregar no puede superar el valor de rango proporcionado al agregar información de desmultiplexación de Tx.
WifiAdapterRemovePeer: informa a WiFiCx que un dispositivo se ha desconectado. El marco detiene la cola asociada.
Cambios en la directiva de energía
Para la administración de energía, los controladores de cliente usan el objeto NETPOWERSETTINGS , como otros tipos de controladores de cliente netAdapterCx.
Para apoyar el reposo del dispositivo cuando el sistema está en estado activo (S0), el controlador llama a WdfDeviceAssignS0IdleSettings y establece en el miembro IdleTimeoutType de WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS el valor 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);