Use 1Mbits/s as beacon sending rate; it seems to fix TX performance issue
[dragonfly.git] / contrib / wpa_supplicant-0.5.8 / ndis_events.c
1 /*
2  * ndis_events - Receive NdisMIndicateStatus() events using WMI
3  * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
4  *
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.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #define _WIN32_WINNT    0x0400
16
17 #include "includes.h"
18
19 #ifndef COBJMACROS
20 #define COBJMACROS
21 #endif /* COBJMACROS */
22 #include <wbemidl.h>
23
24 #include "common.h"
25
26
27 static int wmi_refcnt = 0;
28 static int wmi_first = 1;
29
30 struct ndis_events_data {
31         IWbemObjectSink sink;
32         IWbemObjectSinkVtbl sink_vtbl;
33
34         IWbemServices *pSvc;
35         IWbemLocator *pLoc;
36
37         HANDLE read_pipe, write_pipe, event_avail;
38         UINT ref;
39         int terminating;
40         char *ifname; /* {GUID..} */
41         WCHAR *adapter_desc;
42 };
43
44 enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
45                    EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
46
47 static int ndis_events_get_adapter(struct ndis_events_data *events,
48                                    const char *ifname, const char *desc);
49
50
51 static int ndis_events_constructor(struct ndis_events_data *events)
52 {
53         events->ref = 1;
54
55         if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) {
56                 wpa_printf(MSG_ERROR, "CreatePipe() failed: %d",
57                            (int) GetLastError());
58                 return -1;
59         }
60         events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
61         if (events->event_avail == NULL) {
62                 wpa_printf(MSG_ERROR, "CreateEvent() failed: %d",
63                            (int) GetLastError());
64                 CloseHandle(events->read_pipe);
65                 CloseHandle(events->write_pipe);
66                 return -1;
67         }
68
69         return 0;
70 }
71
72
73 static void ndis_events_destructor(struct ndis_events_data *events)
74 {
75         CloseHandle(events->read_pipe);
76         CloseHandle(events->write_pipe);
77         CloseHandle(events->event_avail);
78         IWbemServices_Release(events->pSvc);
79         IWbemLocator_Release(events->pLoc);
80         if (--wmi_refcnt == 0)
81                 CoUninitialize();
82 }
83
84
85 static HRESULT STDMETHODCALLTYPE
86 ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj)
87 {
88         *obj = NULL;
89
90         if (IsEqualIID(riid, &IID_IUnknown) ||
91             IsEqualIID(riid, &IID_IWbemObjectSink)) {
92                 *obj = this;
93                 IWbemObjectSink_AddRef(this);
94                 return NOERROR;
95         }
96
97         return E_NOINTERFACE;
98 }
99
100
101 static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this)
102 {
103         struct ndis_events_data *events = (struct ndis_events_data *) this;
104         return ++events->ref;
105 }
106
107
108 static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this)
109 {
110         struct ndis_events_data *events = (struct ndis_events_data *) this;
111
112         if (--events->ref != 0)
113                 return events->ref;
114
115         ndis_events_destructor(events);
116         wpa_printf(MSG_DEBUG, "ndis_events: terminated");
117         os_free(events->adapter_desc);
118         os_free(events->ifname);
119         os_free(events);
120         return 0;
121 }
122
123
124 static int ndis_events_send_event(struct ndis_events_data *events,
125                                   enum event_types type,
126                                   char *data, size_t data_len)
127 {
128         char buf[512], *pos, *end;
129         int _type;
130         DWORD written;
131
132         end = buf + sizeof(buf);
133         _type = (int) type;
134         os_memcpy(buf, &_type, sizeof(_type));
135         pos = buf + sizeof(_type);
136
137         if (data) {
138                 if (2 + data_len > (size_t) (end - pos)) {
139                         wpa_printf(MSG_DEBUG, "Not enough room for send_event "
140                                    "data (%d)", data_len);
141                         return -1;
142                 }
143                 *pos++ = data_len >> 8;
144                 *pos++ = data_len & 0xff;
145                 os_memcpy(pos, data, data_len);
146                 pos += data_len;
147         }
148
149         if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) {
150                 SetEvent(events->event_avail);
151                 return 0;
152         }
153         wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError());
154         return -1;
155 }
156
157
158 static void ndis_events_media_connect(struct ndis_events_data *events)
159 {
160         wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect");
161         ndis_events_send_event(events, EVENT_CONNECT, NULL, 0);
162 }
163
164
165 static void ndis_events_media_disconnect(struct ndis_events_data *events)
166 {
167         wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect");
168         ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0);
169 }
170
171
172 static void ndis_events_media_specific(struct ndis_events_data *events,
173                                        IWbemClassObject *pObj)
174 {
175         VARIANT vt;
176         HRESULT hr;
177         LONG lower, upper, k;
178         UCHAR ch;
179         char *data, *pos;
180         size_t data_len;
181
182         wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication");
183
184         /* This is the StatusBuffer from NdisMIndicateStatus() call */
185         hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication",
186                                   0, &vt, NULL, NULL);
187         if (FAILED(hr)) {
188                 wpa_printf(MSG_DEBUG, "Could not get "
189                            "NdisStatusMediaSpecificIndication from "
190                            "the object?!");
191                 return;
192         }
193
194         SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower);
195         SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper);
196         data_len = upper - lower + 1;
197         data = os_malloc(data_len);
198         if (data == NULL) {
199                 wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event "
200                            "data");
201                 VariantClear(&vt);
202                 return;
203         }
204
205         pos = data;
206         for (k = lower; k <= upper; k++) {
207                 SafeArrayGetElement(V_ARRAY(&vt), &k, &ch);
208                 *pos++ = ch;
209         }
210         wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", data, data_len);
211
212         VariantClear(&vt);
213
214         ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len);
215
216         os_free(data);
217 }
218
219
220 static void ndis_events_adapter_arrival(struct ndis_events_data *events)
221 {
222         wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival");
223         ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0);
224 }
225
226
227 static void ndis_events_adapter_removal(struct ndis_events_data *events)
228 {
229         wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval");
230         ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0);
231 }
232
233
234 static HRESULT STDMETHODCALLTYPE
235 ndis_events_indicate(IWbemObjectSink *this, long lObjectCount,
236                      IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray)
237 {
238         struct ndis_events_data *events = (struct ndis_events_data *) this;
239         long i;
240
241         if (events->terminating) {
242                 wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
243                            "indication - terminating");
244                 return WBEM_NO_ERROR;
245         }
246         /* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)",
247            lObjectCount); */
248
249         for (i = 0; i < lObjectCount; i++) {
250                 IWbemClassObject *pObj = ppObjArray[i];
251                 HRESULT hr;
252                 VARIANT vtClass, vt;
253
254                 hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL,
255                                           NULL);
256                 if (FAILED(hr)) {
257                         wpa_printf(MSG_DEBUG, "Failed to get __CLASS from "
258                                    "event.");
259                         break;
260                 }
261                 /* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */
262
263                 hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL,
264                                           NULL);
265                 if (FAILED(hr)) {
266                         wpa_printf(MSG_DEBUG, "Failed to get InstanceName "
267                                    "from event.");
268                         VariantClear(&vtClass);
269                         break;
270                 }
271
272                 if (wcscmp(vtClass.bstrVal,
273                            L"MSNdis_NotifyAdapterArrival") == 0) {
274                         wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to "
275                                    "update adapter description since it may "
276                                    "have changed with new adapter instance");
277                         ndis_events_get_adapter(events, events->ifname, NULL);
278                 }
279
280                 if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) {
281                         wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
282                                    "indication for foreign adapter: "
283                                    "InstanceName: '%S' __CLASS: '%S'",
284                                    vt.bstrVal, vtClass.bstrVal);
285                         VariantClear(&vtClass);
286                         VariantClear(&vt);
287                         continue;
288                 }
289                 VariantClear(&vt);
290
291                 if (wcscmp(vtClass.bstrVal,
292                            L"MSNdis_StatusMediaSpecificIndication") == 0) {
293                         ndis_events_media_specific(events, pObj);
294                 } else if (wcscmp(vtClass.bstrVal,
295                                   L"MSNdis_StatusMediaConnect") == 0) {
296                         ndis_events_media_connect(events);
297                 } else if (wcscmp(vtClass.bstrVal,
298                                   L"MSNdis_StatusMediaDisconnect") == 0) {
299                         ndis_events_media_disconnect(events);
300                 } else if (wcscmp(vtClass.bstrVal,
301                                   L"MSNdis_NotifyAdapterArrival") == 0) {
302                         ndis_events_adapter_arrival(events);
303                 } else if (wcscmp(vtClass.bstrVal,
304                                   L"MSNdis_NotifyAdapterRemoval") == 0) {
305                         ndis_events_adapter_removal(events);
306                 } else {
307                         wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: "
308                                    "'%S'", vtClass.bstrVal);
309                 }
310
311                 VariantClear(&vtClass);
312         }
313
314         return WBEM_NO_ERROR;
315 }
316
317
318 static HRESULT STDMETHODCALLTYPE
319 ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
320                        BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
321 {
322         return WBEM_NO_ERROR;
323 }
324
325
326 static int notification_query(IWbemObjectSink *pDestSink,
327                               IWbemServices *pSvc, const char *class_name)
328 {
329         HRESULT hr;
330         WCHAR query[256];
331
332         _snwprintf(query, 256,
333                   L"SELECT * FROM %S", class_name);
334         wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
335         hr = IWbemServices_ExecNotificationQueryAsync(pSvc, L"WQL", query, 0,
336                                                       0, pDestSink);
337         if (FAILED(hr)) {
338                 wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
339                            "failed with hresult of 0x%x",
340                            class_name, (int) hr);
341                 return -1;
342         }
343
344         return 0;
345 }
346
347
348 static int register_async_notification(IWbemObjectSink *pDestSink,
349                                        IWbemServices *pSvc)
350 {
351         int i;
352         const char *class_list[] = {
353                 "MSNdis_StatusMediaConnect",
354                 "MSNdis_StatusMediaDisconnect",
355                 "MSNdis_StatusMediaSpecificIndication",
356                 "MSNdis_NotifyAdapterArrival",
357                 "MSNdis_NotifyAdapterRemoval",
358                 NULL
359         };
360
361         for (i = 0; class_list[i]; i++) {
362                 if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
363                         return -1;
364         }
365
366         return 0;
367 }
368
369
370 void ndis_events_deinit(struct ndis_events_data *events)
371 {
372         events->terminating = 1;
373         IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
374         IWbemObjectSink_Release(&events->sink);
375         /*
376          * Rest of deinitialization is done in ndis_events_destructor() once
377          * all reference count drops to zero.
378          */
379 }
380
381
382 static int ndis_events_use_desc(struct ndis_events_data *events,
383                                 const char *desc)
384 {
385         char *tmp, *pos;
386         size_t len;
387
388         if (desc == NULL) {
389                 if (events->adapter_desc == NULL)
390                         return -1;
391                 /* Continue using old description */
392                 return 0;
393         }
394
395         tmp = os_strdup(desc);
396         if (tmp == NULL)
397                 return -1;
398
399         pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
400         if (pos)
401                 *pos = '\0';
402
403         len = os_strlen(tmp);
404         events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR));
405         if (events->adapter_desc == NULL) {
406                 os_free(tmp);
407                 return -1;
408         }
409         _snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
410         os_free(tmp);
411         return 0;
412 }
413
414
415 static int ndis_events_get_adapter(struct ndis_events_data *events,
416                                    const char *ifname, const char *desc)
417 {
418         HRESULT hr;
419         IWbemServices *pSvc;
420 #define MAX_QUERY_LEN 256
421         WCHAR query[MAX_QUERY_LEN];
422         IEnumWbemClassObject *pEnumerator;
423         IWbemClassObject *pObj;
424         ULONG uReturned;
425         VARIANT vt;
426         int len, pos;
427
428         /*
429          * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter
430          * to have better probability of matching with InstanceName from
431          * MSNdis events. If this fails, use the provided description.
432          */
433
434         os_free(events->adapter_desc);
435         events->adapter_desc = NULL;
436
437         hr = IWbemLocator_ConnectServer(events->pLoc, L"ROOT\\CIMV2", NULL,
438                                         NULL, 0, 0, 0, 0, &pSvc);
439         if (FAILED(hr)) {
440                 wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI "
441                            "server (ROOT\\CIMV2) - error 0x%x", (int) hr);
442                 return ndis_events_use_desc(events, desc);
443         }
444         wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
445
446         _snwprintf(query, MAX_QUERY_LEN,
447                   L"SELECT Index FROM Win32_NetworkAdapterConfiguration "
448                   L"WHERE SettingID='%S'", ifname);
449         wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
450
451         hr = IWbemServices_ExecQuery(pSvc, L"WQL", query,
452                                      WBEM_FLAG_FORWARD_ONLY |
453                                      WBEM_FLAG_RETURN_IMMEDIATELY,
454                                      NULL, &pEnumerator);
455         if (!SUCCEEDED(hr)) {
456                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
457                            "GUID from Win32_NetworkAdapterConfiguration: "
458                            "0x%x", (int) hr);
459                 IWbemServices_Release(pSvc);
460                 return ndis_events_use_desc(events, desc);
461         }
462
463         uReturned = 0;
464         hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
465                                        &pObj, &uReturned);
466         if (!SUCCEEDED(hr) || uReturned == 0) {
467                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
468                            "GUID from Win32_NetworkAdapterConfiguration: "
469                            "0x%x", (int) hr);
470                 IEnumWbemClassObject_Release(pEnumerator);
471                 IWbemServices_Release(pSvc);
472                 return ndis_events_use_desc(events, desc);
473         }
474         IEnumWbemClassObject_Release(pEnumerator);
475
476         VariantInit(&vt);
477         hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL);
478         if (!SUCCEEDED(hr)) {
479                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from "
480                            "Win32_NetworkAdapterConfiguration: 0x%x",
481                            (int) hr);
482                 IWbemServices_Release(pSvc);
483                 return ndis_events_use_desc(events, desc);
484         }
485
486         _snwprintf(query, MAX_QUERY_LEN,
487                   L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE "
488                   L"Index=%d",
489                   vt.uintVal);
490         wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
491         VariantClear(&vt);
492         IWbemClassObject_Release(pObj);
493
494         hr = IWbemServices_ExecQuery(pSvc, L"WQL", query,
495                                      WBEM_FLAG_FORWARD_ONLY |
496                                      WBEM_FLAG_RETURN_IMMEDIATELY,
497                                      NULL, &pEnumerator);
498         if (!SUCCEEDED(hr)) {
499                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
500                            "from Win32_NetworkAdapter: 0x%x", (int) hr);
501                 IWbemServices_Release(pSvc);
502                 return ndis_events_use_desc(events, desc);
503         }
504
505         uReturned = 0;
506         hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
507                                        &pObj, &uReturned);
508         if (!SUCCEEDED(hr) || uReturned == 0) {
509                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
510                            "from Win32_NetworkAdapter: 0x%x", (int) hr);
511                 IEnumWbemClassObject_Release(pEnumerator);
512                 IWbemServices_Release(pSvc);
513                 return ndis_events_use_desc(events, desc);
514         }
515         IEnumWbemClassObject_Release(pEnumerator);
516
517         hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
518         if (!SUCCEEDED(hr)) {
519                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
520                            "Win32_NetworkAdapter: 0x%x", (int) hr);
521                 IWbemClassObject_Release(pObj);
522                 IWbemServices_Release(pSvc);
523                 return ndis_events_use_desc(events, desc);
524         }
525
526         wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
527                    vt.bstrVal);
528         events->adapter_desc = _wcsdup(vt.bstrVal);
529         VariantClear(&vt);
530
531         /*
532          * Try to get even better candidate for matching with InstanceName
533          * from Win32_PnPEntity. This is needed at least for some USB cards
534          * that can change the InstanceName whenever being unplugged and
535          * plugged again.
536          */
537
538         hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL);
539         if (!SUCCEEDED(hr)) {
540                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID "
541                            "from Win32_NetworkAdapter: 0x%x", (int) hr);
542                 IWbemClassObject_Release(pObj);
543                 IWbemServices_Release(pSvc);
544                 if (events->adapter_desc == NULL)
545                         return ndis_events_use_desc(events, desc);
546                 return 0; /* use Win32_NetworkAdapter::Name */
547         }
548
549         wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
550                    "'%S'", vt.bstrVal);
551
552         len = _snwprintf(query, MAX_QUERY_LEN,
553                         L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='");
554         if (len < 0 || len >= MAX_QUERY_LEN - 1) {
555                 VariantClear(&vt);
556                 IWbemClassObject_Release(pObj);
557                 IWbemServices_Release(pSvc);
558                 if (events->adapter_desc == NULL)
559                         return ndis_events_use_desc(events, desc);
560                 return 0; /* use Win32_NetworkAdapter::Name */
561         }
562
563         /* Escape \ as \\ */
564         for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) {
565                 if (vt.bstrVal[pos] == '\\') {
566                         if (len >= MAX_QUERY_LEN - 3)
567                                 break;
568                         query[len++] = '\\';
569                 }
570                 query[len++] = vt.bstrVal[pos];
571         }
572         query[len++] = L'\'';
573         query[len] = L'\0';
574         VariantClear(&vt);
575         IWbemClassObject_Release(pObj);
576         wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
577
578         hr = IWbemServices_ExecQuery(pSvc, L"WQL", query,
579                                      WBEM_FLAG_FORWARD_ONLY |
580                                      WBEM_FLAG_RETURN_IMMEDIATELY,
581                                      NULL, &pEnumerator);
582         if (!SUCCEEDED(hr)) {
583                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
584                            "Name from Win32_PnPEntity: 0x%x", (int) hr);
585                 IWbemServices_Release(pSvc);
586                 if (events->adapter_desc == NULL)
587                         return ndis_events_use_desc(events, desc);
588                 return 0; /* use Win32_NetworkAdapter::Name */
589         }
590
591         uReturned = 0;
592         hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
593                                        &pObj, &uReturned);
594         if (!SUCCEEDED(hr) || uReturned == 0) {
595                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
596                            "from Win32_PnPEntity: 0x%x", (int) hr);
597                 IEnumWbemClassObject_Release(pEnumerator);
598                 IWbemServices_Release(pSvc);
599                 if (events->adapter_desc == NULL)
600                         return ndis_events_use_desc(events, desc);
601                 return 0; /* use Win32_NetworkAdapter::Name */
602         }
603         IEnumWbemClassObject_Release(pEnumerator);
604
605         hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
606         if (!SUCCEEDED(hr)) {
607                 wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
608                            "Win32_PnPEntity: 0x%x", (int) hr);
609                 IWbemClassObject_Release(pObj);
610                 IWbemServices_Release(pSvc);
611                 if (events->adapter_desc == NULL)
612                         return ndis_events_use_desc(events, desc);
613                 return 0; /* use Win32_NetworkAdapter::Name */
614         }
615
616         wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'",
617                    vt.bstrVal);
618         os_free(events->adapter_desc);
619         events->adapter_desc = _wcsdup(vt.bstrVal);
620         VariantClear(&vt);
621
622         IWbemClassObject_Release(pObj);
623
624         IWbemServices_Release(pSvc);
625
626         if (events->adapter_desc == NULL)
627                 return ndis_events_use_desc(events, desc);
628
629         return 0;
630 }
631
632
633 struct ndis_events_data *
634 ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
635                  const char *ifname, const char *desc)
636 {
637         HRESULT hr;
638         IWbemObjectSink *pSink;
639         struct ndis_events_data *events;
640
641         events = os_zalloc(sizeof(*events));
642         if (events == NULL) {
643                 wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
644                 return NULL;
645         }
646         events->ifname = os_strdup(ifname);
647         if (events->ifname == NULL) {
648                 os_free(events);
649                 return NULL;
650         }
651
652         if (wmi_refcnt++ == 0) {
653                 hr = CoInitializeEx(0, COINIT_MULTITHREADED);
654                 if (FAILED(hr)) {
655                         wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
656                                    "returned 0x%x", (int) hr);
657                         os_free(events);
658                         return NULL;
659                 }
660         }
661
662         if (wmi_first) {
663                 /* CoInitializeSecurity() must be called once and only once
664                  * per process, so let's use wmi_first flag to protect against
665                  * multiple calls. */
666                 wmi_first = 0;
667
668                 hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
669                                           RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
670                                           RPC_C_IMP_LEVEL_IMPERSONATE,
671                                           NULL, EOAC_SECURE_REFS, NULL);
672                 if (FAILED(hr)) {
673                         wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
674                                    "- returned 0x%x", (int) hr);
675                         os_free(events);
676                         return NULL;
677                 }
678         }
679
680         hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
681                               &IID_IWbemLocator, (LPVOID *) &events->pLoc);
682         if (FAILED(hr)) {
683                 wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
684                            "0x%x", (int) hr);
685                 CoUninitialize();
686                 os_free(events);
687                 return NULL;
688         }
689
690         if (ndis_events_get_adapter(events, ifname, desc) < 0) {
691                 CoUninitialize();
692                 os_free(events);
693                 return NULL;
694         }
695         wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
696                    events->adapter_desc);
697
698         hr = IWbemLocator_ConnectServer(events->pLoc, L"ROOT\\WMI", NULL, NULL,
699                                         0, 0, 0, 0, &events->pSvc);
700         if (FAILED(hr)) {
701                 wpa_printf(MSG_ERROR, "Could not connect to server - error "
702                            "0x%x", (int) hr);
703                 CoUninitialize();
704                 os_free(events->adapter_desc);
705                 os_free(events);
706                 return NULL;
707         }
708         wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
709
710         ndis_events_constructor(events);
711         pSink = &events->sink;
712         pSink->lpVtbl = &events->sink_vtbl;
713         events->sink_vtbl.QueryInterface = ndis_events_query_interface;
714         events->sink_vtbl.AddRef = ndis_events_add_ref;
715         events->sink_vtbl.Release = ndis_events_release;
716         events->sink_vtbl.Indicate = ndis_events_indicate;
717         events->sink_vtbl.SetStatus = ndis_events_set_status;
718
719         if (register_async_notification(pSink, events->pSvc) < 0) {
720                 wpa_printf(MSG_DEBUG, "Failed to register async "
721                            "notifications");
722                 ndis_events_destructor(events);
723                 os_free(events->adapter_desc);
724                 os_free(events);
725                 return NULL;
726         }
727
728         *read_pipe = events->read_pipe;
729         *event_avail = events->event_avail;
730
731         return events;
732 }