Escritura de un controlador de cliente WiFiCx
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 debe realizar las siguientes 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 de WDFDEVICE_INIT pasado por el marco.
Llame a WifiDeviceInitialize para registrar funciones de devolución de llamada específicas del dispositivo WiFiCx, utilizando 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. El control de errores se ha dejado fuera 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);
En este diagrama de flujo de mensajes se muestra el proceso de inicialización.
Flujo de creación del adaptador predeterminado (estación)
A continuación, el controlador de 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 interrumpirse para poder consultar las funcionalidades de firmware, esto se puede hacer en EvtWdfDeviceD0EntryPostInterruptsEnabled.
Tenga en cuenta que WiFiCx ya no llama a WDI_TASK_OPEN/WDI_TASK_CLOSE para indicar a los clientes que carguen o descarguen firmware ni consultarán las funcionalidades de Wi-Fi a través del comando WDI_GET_ADAPTER_CAPABILITIES .
A diferencia de otros tipos de controladores NetAdapterCx, los controladores WiFiCx no deben crear el objeto NETADAPTER desde la función de devolución de llamada EvtDriverDeviceAdd . En su lugar, WiFiCx indicará a los controladores que creen el netAdapter predeterminado (station) más adelante con la devolución de llamada EvtWifiDeviceCreateAdapter (después de que la devolución de llamada evtDevicePrepareHardware del cliente se realice correctamente). Además, WiFiCx/WDI ya no llama 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 enviará comandos de inicialización para el dispositivo o adaptador (por ejemplo, SET_ADAPTER_CONFIGURATION, TASK_SET_RADIO_STATE, etc.).
Para obtener un ejemplo de código de EvtWifiDeviceCreateAdapter, consulte Devolución de llamada de eventos para la creación del adaptador.
Control de mensajes de comandos de WiFiCx
Los mensajes de comando WiFiCx se basan en los comandos del modelo WDI anteriores para la mayoría de las operaciones de ruta de acceso de control. Estos comandos se definen en las indicaciones de estado OID de la tarea WiFiCx, OID de propiedad WiFiCx y WiFiCx. Consulta 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 de 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 las longitudes de búfer de entrada y salida. 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 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 más adelante 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 el M1.
También se notifican indicaciones no solicitadas 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 describe cómo los controladores WiFiCx pueden admitir Wi-Fi Direct.
Wi-Fi funcionalidades de dispositivo directo
WIFI_WIFIDIRECT_CAPABILITIES representa todas las funcionalidades pertinentes que se establecieron anteriormente en WDI a través de la WDI_P2P_CAPABILITIES y WDI_AP_CAPABILITIES TLV. El controlador cliente llama a WifiDeviceSetWiFiDirectCapabilities para informar Wi-Fi funcionalidades directas a WiFiCx en la fase de funcionalidades del dispositivo establecido.
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 de eventos de Direct 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;
}
Devolución de llamada de eventos para la creación del adaptador
Los controladores de cliente crean el adaptador de estación y el adaptador wfdRole con la misma devolución de llamada 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.
- Llame 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 de 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 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$
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 debe configurar diferentes colas para diferentes elementos del mismo nivel, puede hacerlo configurando las propiedades de DEMUX adecuadas. Si se agregan propiedades de demux, el recuento de colas tx es el producto del número máximo de pares y el número máximo de tids, además de 1 (para difusión/multidifusión).
Varias colas para QOS
Antes de usar un objeto NETADAPTER_INIT * para crear un NETADAPTER, el controlador de cliente debe agregarle la demux WMMINFO:
...
WIFI_ADAPTER_TX_DEMUX wmmInfoDemux;
WIFI_ADAPTER_TX_WMMINFO_DEMUX_INIT(&wmmInfoDemux);
WifiAdapterInitAddTxDemux(adapterInit, &wmmInfoDemux);
Esto hará que el traductor cree hasta 8 colas tx a petición, según el valor NBL WlanTagHeader::WMMInfo.
El controlador cliente debe consultar la prioridad que usará 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 debe consultar la dirección del mismo nivel que usará el marco para esta cola desde EvtPacketQueueStart:
auto const peerAddress = WifiTxQueueGetDemuxPeerAddress(queue);
Todos los paquetes colocados en esta cola entre EvtStart y EvtStop serán para este mismo nivel.
Las colas solo se abren para las direcciones del mismo nivel que el controlador agregó 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 del mismo nivel asociando una cola a la dirección del mismo nivel. El número máximo de elementos del mismo nivel que el controlador puede agregar no superará el valor de intervalo proporcionado al agregar información de desmultiplexación tx.
WifiAdapterRemovePeer: indica a WiFiCx que se ha desconectado un par. Esto hace que el marco detenga la cola asociada.
Cambios en la directiva de energía
Para la administración de energía, los controladores de cliente deben usar el objeto NETPOWERSETTINGS como otros tipos de controladores de cliente netAdapterCx.
Para admitir el id. de dispositivo cuando el sistema está en estado de trabajo (S0), el controlador debe llamar a WdfDeviceAssignS0IdleSettings y establecer el miembro IdleTimeoutType de WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS en 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);