1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
|
// Copyright (C) Microsoft Corporation. All rights reserved.
#include "wifitransition.h"
// Common TLV parsing helper: computes TLV span, optional dump, calls a parser, converts status.
template<typename Param, typename ParserFn>
NTSTATUS ParseTlvCommon(TransitionContext& ctx,
UINT16 messageId,
ParserFn parser,
Param& outParams,
bool dumpStream = true)
{
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 (dumpStream)
{
DumpMessageTlvByteStream(
messageId,
TRUE,
ctx.DevCtx->TlvContext.PeerVersion,
tlvLen,
tlvBytes,
0,
nullptr);
}
auto ndisStatus = parser(tlvLen, tlvBytes, &ctx.DevCtx->TlvContext, &outParams);
return Wifi::ConvertNDISSTATUSToNTSTATUS(ndisStatus);
}
// Primary traits template (specialize per MessageId)
template<UINT16 MsgId>
struct TransitionTraits;
// --- Generic pure-type traits template (add before existing specializations) ---
// WIFIREQUEST always needs M3 notification, so TPreM3Fn is mandatory.
// and WIFICX expectes the M3 then M4 order, so we always execute M3 then M4.
// using template parameters to configure parsing, cleanup, M3/M4 steps.
// to make sure that all transitions have consistent implementations.
template<
UINT16 TMsgId,
typename TParam,
UINT16 TCompleteIndication,
bool TDumpTlvStream,
NDIS_STATUS (*TParseFn)(ULONG, const UINT8*, PCTLV_CONTEXT, TParam*),
void (*TCleanupFn)(TParam*),
NTSTATUS (WifiHAL::*TPreM3Fn)(), // mandatory pre-M3 hook
NTSTATUS (WifiHAL::*THalM3Fn)(const TParam&, const PWDI_MESSAGE_HEADER, UINT BytesWriten), // optional HAL M3 (may be nullptr)
NTSTATUS (WifiHAL::*TPreM4Fn)(), // optional pre-M4 hook (may be nullptr)
NTSTATUS (WifiHAL::*THalM4Fn)(const PWDI_MESSAGE_HEADER) // optional HAL M4 (may be nullptr)
>
struct GenericTransitionTraits
{
using ParamType = TParam;
enum : UINT16 { CompleteIndication = TCompleteIndication };
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); }
// Make static so pointer matches ExecuteSteps expected callable type (no implicit this)
static NTSTATUS StepM3(TransitionContext& c, ParamType& p, UINT& bytesWritten)
{
bytesWritten = sizeof(WDI_MESSAGE_HEADER);
ASSERT(TPreM3Fn);
if (TPreM3Fn)
{
// Call member function pointer on WifiHAL instance
NTSTATUS preStatus = (GetWifiHalFromHandle(c.DevCtx->WdfDevice)->*TPreM3Fn)();
if (!NT_SUCCESS(preStatus))
{
return preStatus;
}
}
if (THalM3Fn)
{
// Pass required third argument (BytesWriten) to HAL M3 function
return (GetWifiHalFromHandle(c.DevCtx->WdfDevice)->*THalM3Fn)(p, c.Header, bytesWritten);
}
return STATUS_SUCCESS;
}
static NTSTATUS StepM4(TransitionContext& c, ParamType&)
{
if (TPreM4Fn)
{
NTSTATUS preStatus = (GetWifiHalFromHandle(c.DevCtx->WdfDevice)->*TPreM4Fn)();
if (!NT_SUCCESS(preStatus))
{
return preStatus;
}
}
if (THalM4Fn)
{
return (GetWifiHalFromHandle(c.DevCtx->WdfDevice)->*THalM4Fn)(c.Header);
}
return (TPreM4Fn == nullptr && THalM4Fn == nullptr) ? STATUS_PENDING : STATUS_SUCCESS;
}
NTSTATUS Handle(TransitionContext& ctx, ParamType& p)
{
return ExecuteSteps(ctx, p, &GenericTransitionTraits::StepM3, &GenericTransitionTraits::StepM4);
}
bool ShouldSendComplete(NTSTATUS s) const { return s != STATUS_PENDING; }
};
// Execute two step callables.
// StepM3Fn signature: NTSTATUS (TransitionContext&, Param&, UINT& bytesWritten)
// StepM4Fn signature: NTSTATUS (TransitionContext&, Param&)
// Always calls WifiRequestComplete after StepM3 with the bytesWritten produced by StepM3.
// Skips StepM4 if StepM3 failed
template<typename Param, typename StepM3Fn, typename StepM4Fn>
NTSTATUS ExecuteSteps(TransitionContext& ctx, Param& p, StepM3Fn stepM3, StepM4Fn stepM4)
{
UINT bytesWritten = sizeof(WDI_MESSAGE_HEADER); // default minimum
NTSTATUS m3Status = stepM3(ctx, p, bytesWritten);
// Report the M3 status back to OS
// OS expects M3 before the M4
WifiIhvNotifyM3Completion(ctx.WifiRequest, m3Status, bytesWritten);
if (!NT_SUCCESS(m3Status))
{
return m3Status;
}
return stepM4(ctx, p);
}
// -------- Generic runner (compile-time) --------
template<UINT16 MsgId>
NTSTATUS RunTransition(TransitionContext& ctx)
{
TransitionTraits<MsgId> traits;
typename TransitionTraits<MsgId>::ParamType params{};
NTSTATUS parseStatus = traits.Parse(ctx, params);
if (!NT_SUCCESS(parseStatus))
{
traits.Cleanup(params);
// Report Failed M3 to OS
// Note: No M4 indication on parse failure
WifiIhvNotifyM3Completion(ctx.WifiRequest, parseStatus, 0);
return parseStatus;
}
NTSTATUS m4Status = traits.Handle(ctx, params);
if (traits.ShouldSendComplete(m4Status))
{
WifiIhvSendM4IndicationToOs(
ctx.Device,
TransitionTraits<MsgId>::CompleteIndication,
ctx.Header,
m4Status);
}
traits.Cleanup(params);
return m4Status;
}
//// -------- SCENARIO: [Connect with a SAE WI-FI7 network --------
/// Demo: Handle WDI_TASK_CONNECT + WDI_SET_SAE_AUTH_PARAMS then WDI_TASK_DISCONNECT
/// Scope:
/// -WifiRequest WDI_TASK_CONNECT & WDI_TASK_DISCONNECT are both WIFICX task commands, which is a two step M3/M4 transition
/// -The direct WifiRequest WDI_SET_SAE_AUTH_PARAMS, which is a single step transition but
/// is logically part of the connect scenario. since WDI_SET_SAE_AUTH_PARAMS is WIFICX property command,
/// it only has M3 step, no M4 step.
/// - The WifiCx unsolicited indication e.g. WDI_INDICATION_SAE_AUTH_PARAMS_NEEDED is sent from the HAL during the connect process,
/// Notes:
/// - M3 and M4 status mainly used for WifiCx to track progress of the transition. e.g. the hung detection and trigger recovery.
/// - The actual scenario result is reported through unsolicited indication.
///
// -------- WDI_TASK_CONNECT --------
template<>
struct TransitionTraits<WDI_TASK_CONNECT>
: GenericTransitionTraits <
WDI_TASK_CONNECT,
WDI_TASK_CONNECT_PARAMETERS,
WDI_INDICATION_CONNECT_COMPLETE,
true, // dump TLV stream? (was true in original)
ParseWdiTaskConnect,
CleanupParsedWdiTaskConnect,
&WifiHAL::WifiIhvIsDeviceReadyForRequest, // pre-M3
&WifiHAL::WifiIhvConnect, // HAL M3
&WifiHAL::WifiIhvGetPendingTransitionStatus, // pre-M4
nullptr
>
{
};
// --- WDI_SET_SAE_AUTH_PARAMS ---
template<>
struct TransitionTraits<WDI_SET_SAE_AUTH_PARAMS>
: GenericTransitionTraits<
WDI_SET_SAE_AUTH_PARAMS,
WDI_SET_SAE_AUTH_PARAMS_COMMAND,
WDI_INDICATION_CONNECT_COMPLETE,
false, // dump TLV stream? (was false in original)
ParseWdiSetSaeAuthParams,
CleanupParsedWdiSetSaeAuthParams,
&WifiHAL::WifiIhvIsDeviceReadyForRequest, // pre-M3
&WifiHAL::WifiIhvSetSaeAuthParams, // HAL M3
&WifiHAL::WifiIhvGetPendingTransitionStatus, // pre-M4
nullptr// HAL M4
>
{};
// -------- WDI_TASK_DISCONNECT --------
template<>
struct TransitionTraits<WDI_TASK_DISCONNECT>
: GenericTransitionTraits<
WDI_TASK_DISCONNECT,
WDI_TASK_DISCONNECT_PARAMETERS,
WDI_INDICATION_DISCONNECT_COMPLETE,
false, // dump TLV stream? (was false in original)
ParseWdiTaskDisconnect,
CleanupParsedWdiTaskDisconnect,
&WifiHAL::WifiIhvIsDeviceReadyForRequest, // pre-M3
&WifiHAL::WifiIhvDisconnect, // HAL M3
&WifiHAL::WifiIhvGetPendingTransitionStatus, // pre-M4
nullptr // HAL M4
>
{
};
/// ----- End of scenario [Connect with a SAE WI-FI7 network]-----
// -------- WDI_TASK_DOT11_RESET --------
template<>
struct TransitionTraits<WDI_TASK_DOT11_RESET>
: GenericTransitionTraits <
WDI_TASK_DOT11_RESET,
WDI_TASK_DOT11_RESET_PARAMETERS,
WDI_INDICATION_DOT11_RESET_COMPLETE,
false, // dump TLV stream? (was false in original)
ParseWdiTaskDot11Reset,
CleanupParsedWdiTaskDot11Reset,
&WifiHAL::WifiIhvIsDeviceReadyForRequest, // pre-M3
&WifiHAL::WifiIhvReset, // HAL M3
&WifiHAL::WifiIhvGetPendingTransitionStatus, // pre-M4
nullptr // HAL M4
>
{
};
// -------- WDI_TASK_SCAN --------
template<>
struct TransitionTraits<WDI_TASK_SCAN>
: GenericTransitionTraits <
WDI_TASK_SCAN,
WDI_SCAN_PARAMETERS,
WDI_INDICATION_SCAN_COMPLETE,
true, // dump TLV stream? (was true in original)
ParseWdiTaskScan,
CleanupParsedWdiTaskScan,
&WifiHAL::WifiIhvIsDeviceReadyForRequest, // pre-M3
&WifiHAL::WifiIhvScan, // HAL M3
&WifiHAL::WifiIhvGetPendingTransitionStatus, // pre-M4
nullptr // HAL M4
>
{
};
// -------- WDI_TASK_SET_RADIO_STATE --------
template<>
struct TransitionTraits<WDI_TASK_SET_RADIO_STATE>
: GenericTransitionTraits <
WDI_TASK_SET_RADIO_STATE,
WDI_SET_RADIO_STATE_PARAMETERS,
WDI_INDICATION_SET_RADIO_STATE_COMPLETE,
true, // dump TLV stream? (was true in original)
ParseWdiTaskSetRadioState,
CleanupParsedWdiTaskSetRadioState,
&WifiHAL::WifiIhvIsDeviceReadyForRequest, // pre-M3
&WifiHAL::WifiIhvSetRadioState, // HAL M3
&WifiHAL::WifiIhvGetPendingTransitionStatus, // pre-M4
nullptr // HAL M4
>
{
};
// Runtime dispatcher switches on MessageId and invokes the matching compile-time runner.
NTSTATUS RunTransitionByMessage(TransitionContext& ctx, UINT16 messageId)
{
switch (messageId)
{
case WDI_TASK_SET_RADIO_STATE:
return RunTransition<WDI_TASK_SET_RADIO_STATE>(ctx);
case WDI_TASK_SCAN:
return RunTransition<WDI_TASK_SCAN>(ctx);
case WDI_TASK_DOT11_RESET:
return RunTransition<WDI_TASK_DOT11_RESET>(ctx);
case WDI_TASK_CONNECT:
return RunTransition<WDI_TASK_CONNECT>(ctx);
case WDI_TASK_DISCONNECT:
return RunTransition<WDI_TASK_DISCONNECT>(ctx);
case WDI_SET_SAE_AUTH_PARAMS:
return RunTransition<WDI_SET_SAE_AUTH_PARAMS>(ctx);
default:
UINT bytesWritten = sizeof(WDI_MESSAGE_HEADER);
WifiRequestComplete(ctx.WifiRequest, STATUS_NOT_SUPPORTED, bytesWritten);
return STATUS_NOT_SUPPORTED;
}
}
|