diff options
| author | Yang You (UU) <[email protected]> | 2026-06-17 10:33:03 -0700 |
|---|---|---|
| committer | Yang You (UU) <[email protected]> | 2026-06-17 10:33:03 -0700 |
| commit | 4c89b2cd0cf2e94bc2e933e9ccf7d35c7b26a140 (patch) | |
| tree | 6e8201e56ede0e2c92489b1c7ce0230c90329295 | |
| parent | 52f73736b407acb1f8a0af513c9614a5354cea5b (diff) | |
check in the demo for OEM solution based on Service Command channel.
| -rw-r--r-- | network/wlan/wificx/OEM/OemDeviceService.vcxproj | 50 | ||||
| -rw-r--r-- | network/wlan/wificx/OEM/OemDeviceServiceApp.cpp | 158 | ||||
| -rw-r--r-- | network/wlan/wificx/OEM/README.md | 90 | ||||
| -rw-r--r-- | network/wlan/wificx/drivercode/SharedTypes.h | 17 | ||||
| -rw-r--r-- | network/wlan/wificx/drivercode/wifihal.cpp | 134 | ||||
| -rw-r--r-- | network/wlan/wificx/drivercode/wifihal.h | 15 | ||||
| -rw-r--r-- | network/wlan/wificx/drivercode/wifirequest.cpp | 2 | ||||
| -rw-r--r-- | network/wlan/wificx/drivercode/wifitransition.cpp | 128 | ||||
| -rw-r--r-- | network/wlan/wificx/drivercode/wifitransition.h | 2 | ||||
| -rw-r--r-- | network/wlan/wificx/wificxsampleclient.sln | 14 |
10 files changed, 605 insertions, 5 deletions
diff --git a/network/wlan/wificx/OEM/OemDeviceService.vcxproj b/network/wlan/wificx/OEM/OemDeviceService.vcxproj new file mode 100644 index 00000000..c48f5bb8 --- /dev/null +++ b/network/wlan/wificx/OEM/OemDeviceService.vcxproj @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|ARM64"> + <Configuration>Debug</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|ARM64"> + <Configuration>Release</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{B3C9A1E2-7F4D-4B2A-9C5E-1A2B3C4D5E6F}</ProjectGuid> + <RootNamespace>OemDeviceService</RootNamespace> + <ConfigurationType>Application</ConfigurationType> + <CharacterSet>Unicode</CharacterSet> + <ProjectName>OemDeviceServiceApplication</ProjectName> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ItemDefinitionGroup> + <ClCompile> + <WarningLevel>Level4</WarningLevel> + <PreprocessorDefinitions>_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <AdditionalDependencies>wlanapi.lib;ole32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="OemDeviceServiceApp.cpp" /> + </ItemGroup> + <ItemGroup> + <None Include="README.md" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/network/wlan/wificx/OEM/OemDeviceServiceApp.cpp b/network/wlan/wificx/OEM/OemDeviceServiceApp.cpp new file mode 100644 index 00000000..74c0e3d3 --- /dev/null +++ b/network/wlan/wificx/OEM/OemDeviceServiceApp.cpp @@ -0,0 +1,158 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// OEM sample: enumerates supported device services, then sends "Hello, My Driver" +// to the WiFiCx sample driver via WlanDeviceServiceCommand and prints the +// driver's "Nice to meet you, My OEM". + +#define NOMINMAX // use std::min/std::max instead of the windows.h min/max macros +#include <windows.h> +#include <wlanapi.h> +#include <objbase.h> // StringFromGUID2 +#include <algorithm> // std::min +#include <cstdio> + +#pragma comment(lib, "wlanapi.lib") +#pragma comment(lib, "ole32.lib") // StringFromGUID2 + +// WlanGetSupportedDeviceServices, WlanDeviceServiceCommand and +// WLAN_DEVICE_SERVICE_GUID_LIST are all declared by wlanapi.h. + +// This GUID/opcode pair MUST match the driver (drivercode\SharedTypes.h). +// {2d6f9a14-3a1d-4f0a-9b7e-1c2e3a4b5c6d} +static const GUID GUID_OEM_SAMPLE_DEVICE_SERVICE = +{ 0x2d6f9a14, 0x3a1d, 0x4f0a, { 0x9b, 0x7e, 0x1c, 0x2e, 0x3a, 0x4b, 0x5c, 0x6d } }; + +#define OEM_DEVICE_SERVICE_OPCODE_HELLO 0x00000001 +#define OEM_DEVICE_SERVICE_REQUEST_STRING "Hello, My Driver" + +static void PrintGuid(const GUID& g) +{ + wchar_t buf[64] = { 0 }; + StringFromGUID2(g, buf, ARRAYSIZE(buf)); + wprintf(L"%s", buf); +} + +// Enumerate the device services the driver advertises (via WDI_GET_SUPPORTED_DEVICE_SERVICES). +static bool QuerySupportedServices(HANDLE hClient, const GUID& interfaceGuid) +{ + PWLAN_DEVICE_SERVICE_GUID_LIST pList = nullptr; + DWORD result = WlanGetSupportedDeviceServices(hClient, &interfaceGuid, &pList); + if (result != ERROR_SUCCESS || pList == nullptr) + { + printf("WlanGetSupportedDeviceServices failed with error %u\n", result); + return false; + } + + bool found = false; + printf("Supported device services: %u\n", pList->dwNumberOfItems); + for (DWORD i = 0; i < pList->dwNumberOfItems; i++) + { + printf(" [%u] ", i); + PrintGuid(pList->DeviceService[i]); + if (IsEqualGUID(pList->DeviceService[i], GUID_OEM_SAMPLE_DEVICE_SERVICE)) + { + found = true; + printf(" <-- OEM sample service"); + } + printf("\n"); + } + + WlanFreeMemory(pList); + return found; +} + +static void SendHelloToInterface(HANDLE hClient, const GUID& interfaceGuid) +{ + char inBuffer[] = OEM_DEVICE_SERVICE_REQUEST_STRING; // includes null terminator + DWORD inBufferSize = static_cast<DWORD>(sizeof(inBuffer)); + + BYTE outBuffer[256] = { 0 }; + DWORD outBufferSize = static_cast<DWORD>(sizeof(outBuffer)); + DWORD bytesReturned = 0; + + printf("Sending device service command: \"%s\"\n", inBuffer); + + DWORD result = WlanDeviceServiceCommand( + hClient, + &interfaceGuid, + const_cast<LPGUID>(&GUID_OEM_SAMPLE_DEVICE_SERVICE), + OEM_DEVICE_SERVICE_OPCODE_HELLO, + inBufferSize, + inBuffer, + outBufferSize, + outBuffer, + &bytesReturned); + + if (result != ERROR_SUCCESS) + { + printf("WlanDeviceServiceCommand failed with error %u\n", result); + return; + } + + if (bytesReturned > 0) + { + outBuffer[std::min(bytesReturned, static_cast<DWORD>(sizeof(outBuffer) - 1))] = '\0'; + printf("Driver responded: \"%s\" (%u bytes)\n", reinterpret_cast<char*>(outBuffer), bytesReturned); + } + else + { + printf("Driver returned no data.\n"); + } +} + +int __cdecl main() +{ + HANDLE hClient = nullptr; + DWORD negotiatedVersion = 0; + PWLAN_INTERFACE_INFO_LIST pIfList = nullptr; + + // 1) WlanOpenHandle + DWORD result = WlanOpenHandle(WLAN_API_VERSION_2_0, nullptr, &negotiatedVersion, &hClient); + if (result != ERROR_SUCCESS) + { + printf("WlanOpenHandle failed with error %u\n", result); + return 1; + } + + // 2) WlanEnumInterfaces + result = WlanEnumInterfaces(hClient, nullptr, &pIfList); + if (result != ERROR_SUCCESS) + { + printf("WlanEnumInterfaces failed with error %u\n", result); + WlanCloseHandle(hClient, nullptr); + return 1; + } + + printf("Found %u WLAN interface(s).\n", pIfList->dwNumberOfItems); + + for (DWORD i = 0; i < pIfList->dwNumberOfItems; i++) + { + const WLAN_INTERFACE_INFO& ifInfo = pIfList->InterfaceInfo[i]; + printf("\nInterface[%u]: %ws\n", i, ifInfo.strInterfaceDescription); + + // Enumerate supported device services first. + bool supported = QuerySupportedServices(hClient, ifInfo.InterfaceGuid); + + // 3) WlanDeviceServiceCommand (only if our service is advertised) + if (supported) + { + SendHelloToInterface(hClient, ifInfo.InterfaceGuid); + } + else + { + printf("OEM sample device service not advertised on this interface; skipping command.\n"); + } + } + + // 4) WlanFreeMemory(pIfList); + if (pIfList != nullptr) + { + WlanFreeMemory(pIfList); + pIfList = nullptr; + } + + // 5) WlanCloseHandle(hClient, nullptr); + WlanCloseHandle(hClient, nullptr); + + return 0; +} diff --git a/network/wlan/wificx/OEM/README.md b/network/wlan/wificx/OEM/README.md new file mode 100644 index 00000000..98419932 --- /dev/null +++ b/network/wlan/wificx/OEM/README.md @@ -0,0 +1,90 @@ +# OEM Device Service Sample (`OemDeviceServiceApplication`) + +A user-mode console application that demonstrates how an OEM/IHV utility communicates +with the WiFiCx sample driver through a **WLAN device service**. The app sends the +request string `"Hello, My Driver"` and prints the driver's reply +`"Nice to meet you, My OEM"`. + +## What it does + +The tool walks through the standard WLAN client flow: + +1. **`WlanOpenHandle`** — opens a client handle using `WLAN_API_VERSION_2_0`. +2. **`WlanEnumInterfaces`** — enumerates all WLAN interfaces on the machine. +3. **`WlanGetSupportedDeviceServices`** — for each interface, enumerates the device + service GUIDs the driver advertises and checks whether the OEM sample service + (`GUID_OEM_SAMPLE_DEVICE_SERVICE`) is present. +4. **`WlanDeviceServiceCommand`** — when the service is advertised, sends the + `OEM_DEVICE_SERVICE_OPCODE_HELLO` opcode with the request payload and prints the + bytes returned by the driver. +5. **`WlanFreeMemory` / `WlanCloseHandle`** — releases the interface list and the + client handle. + +If the OEM sample device service is not advertised on an interface, the command is +skipped for that interface. + +## Device service contract + +These values **must stay in sync** with the driver-side definitions in +[`drivercode/SharedTypes.h`](../drivercode/SharedTypes.h): + +| Item | Value | +| --- | --- | +| Service GUID | `{2d6f9a14-3a1d-4f0a-9b7e-1c2e3a4b5c6d}` (`GUID_OEM_SAMPLE_DEVICE_SERVICE`) | +| Opcode | `0x00000001` (`OEM_DEVICE_SERVICE_OPCODE_HELLO`) | +| Request string | `"Hello, My Driver"` | +| Response string | `"Nice to meet you, My OEM"` | + +The driver advertises the service GUID via `WDI_GET_SUPPORTED_DEVICE_SERVICES`, so the +GUID must match on both sides for the exchange to succeed. + +## Source layout + +| File | Purpose | +| --- | --- | +| [`OemDeviceServiceApp.cpp`](OemDeviceServiceApp.cpp) | Application entry point and device service logic. | +| [`OemDeviceService.vcxproj`](OemDeviceService.vcxproj) | MSBuild project for the console app. | + +## Build + +The project (`OemDeviceService.vcxproj`) builds as a console **Application** using the +`WindowsApplicationForDrivers10.0` platform toolset. + +- **Configurations:** `Debug`, `Release` +- **Platforms:** `x64`, `ARM64` +- **Linked libraries:** `wlanapi.lib`, `ole32.lib` + +Build it from Visual Studio as part of the solution, or from the command line: + +```cmd +msbuild OEM\OemDeviceService.vcxproj /p:Configuration=Release /p:Platform=x64 +``` + +## Run + +Run the resulting executable from an elevated command prompt on a machine where the +WiFiCx sample driver is installed: + +```cmd +OemDeviceServiceApplication.exe +``` + +### Example output + +```text +Found 1 WLAN interface(s). + +Interface[0]: WiFiCx Sample Client Device +Supported device services: 1 + [0] {2D6F9A14-3A1D-4F0A-9B7E-1C2E3A4B5C6D} <-- OEM sample service +Sending device service command: "Hello, My Driver" +Driver responded: "Nice to meet you, My OEM" (25 bytes) +``` + +## Requirements + +- The WiFiCx sample driver must be installed and the adapter present. +- The driver must advertise `GUID_OEM_SAMPLE_DEVICE_SERVICE`; otherwise the app prints + that the service is not advertised and skips the command. +- WLAN API (`wlanapi.lib`) and COM (`ole32.lib` for `StringFromGUID2`) are available on + the host. diff --git a/network/wlan/wificx/drivercode/SharedTypes.h b/network/wlan/wificx/drivercode/SharedTypes.h index 43fd815a..e454378b 100644 --- a/network/wlan/wificx/drivercode/SharedTypes.h +++ b/network/wlan/wificx/drivercode/SharedTypes.h @@ -13,4 +13,19 @@ // ============================= // {bb67559a-06f6-4eb0-81e9-21fdc3b60efb} DEFINE_GUID(GUID_WIFICX_SAMPLE_CLIENT_INTERFACE, 0xbb67559a, 0x06f6, 0x4eb0, 0x81, 0xe9, 0x21, 0xfd, 0xc3, 0xb6, 0x0e, 0xfb); -#define WIFI_DRIVER_DEFAULT_POOL_TAG 'shiW' // WIFI IHV Sample Driver
\ No newline at end of file +#define WIFI_DRIVER_DEFAULT_POOL_TAG 'shiW' // WIFI IHV Sample Driver + +// ============================= +// OEM Device Service contract +// ============================= +// This GUID/opcode pair MUST match the OEM user-mode app (OEM\OemDeviceService.cpp). +// {2d6f9a14-3a1d-4f0a-9b7e-1c2e3a4b5c6d} +DEFINE_GUID(GUID_OEM_SAMPLE_DEVICE_SERVICE, + 0x2d6f9a14, 0x3a1d, 0x4f0a, 0x9b, 0x7e, 0x1c, 0x2e, 0x3a, 0x4b, 0x5c, 0x6d); + +// Opcode understood by the driver for the "hello / nice to meet you" exchange. +#define OEM_DEVICE_SERVICE_OPCODE_HELLO 0x00000001 + +// Payload strings exchanged with the OEM app. +#define OEM_DEVICE_SERVICE_REQUEST_STRING "Hello, My Driver" +#define OEM_DEVICE_SERVICE_RESPONSE_STRING "Nice to meet you, My OEM"
\ No newline at end of file diff --git a/network/wlan/wificx/drivercode/wifihal.cpp b/network/wlan/wificx/drivercode/wifihal.cpp index fead61b3..e7aaec1f 100644 --- a/network/wlan/wificx/drivercode/wifihal.cpp +++ b/network/wlan/wificx/drivercode/wifihal.cpp @@ -982,3 +982,137 @@ NTSTATUS WifiHAL::WifiIhvDisconnect(const WDI_TASK_DISCONNECT_PARAMETERS&, const return STATUS_SUCCESS; } + +// -------- WDI_GET_SUPPORTED_DEVICE_SERVICES (OID_WDI_GET_SUPPORTED_DEVICES) -------- +// Property GET: the request has no input (Inputs is empty). Builds the +// WDI_TLV_DEVICE_SERVICE_GUID_LIST result advertising GUID_OEM_SAMPLE_DEVICE_SERVICE and +// serializes it via the generated TLV generator, so the OS learns which device services +// this driver supports. +// OutBuffer receives the full WDI message (header + TLVs); BytesWritten = total length. +_Use_decl_annotations_ +NTSTATUS WifiHAL::WifiIhvGetSupportedDeviceServices(const WDI_GET_SUPPORTED_DEVICE_SERVICES_INPUTS& Inputs, void* OutBuffer, ULONG OutBufferLen, ULONG& BytesWritten) +{ + UNREFERENCED_PARAMETER(Inputs); // GET request carries no input data + + BytesWritten = sizeof(WDI_MESSAGE_HEADER); + + if (OutBuffer == nullptr || OutBufferLen < sizeof(WDI_MESSAGE_HEADER)) + { + WFCError("GetSupportedDeviceServices: invalid out buffer (OutBufferLen=%u)", OutBufferLen); + return STATUS_INVALID_PARAMETER; + } + + // WDI_TLV_DEVICE_SERVICE_GUID_LIST: a list containing our single device service GUID. + // WDI_GUID_LIST_CONTAINER is ArrayOfElements<GUID>; SimpleAssign points it at our + // stack array (the generator copies the data while serializing the TLV). + GUID supportedServices[] = { GUID_OEM_SAMPLE_DEVICE_SERVICE }; + + WDI_GET_SUPPORTED_DEVICE_SERVICES_PARAMETERS results{}; + results.DeviceServiceGUIDList.SimpleAssign(supportedServices, ARRAYSIZE(supportedServices)); + + // Generate the TLV byte stream. ReservedHeaderLength reserves room for the + // WDI_MESSAGE_HEADER at the front of the produced buffer. + ULONG generatedLength = 0; + UINT8* pGenerated = nullptr; + + NDIS_STATUS genStatus = GenerateWdiGetSupportedDeviceServices( + &results, sizeof(WDI_MESSAGE_HEADER), m_TlvContext, &generatedLength, &pGenerated); + + NTSTATUS ntStatus = Wifi::ConvertNDISSTATUSToNTSTATUS(genStatus); + if (!NT_SUCCESS(ntStatus) || pGenerated == nullptr) + { + WFCError("GetSupportedDeviceServices: Generate failed, status=%!STATUS!", ntStatus); + return ntStatus; + } + + if (OutBufferLen < generatedLength) + { + WFCError("GetSupportedDeviceServices: out buffer too small (have=%u need=%u)", + OutBufferLen, generatedLength); + FreeGenerated(pGenerated); + return STATUS_BUFFER_TOO_SMALL; + } + + RtlCopyMemory(OutBuffer, pGenerated, generatedLength); + BytesWritten = generatedLength; + FreeGenerated(pGenerated); + + WFCInfo("GetSupportedDeviceServices: advertised %u device service(s), %u bytes", + ARRAYSIZE(supportedServices), BytesWritten); + return STATUS_SUCCESS; +} + +// -------- OEM Device Service Command (OID_WDI_DEVICE_SERVICE_COMMAND) -------- +// Reads the request data blob (WDI_TLV_DEVICE_SERVICE_PARAMS_DATA_BLOB) parsed into +// Inputs.Params, expects "Hello, My Driver", and returns "Nice to meet you, My OEM" +// as the response data blob, serialized via the generated TLV generator. +// OutBuffer receives the full WDI message (header + TLVs); BytesWritten = total length. +_Use_decl_annotations_ +NTSTATUS WifiHAL::WifiIhvDeviceServiceCommand(const WDI_DEVICE_SERVICE_COMMAND_INPUTS& Inputs, void* OutBuffer, ULONG OutBufferLen, ULONG& BytesWritten) +{ + BytesWritten = sizeof(WDI_MESSAGE_HEADER); + + if (OutBuffer == nullptr || OutBufferLen < sizeof(WDI_MESSAGE_HEADER)) + { + WFCError("OEM device service: invalid out buffer (OutBufferLen=%u)", OutBufferLen); + return STATUS_INVALID_PARAMETER; + } + + // Log the request data blob ("Hello, My Driver"), if present. + if (Inputs.Optional.Params_IsPresent && + Inputs.Params.ElementCount > 0 && + Inputs.Params.pElements[0].ElementCount > 0 && + Inputs.Params.pElements[0].pElements != nullptr) + { + WFCInfo("OEM device service: opcode=0x%08X, received %u-byte data blob: %hs", + Inputs.Opcode, + Inputs.Params.pElements[0].ElementCount, + reinterpret_cast<const char*>(Inputs.Params.pElements[0].pElements)); + } + else + { + WFCInfo("OEM device service: opcode=0x%08X, no input data blob", Inputs.Opcode); + } + + // Build the response data blob ("Nice to meet you, My OEM"), including the null terminator. + // SimpleAssign points the blob at this buffer (no copy); the generator copies the bytes + // while serializing, and the buffer outlives that call. + UINT8 responseBytes[] = OEM_DEVICE_SERVICE_RESPONSE_STRING; + + WDI_BYTE_BLOB responseBlob{}; + responseBlob.SimpleAssign(responseBytes, static_cast<UINT32>(sizeof(responseBytes))); + + WDI_DEVICE_SERVICE_COMMAND_PARAMETERS params{}; + params.Optional.Params_IsPresent = TRUE; + params.Params.SimpleAssign(&responseBlob, 1); + + // Serialize WDI_TLV_DEVICE_SERVICE_PARAMS_DATA_BLOB into the response message. + ULONG generatedLength = 0; + UINT8* pGenerated = nullptr; + + NDIS_STATUS genStatus = GenerateWdiDeviceServiceCommand( + ¶ms, sizeof(WDI_MESSAGE_HEADER), m_TlvContext, &generatedLength, &pGenerated); + + NTSTATUS ntStatus = Wifi::ConvertNDISSTATUSToNTSTATUS(genStatus); + if (!NT_SUCCESS(ntStatus) || pGenerated == nullptr) + { + WFCError("OEM device service: Generate failed, status=%!STATUS!", ntStatus); + return ntStatus; + } + + if (OutBufferLen < generatedLength) + { + WFCError("OEM device service: out buffer too small (have=%u need=%u)", + OutBufferLen, generatedLength); + FreeGenerated(pGenerated); + return STATUS_BUFFER_TOO_SMALL; + } + + RtlCopyMemory(OutBuffer, pGenerated, generatedLength); + BytesWritten = generatedLength; + FreeGenerated(pGenerated); + + WFCInfo("OEM device service: responded with \"%hs\" (%u bytes)", + reinterpret_cast<const char*>(responseBytes), BytesWritten); + return STATUS_SUCCESS; +} diff --git a/network/wlan/wificx/drivercode/wifihal.h b/network/wlan/wificx/drivercode/wifihal.h index 38e1166a..f776f425 100644 --- a/network/wlan/wificx/drivercode/wifihal.h +++ b/network/wlan/wificx/drivercode/wifihal.h @@ -28,6 +28,21 @@ public: NTSTATUS WifiIhvConnect(_In_ const WDI_TASK_CONNECT_PARAMETERS& ConnectParameters, _In_ const PWDI_MESSAGE_HEADER pWdiHeader, _In_ UINT BytesWritten); NTSTATUS WifiIhvSetSaeAuthParams(_In_ const WDI_SET_SAE_AUTH_PARAMS_COMMAND& setSAEAuthParams, _In_ const PWDI_MESSAGE_HEADER pWdiHeader, _In_ UINT BytesWritten); NTSTATUS WifiIhvDisconnect(_In_ const WDI_TASK_DISCONNECT_PARAMETERS& disconnectParameters, _In_ const PWDI_MESSAGE_HEADER pWdiHeader, _In_ UINT BytesWritten); + + // Device service property handlers (OID_WDI_GET_SUPPORTED_DEVICES / OID_WDI_DEVICE_SERVICE_COMMAND). + // Match the PropertyTransitionTraits handler shape: (const parsed input&, out-buffer, + // out-buffer length, bytesWritten&). The handler serializes the response TLV stream into + // OutBuffer and reports the number of bytes written; the dispatch layer completes the request. + NTSTATUS WifiIhvGetSupportedDeviceServices( + _In_ const WDI_GET_SUPPORTED_DEVICE_SERVICES_INPUTS& Inputs, + _Out_writes_bytes_to_(OutBufferLen, BytesWritten) void* OutBuffer, + _In_ ULONG OutBufferLen, + _Out_ ULONG& BytesWritten); + NTSTATUS WifiIhvDeviceServiceCommand( + _In_ const WDI_DEVICE_SERVICE_COMMAND_INPUTS& Inputs, + _Out_writes_bytes_to_(OutBufferLen, BytesWritten) void* OutBuffer, + _In_ ULONG OutBufferLen, + _Out_ ULONG& BytesWritten); private: NTSTATUS WifiIhvPerformAssociation(_In_ const struct ArrayOfElements<WDI_CONNECT_BSS_ENTRY_CONTAINER>* pPreferredBSSEntryList, _In_ const struct ArrayOfElements<WDI_AUTH_ALGORITHM>* pAuthenticationAlgorithms, _In_ const PWDI_MESSAGE_HEADER pWdiHeader); NTSTATUS WifiIhvSendLinkStateIndication(_In_ const PWDI_MESSAGE_HEADER pWdiHeader, ULONG numLinks); diff --git a/network/wlan/wificx/drivercode/wifirequest.cpp b/network/wlan/wificx/drivercode/wifirequest.cpp index fd02bd91..c84c1d97 100644 --- a/network/wlan/wificx/drivercode/wifirequest.cpp +++ b/network/wlan/wificx/drivercode/wifirequest.cpp @@ -95,4 +95,4 @@ _Use_decl_annotations_ void WifiIhvSendM4IndicationToOs(WDFDEVICE Device, UINT16 WifiRequestMessageId, const PWDI_MESSAGE_HEADER pWdiHeader, NTSTATUS WifiRequestM4Status) { WifiIhvSendIndicationToOs(Device, pWdiHeader, WifiRequestMessageId, pWdiHeader->TransactionId, WifiRequestM4Status, nullptr, 0); -}
\ No newline at end of file +} diff --git a/network/wlan/wificx/drivercode/wifitransition.cpp b/network/wlan/wificx/drivercode/wifitransition.cpp index 41106932..2e1b7ef2 100644 --- a/network/wlan/wificx/drivercode/wifitransition.cpp +++ b/network/wlan/wificx/drivercode/wifitransition.cpp @@ -192,6 +192,98 @@ NTSTATUS RunTransition(TransitionContext& ctx) return m4Status; } +// ============================================================================ +// Property GET/SET messages (single M3 completion) +// ---------------------------------------------------------------------------- +// Unlike the M3/M4 task flow above, a property GET/SET is a single M3 step that +// returns its result synchronously in the request's in/out buffer. The HAL +// handler writes the response TLV stream and reports the real number of bytes +// written (by reference); the request is completed (M3) with that length. +// No M4 indication. +// ============================================================================ +template< + UINT16 TMsgId, + typename TParam, + bool TDumpTlvStream, + NDIS_STATUS (*TParseFn)(ULONG, const UINT8*, PCTLV_CONTEXT, TParam*), + void (*TCleanupFn)(TParam*), + NTSTATUS (WifiHAL::*TPreFn)(), // optional pre-check hook (may be nullptr) + NTSTATUS (WifiHAL::*TPropertyFn)(const TParam&, void*, ULONG, ULONG&) // mandatory property HAL handler +> +struct PropertyM3Traits +{ + using ParamType = TParam; + + NTSTATUS Parse(TransitionContext& ctx, ParamType& p) + { + if (ctx.InLen < sizeof(WDI_MESSAGE_HEADER)) + { + return STATUS_INVALID_PARAMETER; + } + + auto* tlvBytes = static_cast<UCHAR*>(ctx.RawBuffer) + sizeof(WDI_MESSAGE_HEADER); + auto tlvLen = static_cast<ULONG>(ctx.InLen - sizeof(WDI_MESSAGE_HEADER)); + + if (TDumpTlvStream) + { + DumpMessageTlvByteStream(TMsgId, TRUE, ctx.DevCtx->TlvContext.PeerVersion, tlvLen, tlvBytes, 0, nullptr); + } + + auto ndisStatus = TParseFn(tlvLen, tlvBytes, &ctx.DevCtx->TlvContext, &p); + return Wifi::ConvertNDISSTATUSToNTSTATUS(ndisStatus); + } + + void Cleanup(ParamType& p) { TCleanupFn(&p); } + + // Runs the optional pre-check then the property handler. The handler writes the + // out-buffer and reports the number of bytes written. + NTSTATUS Handle(TransitionContext& ctx, ParamType& p, ULONG& bytesWritten) + { + bytesWritten = sizeof(WDI_MESSAGE_HEADER); + + WifiHAL* hal = GetWifiHalFromHandle(ctx.Device); + + if (TPreFn) + { + NTSTATUS preStatus = (hal->*TPreFn)(); + if (!NT_SUCCESS(preStatus)) + { + return preStatus; + } + } + + return (hal->*TPropertyFn)(p, ctx.RawBuffer, ctx.OutLen, bytesWritten); + } +}; + +// Primary traits template for property GET/SET messages (specialize per MessageId) +template<UINT16 MsgId> +struct PropertyTraits; + +// Generic runner for property GET/SET messages: parse -> handle -> complete (M3). +// Completes the request synchronously with the number of bytes the HAL wrote. +template<UINT16 MsgId> +NTSTATUS RunPropertyM3(TransitionContext& ctx) +{ + PropertyTraits<MsgId> traits; + typename PropertyTraits<MsgId>::ParamType params{}; + + NTSTATUS status = traits.Parse(ctx, params); + if (!NT_SUCCESS(status)) + { + traits.Cleanup(params); + WifiRequestComplete(ctx.WifiRequest, status, sizeof(WDI_MESSAGE_HEADER)); + return status; + } + + ULONG bytesWritten = sizeof(WDI_MESSAGE_HEADER); + status = traits.Handle(ctx, params, bytesWritten); + + traits.Cleanup(params); + WifiRequestComplete(ctx.WifiRequest, status, bytesWritten); + return status; +} + //// -------- SCENARIO: [Connect with a SAE WI-FI7 network -------- /// Demo: Handle WDI_TASK_CONNECT + WDI_SET_SAE_AUTH_PARAMS then WDI_TASK_DISCONNECT /// Scope: @@ -313,6 +405,38 @@ struct TransitionTraits<WDI_TASK_SET_RADIO_STATE> { }; +// -------- WDI_GET_SUPPORTED_DEVICE_SERVICES (property GET) -------- +// Request body is empty (header sufficient); the HAL produces the +// WDI_TLV_DEVICE_SERVICE_GUID_LIST result into the out-buffer. +template<> +struct PropertyTraits<WDI_GET_SUPPORTED_DEVICE_SERVICES> + : PropertyM3Traits< + WDI_GET_SUPPORTED_DEVICE_SERVICES, + WDI_GET_SUPPORTED_DEVICE_SERVICES_INPUTS, + true, // dump TLV stream + ParseWdiGetSupportedDeviceServices, + CleanupParsedWdiGetSupportedDeviceServices, + &WifiHAL::WifiIhvIsDeviceReadyForRequest, // pre-check + &WifiHAL::WifiIhvGetSupportedDeviceServices // property handler + > +{}; + +// -------- WDI_DEVICE_SERVICE_COMMAND (property SET/GET) -------- +// Reads the request data blob (WDI_TLV_DEVICE_SERVICE_PARAMS_*) and the HAL writes +// the response data blob into the out-buffer. +template<> +struct PropertyTraits<WDI_DEVICE_SERVICE_COMMAND> + : PropertyM3Traits< + WDI_DEVICE_SERVICE_COMMAND, + WDI_DEVICE_SERVICE_COMMAND_INPUTS, + true, // dump TLV stream + ParseWdiDeviceServiceCommand, + CleanupParsedWdiDeviceServiceCommand, + &WifiHAL::WifiIhvIsDeviceReadyForRequest, // pre-check + &WifiHAL::WifiIhvDeviceServiceCommand // property handler + > +{}; + // Runtime dispatcher switches on MessageId and invokes the matching compile-time runner. NTSTATUS RunTransitionByMessage(TransitionContext& ctx, UINT16 messageId) { @@ -330,6 +454,10 @@ NTSTATUS RunTransitionByMessage(TransitionContext& ctx, UINT16 messageId) return RunTransition<WDI_TASK_DISCONNECT>(ctx); case WDI_SET_SAE_AUTH_PARAMS: return RunTransition<WDI_SET_SAE_AUTH_PARAMS>(ctx); + case WDI_GET_SUPPORTED_DEVICE_SERVICES: + return RunPropertyM3<WDI_GET_SUPPORTED_DEVICE_SERVICES>(ctx); + case WDI_DEVICE_SERVICE_COMMAND: + return RunPropertyM3<WDI_DEVICE_SERVICE_COMMAND>(ctx); default: UINT bytesWritten = sizeof(WDI_MESSAGE_HEADER); WifiRequestComplete(ctx.WifiRequest, STATUS_NOT_SUPPORTED, bytesWritten); diff --git a/network/wlan/wificx/drivercode/wifitransition.h b/network/wlan/wificx/drivercode/wifitransition.h index a0addf88..c48d4a78 100644 --- a/network/wlan/wificx/drivercode/wifitransition.h +++ b/network/wlan/wificx/drivercode/wifitransition.h @@ -11,7 +11,7 @@ struct TransitionContext PWIFI_IHV_DEVICE_CONTEXT DevCtx; WIFIREQUEST WifiRequest; PWDI_MESSAGE_HEADER Header; - void* RawBuffer; + void* RawBuffer; // from WifiRequestGetInOutBuffer in EvtWifiDeviceSendCommand UINT InLen; UINT OutLen; }; diff --git a/network/wlan/wificx/wificxsampleclient.sln b/network/wlan/wificx/wificxsampleclient.sln index d484d08c..ddcf190c 100644 --- a/network/wlan/wificx/wificxsampleclient.sln +++ b/network/wlan/wificx/wificxsampleclient.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.7.34221.43 +# Visual Studio Version 18 +VisualStudioVersion = 18.7.11903.348 stable MinimumVisualStudioVersion = 12.0 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wificxsampleclientkm", "km\wificxsampleclientkm.vcxproj", "{272D3E7B-C7BA-66D1-E05D-B9723A6F0777}" ProjectSection(ProjectDependencies) = postProject @@ -16,6 +16,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netvadapterlibrarykm", "..\ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netvadapterlibraryum", "..\..\netadaptercx\netvadapterlibrary\um\netvadapterlibraryum.vcxproj", "{612F33AD-430C-4FE7-8000-35E15A5EB757}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OemDeviceService", "OEM\OemDeviceService.vcxproj", "{B3C9A1E2-7F4D-4B2A-9C5E-1A2B3C4D5E6F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -72,6 +74,14 @@ Global {612F33AD-430C-4FE7-8000-35E15A5EB757}.Release|x64.ActiveCfg = Release|x64 {612F33AD-430C-4FE7-8000-35E15A5EB757}.Release|x64.Build.0 = Release|x64 {612F33AD-430C-4FE7-8000-35E15A5EB757}.Release|x64.Deploy.0 = Release|x64 + {B3C9A1E2-7F4D-4B2A-9C5E-1A2B3C4D5E6F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B3C9A1E2-7F4D-4B2A-9C5E-1A2B3C4D5E6F}.Debug|ARM64.Build.0 = Debug|ARM64 + {B3C9A1E2-7F4D-4B2A-9C5E-1A2B3C4D5E6F}.Debug|x64.ActiveCfg = Debug|x64 + {B3C9A1E2-7F4D-4B2A-9C5E-1A2B3C4D5E6F}.Debug|x64.Build.0 = Debug|x64 + {B3C9A1E2-7F4D-4B2A-9C5E-1A2B3C4D5E6F}.Release|ARM64.ActiveCfg = Release|ARM64 + {B3C9A1E2-7F4D-4B2A-9C5E-1A2B3C4D5E6F}.Release|ARM64.Build.0 = Release|ARM64 + {B3C9A1E2-7F4D-4B2A-9C5E-1A2B3C4D5E6F}.Release|x64.ActiveCfg = Release|x64 + {B3C9A1E2-7F4D-4B2A-9C5E-1A2B3C4D5E6F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE |
