2 * ndis_events - Receive NdisMIndicateStatus() events using WMI
3 * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
9 #define _WIN32_WINNT 0x0400
15 #endif /* COBJMACROS */
21 static int wmi_refcnt = 0;
22 static int wmi_first = 1;
24 struct ndis_events_data {
26 IWbemObjectSinkVtbl sink_vtbl;
31 HANDLE read_pipe, write_pipe, event_avail;
34 char *ifname; /* {GUID..} */
38 #define BstrAlloc(x) (x) ? SysAllocString(x) : NULL
39 #define BstrFree(x) if (x) SysFreeString(x)
41 /* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to
43 HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery(
44 IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
45 long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum)
47 BSTR bsQueryLanguage, bsQuery;
50 bsQueryLanguage = BstrAlloc(strQueryLanguage);
51 bsQuery = BstrAlloc(strQuery);
53 hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags,
56 BstrFree(bsQueryLanguage);
63 HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync(
64 IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
65 long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
67 BSTR bsQueryLanguage, bsQuery;
70 bsQueryLanguage = BstrAlloc(strQueryLanguage);
71 bsQuery = BstrAlloc(strQuery);
73 hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage,
74 bsQuery, lFlags, pCtx,
77 BstrFree(bsQueryLanguage);
84 HRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer(
85 IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser,
86 LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags,
87 LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace)
89 BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority;
92 bsNetworkResource = BstrAlloc(strNetworkResource);
93 bsUser = BstrAlloc(strUser);
94 bsPassword = BstrAlloc(strPassword);
95 bsLocale = BstrAlloc(strLocale);
96 bsAuthority = BstrAlloc(strAuthority);
98 hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser,
99 bsPassword, bsLocale, lSecurityFlags,
100 bsAuthority, pCtx, ppNamespace);
102 BstrFree(bsNetworkResource);
104 BstrFree(bsPassword);
106 BstrFree(bsAuthority);
112 enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
113 EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
115 static int ndis_events_get_adapter(struct ndis_events_data *events,
116 const char *ifname, const char *desc);
119 static int ndis_events_constructor(struct ndis_events_data *events)
123 if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) {
124 wpa_printf(MSG_ERROR, "CreatePipe() failed: %d",
125 (int) GetLastError());
128 events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
129 if (events->event_avail == NULL) {
130 wpa_printf(MSG_ERROR, "CreateEvent() failed: %d",
131 (int) GetLastError());
132 CloseHandle(events->read_pipe);
133 CloseHandle(events->write_pipe);
141 static void ndis_events_destructor(struct ndis_events_data *events)
143 CloseHandle(events->read_pipe);
144 CloseHandle(events->write_pipe);
145 CloseHandle(events->event_avail);
146 IWbemServices_Release(events->pSvc);
147 IWbemLocator_Release(events->pLoc);
148 if (--wmi_refcnt == 0)
153 static HRESULT STDMETHODCALLTYPE
154 ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj)
158 if (IsEqualIID(riid, &IID_IUnknown) ||
159 IsEqualIID(riid, &IID_IWbemObjectSink)) {
161 IWbemObjectSink_AddRef(this);
165 return E_NOINTERFACE;
169 static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this)
171 struct ndis_events_data *events = (struct ndis_events_data *) this;
172 return ++events->ref;
176 static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this)
178 struct ndis_events_data *events = (struct ndis_events_data *) this;
180 if (--events->ref != 0)
183 ndis_events_destructor(events);
184 wpa_printf(MSG_DEBUG, "ndis_events: terminated");
185 os_free(events->adapter_desc);
186 os_free(events->ifname);
192 static int ndis_events_send_event(struct ndis_events_data *events,
193 enum event_types type,
194 char *data, size_t data_len)
196 char buf[512], *pos, *end;
200 end = buf + sizeof(buf);
202 os_memcpy(buf, &_type, sizeof(_type));
203 pos = buf + sizeof(_type);
206 if (2 + data_len > (size_t) (end - pos)) {
207 wpa_printf(MSG_DEBUG, "Not enough room for send_event "
208 "data (%d)", data_len);
211 *pos++ = data_len >> 8;
212 *pos++ = data_len & 0xff;
213 os_memcpy(pos, data, data_len);
217 if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) {
218 SetEvent(events->event_avail);
221 wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError());
226 static void ndis_events_media_connect(struct ndis_events_data *events)
228 wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect");
229 ndis_events_send_event(events, EVENT_CONNECT, NULL, 0);
233 static void ndis_events_media_disconnect(struct ndis_events_data *events)
235 wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect");
236 ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0);
240 static void ndis_events_media_specific(struct ndis_events_data *events,
241 IWbemClassObject *pObj)
245 LONG lower, upper, k;
250 wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication");
252 /* This is the StatusBuffer from NdisMIndicateStatus() call */
253 hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication",
256 wpa_printf(MSG_DEBUG, "Could not get "
257 "NdisStatusMediaSpecificIndication from "
262 SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower);
263 SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper);
264 data_len = upper - lower + 1;
265 data = os_malloc(data_len);
267 wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event "
274 for (k = lower; k <= upper; k++) {
275 SafeArrayGetElement(V_ARRAY(&vt), &k, &ch);
278 wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len);
282 ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len);
288 static void ndis_events_adapter_arrival(struct ndis_events_data *events)
290 wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival");
291 ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0);
295 static void ndis_events_adapter_removal(struct ndis_events_data *events)
297 wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval");
298 ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0);
302 static HRESULT STDMETHODCALLTYPE
303 ndis_events_indicate(IWbemObjectSink *this, long lObjectCount,
304 IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray)
306 struct ndis_events_data *events = (struct ndis_events_data *) this;
309 if (events->terminating) {
310 wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
311 "indication - terminating");
312 return WBEM_NO_ERROR;
314 /* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)",
317 for (i = 0; i < lObjectCount; i++) {
318 IWbemClassObject *pObj = ppObjArray[i];
322 hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL,
325 wpa_printf(MSG_DEBUG, "Failed to get __CLASS from "
329 /* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */
331 hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL,
334 wpa_printf(MSG_DEBUG, "Failed to get InstanceName "
336 VariantClear(&vtClass);
340 if (wcscmp(vtClass.bstrVal,
341 L"MSNdis_NotifyAdapterArrival") == 0) {
342 wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to "
343 "update adapter description since it may "
344 "have changed with new adapter instance");
345 ndis_events_get_adapter(events, events->ifname, NULL);
348 if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) {
349 wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
350 "indication for foreign adapter: "
351 "InstanceName: '%S' __CLASS: '%S'",
352 vt.bstrVal, vtClass.bstrVal);
353 VariantClear(&vtClass);
359 if (wcscmp(vtClass.bstrVal,
360 L"MSNdis_StatusMediaSpecificIndication") == 0) {
361 ndis_events_media_specific(events, pObj);
362 } else if (wcscmp(vtClass.bstrVal,
363 L"MSNdis_StatusMediaConnect") == 0) {
364 ndis_events_media_connect(events);
365 } else if (wcscmp(vtClass.bstrVal,
366 L"MSNdis_StatusMediaDisconnect") == 0) {
367 ndis_events_media_disconnect(events);
368 } else if (wcscmp(vtClass.bstrVal,
369 L"MSNdis_NotifyAdapterArrival") == 0) {
370 ndis_events_adapter_arrival(events);
371 } else if (wcscmp(vtClass.bstrVal,
372 L"MSNdis_NotifyAdapterRemoval") == 0) {
373 ndis_events_adapter_removal(events);
375 wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: "
376 "'%S'", vtClass.bstrVal);
379 VariantClear(&vtClass);
382 return WBEM_NO_ERROR;
386 static HRESULT STDMETHODCALLTYPE
387 ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
388 BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
390 return WBEM_NO_ERROR;
394 static int notification_query(IWbemObjectSink *pDestSink,
395 IWbemServices *pSvc, const char *class_name)
400 _snwprintf(query, 256,
401 L"SELECT * FROM %S", class_name);
402 wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
403 hr = call_IWbemServices_ExecNotificationQueryAsync(
404 pSvc, L"WQL", query, 0, 0, pDestSink);
406 wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
407 "failed with hresult of 0x%x",
408 class_name, (int) hr);
416 static int register_async_notification(IWbemObjectSink *pDestSink,
420 const char *class_list[] = {
421 "MSNdis_StatusMediaConnect",
422 "MSNdis_StatusMediaDisconnect",
423 "MSNdis_StatusMediaSpecificIndication",
424 "MSNdis_NotifyAdapterArrival",
425 "MSNdis_NotifyAdapterRemoval",
429 for (i = 0; class_list[i]; i++) {
430 if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
438 void ndis_events_deinit(struct ndis_events_data *events)
440 events->terminating = 1;
441 IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
442 IWbemObjectSink_Release(&events->sink);
444 * Rest of deinitialization is done in ndis_events_destructor() once
445 * all reference count drops to zero.
450 static int ndis_events_use_desc(struct ndis_events_data *events,
457 if (events->adapter_desc == NULL)
459 /* Continue using old description */
463 tmp = os_strdup(desc);
467 pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
471 len = os_strlen(tmp);
472 events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR));
473 if (events->adapter_desc == NULL) {
477 _snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
483 static int ndis_events_get_adapter(struct ndis_events_data *events,
484 const char *ifname, const char *desc)
488 #define MAX_QUERY_LEN 256
489 WCHAR query[MAX_QUERY_LEN];
490 IEnumWbemClassObject *pEnumerator;
491 IWbemClassObject *pObj;
497 * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter
498 * to have better probability of matching with InstanceName from
499 * MSNdis events. If this fails, use the provided description.
502 os_free(events->adapter_desc);
503 events->adapter_desc = NULL;
505 hr = call_IWbemLocator_ConnectServer(
506 events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc);
508 wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI "
509 "server (ROOT\\CIMV2) - error 0x%x", (int) hr);
510 return ndis_events_use_desc(events, desc);
512 wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
514 _snwprintf(query, MAX_QUERY_LEN,
515 L"SELECT Index FROM Win32_NetworkAdapterConfiguration "
516 L"WHERE SettingID='%S'", ifname);
517 wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
519 hr = call_IWbemServices_ExecQuery(
521 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
523 if (!SUCCEEDED(hr)) {
524 wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
525 "GUID from Win32_NetworkAdapterConfiguration: "
527 IWbemServices_Release(pSvc);
528 return ndis_events_use_desc(events, desc);
532 hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
534 if (!SUCCEEDED(hr) || uReturned == 0) {
535 wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
536 "GUID from Win32_NetworkAdapterConfiguration: "
538 IEnumWbemClassObject_Release(pEnumerator);
539 IWbemServices_Release(pSvc);
540 return ndis_events_use_desc(events, desc);
542 IEnumWbemClassObject_Release(pEnumerator);
545 hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL);
546 if (!SUCCEEDED(hr)) {
547 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from "
548 "Win32_NetworkAdapterConfiguration: 0x%x",
550 IWbemServices_Release(pSvc);
551 return ndis_events_use_desc(events, desc);
554 _snwprintf(query, MAX_QUERY_LEN,
555 L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE "
558 wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
560 IWbemClassObject_Release(pObj);
562 hr = call_IWbemServices_ExecQuery(
564 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
566 if (!SUCCEEDED(hr)) {
567 wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
568 "from Win32_NetworkAdapter: 0x%x", (int) hr);
569 IWbemServices_Release(pSvc);
570 return ndis_events_use_desc(events, desc);
574 hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
576 if (!SUCCEEDED(hr) || uReturned == 0) {
577 wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
578 "from Win32_NetworkAdapter: 0x%x", (int) hr);
579 IEnumWbemClassObject_Release(pEnumerator);
580 IWbemServices_Release(pSvc);
581 return ndis_events_use_desc(events, desc);
583 IEnumWbemClassObject_Release(pEnumerator);
585 hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
586 if (!SUCCEEDED(hr)) {
587 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
588 "Win32_NetworkAdapter: 0x%x", (int) hr);
589 IWbemClassObject_Release(pObj);
590 IWbemServices_Release(pSvc);
591 return ndis_events_use_desc(events, desc);
594 wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
596 events->adapter_desc = _wcsdup(vt.bstrVal);
600 * Try to get even better candidate for matching with InstanceName
601 * from Win32_PnPEntity. This is needed at least for some USB cards
602 * that can change the InstanceName whenever being unplugged and
606 hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL);
607 if (!SUCCEEDED(hr)) {
608 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID "
609 "from Win32_NetworkAdapter: 0x%x", (int) hr);
610 IWbemClassObject_Release(pObj);
611 IWbemServices_Release(pSvc);
612 if (events->adapter_desc == NULL)
613 return ndis_events_use_desc(events, desc);
614 return 0; /* use Win32_NetworkAdapter::Name */
617 wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
620 len = _snwprintf(query, MAX_QUERY_LEN,
621 L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='");
622 if (len < 0 || len >= MAX_QUERY_LEN - 1) {
624 IWbemClassObject_Release(pObj);
625 IWbemServices_Release(pSvc);
626 if (events->adapter_desc == NULL)
627 return ndis_events_use_desc(events, desc);
628 return 0; /* use Win32_NetworkAdapter::Name */
632 for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) {
633 if (vt.bstrVal[pos] == '\\') {
634 if (len >= MAX_QUERY_LEN - 3)
638 query[len++] = vt.bstrVal[pos];
640 query[len++] = L'\'';
643 IWbemClassObject_Release(pObj);
644 wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
646 hr = call_IWbemServices_ExecQuery(
648 WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
650 if (!SUCCEEDED(hr)) {
651 wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
652 "Name from Win32_PnPEntity: 0x%x", (int) hr);
653 IWbemServices_Release(pSvc);
654 if (events->adapter_desc == NULL)
655 return ndis_events_use_desc(events, desc);
656 return 0; /* use Win32_NetworkAdapter::Name */
660 hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
662 if (!SUCCEEDED(hr) || uReturned == 0) {
663 wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
664 "from Win32_PnPEntity: 0x%x", (int) hr);
665 IEnumWbemClassObject_Release(pEnumerator);
666 IWbemServices_Release(pSvc);
667 if (events->adapter_desc == NULL)
668 return ndis_events_use_desc(events, desc);
669 return 0; /* use Win32_NetworkAdapter::Name */
671 IEnumWbemClassObject_Release(pEnumerator);
673 hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
674 if (!SUCCEEDED(hr)) {
675 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
676 "Win32_PnPEntity: 0x%x", (int) hr);
677 IWbemClassObject_Release(pObj);
678 IWbemServices_Release(pSvc);
679 if (events->adapter_desc == NULL)
680 return ndis_events_use_desc(events, desc);
681 return 0; /* use Win32_NetworkAdapter::Name */
684 wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'",
686 os_free(events->adapter_desc);
687 events->adapter_desc = _wcsdup(vt.bstrVal);
690 IWbemClassObject_Release(pObj);
692 IWbemServices_Release(pSvc);
694 if (events->adapter_desc == NULL)
695 return ndis_events_use_desc(events, desc);
701 struct ndis_events_data *
702 ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
703 const char *ifname, const char *desc)
706 IWbemObjectSink *pSink;
707 struct ndis_events_data *events;
709 events = os_zalloc(sizeof(*events));
710 if (events == NULL) {
711 wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
714 events->ifname = os_strdup(ifname);
715 if (events->ifname == NULL) {
720 if (wmi_refcnt++ == 0) {
721 hr = CoInitializeEx(0, COINIT_MULTITHREADED);
723 wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
724 "returned 0x%x", (int) hr);
731 /* CoInitializeSecurity() must be called once and only once
732 * per process, so let's use wmi_first flag to protect against
736 hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
737 RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
738 RPC_C_IMP_LEVEL_IMPERSONATE,
739 NULL, EOAC_SECURE_REFS, NULL);
741 wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
742 "- returned 0x%x", (int) hr);
748 hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
750 (LPVOID *) (void *) &events->pLoc);
752 wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
759 if (ndis_events_get_adapter(events, ifname, desc) < 0) {
764 wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
765 events->adapter_desc);
767 hr = call_IWbemLocator_ConnectServer(
768 events->pLoc, L"ROOT\\WMI", NULL, NULL,
769 0, 0, 0, 0, &events->pSvc);
771 wpa_printf(MSG_ERROR, "Could not connect to server - error "
774 os_free(events->adapter_desc);
778 wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
780 ndis_events_constructor(events);
781 pSink = &events->sink;
782 pSink->lpVtbl = &events->sink_vtbl;
783 events->sink_vtbl.QueryInterface = ndis_events_query_interface;
784 events->sink_vtbl.AddRef = ndis_events_add_ref;
785 events->sink_vtbl.Release = ndis_events_release;
786 events->sink_vtbl.Indicate = ndis_events_indicate;
787 events->sink_vtbl.SetStatus = ndis_events_set_status;
789 if (register_async_notification(pSink, events->pSvc) < 0) {
790 wpa_printf(MSG_DEBUG, "Failed to register async "
792 ndis_events_destructor(events);
793 os_free(events->adapter_desc);
798 *read_pipe = events->read_pipe;
799 *event_avail = events->event_avail;