summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYang You (UU) <[email protected]>2026-06-17 10:33:03 -0700
committerYang You (UU) <[email protected]>2026-06-17 10:33:03 -0700
commit4c89b2cd0cf2e94bc2e933e9ccf7d35c7b26a140 (patch)
tree6e8201e56ede0e2c92489b1c7ce0230c90329295
parent52f73736b407acb1f8a0af513c9614a5354cea5b (diff)
check in the demo for OEM solution based on Service Command channel.
-rw-r--r--network/wlan/wificx/OEM/OemDeviceService.vcxproj50
-rw-r--r--network/wlan/wificx/OEM/OemDeviceServiceApp.cpp158
-rw-r--r--network/wlan/wificx/OEM/README.md90
-rw-r--r--network/wlan/wificx/drivercode/SharedTypes.h17
-rw-r--r--network/wlan/wificx/drivercode/wifihal.cpp134
-rw-r--r--network/wlan/wificx/drivercode/wifihal.h15
-rw-r--r--network/wlan/wificx/drivercode/wifirequest.cpp2
-rw-r--r--network/wlan/wificx/drivercode/wifitransition.cpp128
-rw-r--r--network/wlan/wificx/drivercode/wifitransition.h2
-rw-r--r--network/wlan/wificx/wificxsampleclient.sln14
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(
+ &params, 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