2
* ndis_events - Receive NdisMIndicateStatus() events using WMI
3
* Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License version 2 as
7
* published by the Free Software Foundation.
9
* Alternatively, this software may be distributed under the terms of BSD
12
* See README and COPYING for more details.
15
#define _WIN32_WINNT 0x0400
21
#endif /* COBJMACROS */
27
static int wmi_refcnt = 0;
28
static int wmi_first = 1;
30
struct ndis_events_data {
32
IWbemObjectSinkVtbl sink_vtbl;
37
HANDLE read_pipe, write_pipe, event_avail;
40
char *ifname; /* {GUID..} */
44
#define BstrAlloc(x) (x) ? SysAllocString(x) : NULL
45
#define BstrFree(x) if (x) SysFreeString(x)
47
/* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to
49
HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery(
50
IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
51
long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum)
53
BSTR bsQueryLanguage, bsQuery;
56
bsQueryLanguage = BstrAlloc(strQueryLanguage);
57
bsQuery = BstrAlloc(strQuery);
59
hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags,
62
BstrFree(bsQueryLanguage);
69
HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync(
70
IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
71
long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
73
BSTR bsQueryLanguage, bsQuery;
76
bsQueryLanguage = BstrAlloc(strQueryLanguage);
77
bsQuery = BstrAlloc(strQuery);
79
hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage,
80
bsQuery, lFlags, pCtx,
83
BstrFree(bsQueryLanguage);
90
HRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer(
91
IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser,
92
LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags,
93
LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace)
95
BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority;
98
bsNetworkResource = BstrAlloc(strNetworkResource);
99
bsUser = BstrAlloc(strUser);
100
bsPassword = BstrAlloc(strPassword);
101
bsLocale = BstrAlloc(strLocale);
102
bsAuthority = BstrAlloc(strAuthority);
104
hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser,
105
bsPassword, bsLocale, lSecurityFlags,
106
bsAuthority, pCtx, ppNamespace);
108
BstrFree(bsNetworkResource);
110
BstrFree(bsPassword);
112
BstrFree(bsAuthority);
118
enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
119
EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
121
static int ndis_events_get_adapter(struct ndis_events_data *events,
122
const char *ifname, const char *desc);
125
static int ndis_events_constructor(struct ndis_events_data *events)
129
if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) {
130
wpa_printf(MSG_ERROR, "CreatePipe() failed: %d",
131
(int) GetLastError());
134
events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
135
if (events->event_avail == NULL) {
136
wpa_printf(MSG_ERROR, "CreateEvent() failed: %d",
137
(int) GetLastError());
138
CloseHandle(events->read_pipe);
139
CloseHandle(events->write_pipe);
147
static void ndis_events_destructor(struct ndis_events_data *events)
149
CloseHandle(events->read_pipe);
150
CloseHandle(events->write_pipe);
151
CloseHandle(events->event_avail);
152
IWbemServices_Release(events->pSvc);
153
IWbemLocator_Release(events->pLoc);
154
if (--wmi_refcnt == 0)
159
static HRESULT STDMETHODCALLTYPE
160
ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj)
164
if (IsEqualIID(riid, &IID_IUnknown) ||
165
IsEqualIID(riid, &IID_IWbemObjectSink)) {
167
IWbemObjectSink_AddRef(this);
171
return E_NOINTERFACE;
175
static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this)
177
struct ndis_events_data *events = (struct ndis_events_data *) this;
178
return ++events->ref;
182
static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this)
184
struct ndis_events_data *events = (struct ndis_events_data *) this;
186
if (--events->ref != 0)
189
ndis_events_destructor(events);
190
wpa_printf(MSG_DEBUG, "ndis_events: terminated");
191
os_free(events->adapter_desc);
192
os_free(events->ifname);
198
static int ndis_events_send_event(struct ndis_events_data *events,
199
enum event_types type,
200
char *data, size_t data_len)
202
char buf[512], *pos, *end;
206
end = buf + sizeof(buf);
208
os_memcpy(buf, &_type, sizeof(_type));
209
pos = buf + sizeof(_type);
212
if (2 + data_len > (size_t) (end - pos)) {
213
wpa_printf(MSG_DEBUG, "Not enough room for send_event "
214
"data (%d)", data_len);
217
*pos++ = data_len >> 8;
218
*pos++ = data_len & 0xff;
219
os_memcpy(pos, data, data_len);
223
if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) {
224
SetEvent(events->event_avail);
227
wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError());
232
static void ndis_events_media_connect(struct ndis_events_data *events)
234
wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect");
235
ndis_events_send_event(events, EVENT_CONNECT, NULL, 0);
239
static void ndis_events_media_disconnect(struct ndis_events_data *events)
241
wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect");
242
ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0);
246
static void ndis_events_media_specific(struct ndis_events_data *events,
247
IWbemClassObject *pObj)
251
LONG lower, upper, k;
256
wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication");
258
/* This is the StatusBuffer from NdisMIndicateStatus() call */
259
hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication",
262
wpa_printf(MSG_DEBUG, "Could not get "
263
"NdisStatusMediaSpecificIndication from "
268
SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower);
269
SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper);
270
data_len = upper - lower + 1;
271
data = os_malloc(data_len);
273
wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event "
280
for (k = lower; k <= upper; k++) {
281
SafeArrayGetElement(V_ARRAY(&vt), &k, &ch);
284
wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", data, data_len);
288
ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len);
294
static void ndis_events_adapter_arrival(struct ndis_events_data *events)
296
wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival");
297
ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0);
301
static void ndis_events_adapter_removal(struct ndis_events_data *events)
303
wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval");
304
ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0);
308
static HRESULT STDMETHODCALLTYPE
309
ndis_events_indicate(IWbemObjectSink *this, long lObjectCount,
310
IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray)
312
struct ndis_events_data *events = (struct ndis_events_data *) this;
315
if (events->terminating) {
316
wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
317
"indication - terminating");
318
return WBEM_NO_ERROR;
320
/* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)",
323
for (i = 0; i < lObjectCount; i++) {
324
IWbemClassObject *pObj = ppObjArray[i];
328
hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL,
331
wpa_printf(MSG_DEBUG, "Failed to get __CLASS from "
335
/* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */
337
hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL,
340
wpa_printf(MSG_DEBUG, "Failed to get InstanceName "
342
VariantClear(&vtClass);
346
if (wcscmp(vtClass.bstrVal,
347
L"MSNdis_NotifyAdapterArrival") == 0) {
348
wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to "
349
"update adapter description since it may "
350
"have changed with new adapter instance");
351
ndis_events_get_adapter(events, events->ifname, NULL);
354
if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) {
355
wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
356
"indication for foreign adapter: "
357
"InstanceName: '%S' __CLASS: '%S'",
358
vt.bstrVal, vtClass.bstrVal);
359
VariantClear(&vtClass);
365
if (wcscmp(vtClass.bstrVal,
366
L"MSNdis_StatusMediaSpecificIndication") == 0) {
367
ndis_events_media_specific(events, pObj);
368
} else if (wcscmp(vtClass.bstrVal,
369
L"MSNdis_StatusMediaConnect") == 0) {
370
ndis_events_media_connect(events);
371
} else if (wcscmp(vtClass.bstrVal,
372
L"MSNdis_StatusMediaDisconnect") == 0) {
373
ndis_events_media_disconnect(events);
374
} else if (wcscmp(vtClass.bstrVal,
375
L"MSNdis_NotifyAdapterArrival") == 0) {
376
ndis_events_adapter_arrival(events);
377
} else if (wcscmp(vtClass.bstrVal,
378
L"MSNdis_NotifyAdapterRemoval") == 0) {
379
ndis_events_adapter_removal(events);
381
wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: "
382
"'%S'", vtClass.bstrVal);
385
VariantClear(&vtClass);
388
return WBEM_NO_ERROR;
392
static HRESULT STDMETHODCALLTYPE
393
ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
394
BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
396
return WBEM_NO_ERROR;
400
static int notification_query(IWbemObjectSink *pDestSink,
401
IWbemServices *pSvc, const char *class_name)
406
_snwprintf(query, 256,
407
L"SELECT * FROM %S", class_name);
408
wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
409
hr = call_IWbemServices_ExecNotificationQueryAsync(
410
pSvc, L"WQL", query, 0, 0, pDestSink);
412
wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
413
"failed with hresult of 0x%x",
414
class_name, (int) hr);
422
static int register_async_notification(IWbemObjectSink *pDestSink,
426
const char *class_list[] = {
427
"MSNdis_StatusMediaConnect",
428
"MSNdis_StatusMediaDisconnect",
429
"MSNdis_StatusMediaSpecificIndication",
430
"MSNdis_NotifyAdapterArrival",
431
"MSNdis_NotifyAdapterRemoval",
435
for (i = 0; class_list[i]; i++) {
436
if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
444
void ndis_events_deinit(struct ndis_events_data *events)
446
events->terminating = 1;
447
IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
448
IWbemObjectSink_Release(&events->sink);
450
* Rest of deinitialization is done in ndis_events_destructor() once
451
* all reference count drops to zero.
456
static int ndis_events_use_desc(struct ndis_events_data *events,
463
if (events->adapter_desc == NULL)
465
/* Continue using old description */
469
tmp = os_strdup(desc);
473
pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
477
len = os_strlen(tmp);
478
events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR));
479
if (events->adapter_desc == NULL) {
483
_snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
489
static int ndis_events_get_adapter(struct ndis_events_data *events,
490
const char *ifname, const char *desc)
494
#define MAX_QUERY_LEN 256
495
WCHAR query[MAX_QUERY_LEN];
496
IEnumWbemClassObject *pEnumerator;
497
IWbemClassObject *pObj;
503
* Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter
504
* to have better probability of matching with InstanceName from
505
* MSNdis events. If this fails, use the provided description.
508
os_free(events->adapter_desc);
509
events->adapter_desc = NULL;
511
hr = call_IWbemLocator_ConnectServer(
512
events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc);
514
wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI "
515
"server (ROOT\\CIMV2) - error 0x%x", (int) hr);
516
return ndis_events_use_desc(events, desc);
518
wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
520
_snwprintf(query, MAX_QUERY_LEN,
521
L"SELECT Index FROM Win32_NetworkAdapterConfiguration "
522
L"WHERE SettingID='%S'", ifname);
523
wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
525
hr = call_IWbemServices_ExecQuery(
527
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
529
if (!SUCCEEDED(hr)) {
530
wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
531
"GUID from Win32_NetworkAdapterConfiguration: "
533
IWbemServices_Release(pSvc);
534
return ndis_events_use_desc(events, desc);
538
hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
540
if (!SUCCEEDED(hr) || uReturned == 0) {
541
wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
542
"GUID from Win32_NetworkAdapterConfiguration: "
544
IEnumWbemClassObject_Release(pEnumerator);
545
IWbemServices_Release(pSvc);
546
return ndis_events_use_desc(events, desc);
548
IEnumWbemClassObject_Release(pEnumerator);
551
hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL);
552
if (!SUCCEEDED(hr)) {
553
wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from "
554
"Win32_NetworkAdapterConfiguration: 0x%x",
556
IWbemServices_Release(pSvc);
557
return ndis_events_use_desc(events, desc);
560
_snwprintf(query, MAX_QUERY_LEN,
561
L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE "
564
wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
566
IWbemClassObject_Release(pObj);
568
hr = call_IWbemServices_ExecQuery(
570
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
572
if (!SUCCEEDED(hr)) {
573
wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
574
"from Win32_NetworkAdapter: 0x%x", (int) hr);
575
IWbemServices_Release(pSvc);
576
return ndis_events_use_desc(events, desc);
580
hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
582
if (!SUCCEEDED(hr) || uReturned == 0) {
583
wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
584
"from Win32_NetworkAdapter: 0x%x", (int) hr);
585
IEnumWbemClassObject_Release(pEnumerator);
586
IWbemServices_Release(pSvc);
587
return ndis_events_use_desc(events, desc);
589
IEnumWbemClassObject_Release(pEnumerator);
591
hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
592
if (!SUCCEEDED(hr)) {
593
wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
594
"Win32_NetworkAdapter: 0x%x", (int) hr);
595
IWbemClassObject_Release(pObj);
596
IWbemServices_Release(pSvc);
597
return ndis_events_use_desc(events, desc);
600
wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
602
events->adapter_desc = _wcsdup(vt.bstrVal);
606
* Try to get even better candidate for matching with InstanceName
607
* from Win32_PnPEntity. This is needed at least for some USB cards
608
* that can change the InstanceName whenever being unplugged and
612
hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL);
613
if (!SUCCEEDED(hr)) {
614
wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID "
615
"from Win32_NetworkAdapter: 0x%x", (int) hr);
616
IWbemClassObject_Release(pObj);
617
IWbemServices_Release(pSvc);
618
if (events->adapter_desc == NULL)
619
return ndis_events_use_desc(events, desc);
620
return 0; /* use Win32_NetworkAdapter::Name */
623
wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
626
len = _snwprintf(query, MAX_QUERY_LEN,
627
L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='");
628
if (len < 0 || len >= MAX_QUERY_LEN - 1) {
630
IWbemClassObject_Release(pObj);
631
IWbemServices_Release(pSvc);
632
if (events->adapter_desc == NULL)
633
return ndis_events_use_desc(events, desc);
634
return 0; /* use Win32_NetworkAdapter::Name */
638
for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) {
639
if (vt.bstrVal[pos] == '\\') {
640
if (len >= MAX_QUERY_LEN - 3)
644
query[len++] = vt.bstrVal[pos];
646
query[len++] = L'\'';
649
IWbemClassObject_Release(pObj);
650
wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
652
hr = call_IWbemServices_ExecQuery(
654
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
656
if (!SUCCEEDED(hr)) {
657
wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
658
"Name from Win32_PnPEntity: 0x%x", (int) hr);
659
IWbemServices_Release(pSvc);
660
if (events->adapter_desc == NULL)
661
return ndis_events_use_desc(events, desc);
662
return 0; /* use Win32_NetworkAdapter::Name */
666
hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
668
if (!SUCCEEDED(hr) || uReturned == 0) {
669
wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
670
"from Win32_PnPEntity: 0x%x", (int) hr);
671
IEnumWbemClassObject_Release(pEnumerator);
672
IWbemServices_Release(pSvc);
673
if (events->adapter_desc == NULL)
674
return ndis_events_use_desc(events, desc);
675
return 0; /* use Win32_NetworkAdapter::Name */
677
IEnumWbemClassObject_Release(pEnumerator);
679
hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
680
if (!SUCCEEDED(hr)) {
681
wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
682
"Win32_PnPEntity: 0x%x", (int) hr);
683
IWbemClassObject_Release(pObj);
684
IWbemServices_Release(pSvc);
685
if (events->adapter_desc == NULL)
686
return ndis_events_use_desc(events, desc);
687
return 0; /* use Win32_NetworkAdapter::Name */
690
wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'",
692
os_free(events->adapter_desc);
693
events->adapter_desc = _wcsdup(vt.bstrVal);
696
IWbemClassObject_Release(pObj);
698
IWbemServices_Release(pSvc);
700
if (events->adapter_desc == NULL)
701
return ndis_events_use_desc(events, desc);
707
struct ndis_events_data *
708
ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
709
const char *ifname, const char *desc)
712
IWbemObjectSink *pSink;
713
struct ndis_events_data *events;
715
events = os_zalloc(sizeof(*events));
716
if (events == NULL) {
717
wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
720
events->ifname = os_strdup(ifname);
721
if (events->ifname == NULL) {
726
if (wmi_refcnt++ == 0) {
727
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
729
wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
730
"returned 0x%x", (int) hr);
737
/* CoInitializeSecurity() must be called once and only once
738
* per process, so let's use wmi_first flag to protect against
742
hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
743
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
744
RPC_C_IMP_LEVEL_IMPERSONATE,
745
NULL, EOAC_SECURE_REFS, NULL);
747
wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
748
"- returned 0x%x", (int) hr);
754
hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
755
&IID_IWbemLocator, (LPVOID *) &events->pLoc);
757
wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
764
if (ndis_events_get_adapter(events, ifname, desc) < 0) {
769
wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
770
events->adapter_desc);
772
hr = call_IWbemLocator_ConnectServer(
773
events->pLoc, L"ROOT\\WMI", NULL, NULL,
774
0, 0, 0, 0, &events->pSvc);
776
wpa_printf(MSG_ERROR, "Could not connect to server - error "
779
os_free(events->adapter_desc);
783
wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
785
ndis_events_constructor(events);
786
pSink = &events->sink;
787
pSink->lpVtbl = &events->sink_vtbl;
788
events->sink_vtbl.QueryInterface = ndis_events_query_interface;
789
events->sink_vtbl.AddRef = ndis_events_add_ref;
790
events->sink_vtbl.Release = ndis_events_release;
791
events->sink_vtbl.Indicate = ndis_events_indicate;
792
events->sink_vtbl.SetStatus = ndis_events_set_status;
794
if (register_async_notification(pSink, events->pSvc) < 0) {
795
wpa_printf(MSG_DEBUG, "Failed to register async "
797
ndis_events_destructor(events);
798
os_free(events->adapter_desc);
803
*read_pipe = events->read_pipe;
804
*event_avail = events->event_avail;