Statistics
| Branch: | Revision:

root / tap-win32.c @ 26a76461

History | View | Annotate | Download (19.9 kB)

1
/*
2
 *  TAP-Win32 -- A kernel driver to provide virtual tap device functionality
3
 *               on Windows.  Originally derived from the CIPE-Win32
4
 *               project by Damion K. Wilson, with extensive modifications by
5
 *               James Yonan.
6
 *
7
 *  All source code which derives from the CIPE-Win32 project is
8
 *  Copyright (C) Damion K. Wilson, 2003, and is released under the
9
 *  GPL version 2 (see below).
10
 *
11
 *  All other source code is Copyright (C) James Yonan, 2003-2004,
12
 *  and is released under the GPL version 2 (see below).
13
 *
14
 *  This program is free software; you can redistribute it and/or modify
15
 *  it under the terms of the GNU General Public License as published by
16
 *  the Free Software Foundation; either version 2 of the License, or
17
 *  (at your option) any later version.
18
 *
19
 *  This program is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  You should have received a copy of the GNU General Public License
25
 *  along with this program (see the file COPYING included with this
26
 *  distribution); if not, write to the Free Software Foundation, Inc.,
27
 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28
 */
29
#include "vl.h"
30
#include <stdio.h>
31
#include <windows.h>
32

    
33
/* NOTE: PCIBus is redefined in winddk.h */
34
#define PCIBus _PCIBus
35
#include <ddk/ntapi.h>
36
#include <ddk/winddk.h>
37
#include <ddk/ntddk.h>
38
#undef PCIBus
39

    
40
//=============
41
// TAP IOCTLs
42
//=============
43

    
44
#define TAP_CONTROL_CODE(request,method) \
45
  CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
46

    
47
#define TAP_IOCTL_GET_MAC               TAP_CONTROL_CODE (1, METHOD_BUFFERED)
48
#define TAP_IOCTL_GET_VERSION           TAP_CONTROL_CODE (2, METHOD_BUFFERED)
49
#define TAP_IOCTL_GET_MTU               TAP_CONTROL_CODE (3, METHOD_BUFFERED)
50
#define TAP_IOCTL_GET_INFO              TAP_CONTROL_CODE (4, METHOD_BUFFERED)
51
#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
52
#define TAP_IOCTL_SET_MEDIA_STATUS      TAP_CONTROL_CODE (6, METHOD_BUFFERED)
53
#define TAP_IOCTL_CONFIG_DHCP_MASQ      TAP_CONTROL_CODE (7, METHOD_BUFFERED)
54
#define TAP_IOCTL_GET_LOG_LINE          TAP_CONTROL_CODE (8, METHOD_BUFFERED)
55
#define TAP_IOCTL_CONFIG_DHCP_SET_OPT   TAP_CONTROL_CODE (9, METHOD_BUFFERED)
56

    
57
//=================
58
// Registry keys
59
//=================
60

    
61
#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
62

    
63
#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
64

    
65
//======================
66
// Filesystem prefixes
67
//======================
68

    
69
#define USERMODEDEVICEDIR "\\\\.\\Global\\"
70
#define TAPSUFFIX         ".tap"
71

    
72

    
73
//======================
74
// Compile time configuration
75
//======================
76

    
77
//#define DEBUG_TAP_WIN32 1
78

    
79
#define TUN_ASYNCHRONOUS_WRITES 1
80

    
81
#define TUN_BUFFER_SIZE 1560
82
#define TUN_MAX_BUFFER_COUNT 32
83

    
84
/*
85
 * The data member "buffer" must be the first element in the tun_buffer
86
 * structure. See the function, tap_win32_free_buffer.
87
 */
88
typedef struct tun_buffer_s {
89
    unsigned char buffer [TUN_BUFFER_SIZE];
90
    unsigned long read_size;
91
    struct tun_buffer_s* next;
92
} tun_buffer_t;
93

    
94
typedef struct tap_win32_overlapped {
95
    HANDLE handle;
96
    HANDLE read_event;
97
    HANDLE write_event;
98
    HANDLE output_queue_semaphore;
99
    HANDLE free_list_semaphore;
100
    CRITICAL_SECTION output_queue_cs;
101
    CRITICAL_SECTION free_list_cs;
102
    OVERLAPPED read_overlapped;
103
    OVERLAPPED write_overlapped;
104
    tun_buffer_t buffers[TUN_MAX_BUFFER_COUNT];
105
    tun_buffer_t* free_list;
106
    tun_buffer_t* output_queue_front;
107
    tun_buffer_t* output_queue_back;
108
} tap_win32_overlapped_t;
109

    
110
static tap_win32_overlapped_t tap_overlapped;
111

    
112
static tun_buffer_t* get_buffer_from_free_list(tap_win32_overlapped_t* const overlapped) 
113
{
114
    tun_buffer_t* buffer = NULL;
115
    WaitForSingleObject(overlapped->free_list_semaphore, INFINITE);
116
    EnterCriticalSection(&overlapped->free_list_cs);
117
    buffer = overlapped->free_list;
118
//    assert(buffer != NULL);
119
    overlapped->free_list = buffer->next;
120
    LeaveCriticalSection(&overlapped->free_list_cs);
121
    buffer->next = NULL;
122
    return buffer;
123
}
124

    
125
static void put_buffer_on_free_list(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer)
126
{
127
    EnterCriticalSection(&overlapped->free_list_cs);
128
    buffer->next = overlapped->free_list;
129
    overlapped->free_list = buffer;
130
    LeaveCriticalSection(&overlapped->free_list_cs);
131
    ReleaseSemaphore(overlapped->free_list_semaphore, 1, NULL);
132
}
133

    
134
static tun_buffer_t* get_buffer_from_output_queue(tap_win32_overlapped_t* const overlapped, const int block) 
135
{
136
    tun_buffer_t* buffer = NULL;
137
    DWORD result, timeout = block ? INFINITE : 0L;
138

    
139
    // Non-blocking call
140
    result = WaitForSingleObject(overlapped->output_queue_semaphore, timeout); 
141

    
142
    switch (result) 
143
    { 
144
        // The semaphore object was signaled.
145
        case WAIT_OBJECT_0: 
146
            EnterCriticalSection(&overlapped->output_queue_cs);
147

    
148
            buffer = overlapped->output_queue_front;
149
            overlapped->output_queue_front = buffer->next;
150

    
151
            if(overlapped->output_queue_front == NULL) {
152
                overlapped->output_queue_back = NULL;
153
            }
154

    
155
            LeaveCriticalSection(&overlapped->output_queue_cs);
156
            break; 
157

    
158
        // Semaphore was nonsignaled, so a time-out occurred.
159
        case WAIT_TIMEOUT: 
160
            // Cannot open another window.
161
            break; 
162
    }
163

    
164
    return buffer;
165
}
166

    
167
static tun_buffer_t* get_buffer_from_output_queue_immediate (tap_win32_overlapped_t* const overlapped) 
168
{
169
    return get_buffer_from_output_queue(overlapped, 0);
170
}
171

    
172
static void put_buffer_on_output_queue(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer)
173
{
174
    EnterCriticalSection(&overlapped->output_queue_cs);
175

    
176
    if(overlapped->output_queue_front == NULL && overlapped->output_queue_back == NULL) {
177
        overlapped->output_queue_front = overlapped->output_queue_back = buffer;
178
    } else {
179
        buffer->next = NULL;
180
        overlapped->output_queue_back->next = buffer;
181
        overlapped->output_queue_back = buffer;
182
    }
183

    
184
    LeaveCriticalSection(&overlapped->output_queue_cs);
185

    
186
    ReleaseSemaphore(overlapped->output_queue_semaphore, 1, NULL);
187
}
188

    
189

    
190
static int is_tap_win32_dev(const char *guid)
191
{
192
    HKEY netcard_key;
193
    LONG status;
194
    DWORD len;
195
    int i = 0;
196

    
197
    status = RegOpenKeyEx(
198
        HKEY_LOCAL_MACHINE,
199
        ADAPTER_KEY,
200
        0,
201
        KEY_READ,
202
        &netcard_key);
203

    
204
    if (status != ERROR_SUCCESS) {
205
        return FALSE;
206
    }
207

    
208
    for (;;) {
209
        char enum_name[256];
210
        char unit_string[256];
211
        HKEY unit_key;
212
        char component_id_string[] = "ComponentId";
213
        char component_id[256];
214
        char net_cfg_instance_id_string[] = "NetCfgInstanceId";
215
        char net_cfg_instance_id[256];
216
        DWORD data_type;
217

    
218
        len = sizeof (enum_name);
219
        status = RegEnumKeyEx(
220
            netcard_key,
221
            i,
222
            enum_name,
223
            &len,
224
            NULL,
225
            NULL,
226
            NULL,
227
            NULL);
228

    
229
        if (status == ERROR_NO_MORE_ITEMS)
230
            break;
231
        else if (status != ERROR_SUCCESS) {
232
            return FALSE;
233
        }
234

    
235
        snprintf (unit_string, sizeof(unit_string), "%s\\%s",
236
                  ADAPTER_KEY, enum_name);
237

    
238
        status = RegOpenKeyEx(
239
            HKEY_LOCAL_MACHINE,
240
            unit_string,
241
            0,
242
            KEY_READ,
243
            &unit_key);
244

    
245
        if (status != ERROR_SUCCESS) {
246
            return FALSE;
247
        } else {
248
            len = sizeof (component_id);
249
            status = RegQueryValueEx(
250
                unit_key,
251
                component_id_string,
252
                NULL,
253
                &data_type,
254
                component_id,
255
                &len);
256

    
257
            if (!(status != ERROR_SUCCESS || data_type != REG_SZ)) {
258
                len = sizeof (net_cfg_instance_id);
259
                status = RegQueryValueEx(
260
                    unit_key,
261
                    net_cfg_instance_id_string,
262
                    NULL,
263
                    &data_type,
264
                    net_cfg_instance_id,
265
                    &len);
266

    
267
                if (status == ERROR_SUCCESS && data_type == REG_SZ) {
268
                    if (/* !strcmp (component_id, TAP_COMPONENT_ID) &&*/
269
                        !strcmp (net_cfg_instance_id, guid)) {
270
                        RegCloseKey (unit_key);
271
                        RegCloseKey (netcard_key);
272
                        return TRUE;
273
                    }
274
                }
275
            }
276
            RegCloseKey (unit_key);
277
        }
278
        ++i;
279
    }
280

    
281
    RegCloseKey (netcard_key);
282
    return FALSE;
283
}
284

    
285
static int get_device_guid(
286
    char *name,
287
    int name_size,
288
    char *actual_name,
289
    int actual_name_size)
290
{
291
    LONG status;
292
    HKEY control_net_key;
293
    DWORD len;
294
    int i = 0;
295
    int stop = 0;
296

    
297
    status = RegOpenKeyEx(
298
        HKEY_LOCAL_MACHINE,
299
        NETWORK_CONNECTIONS_KEY,
300
        0,
301
        KEY_READ,
302
        &control_net_key);
303

    
304
    if (status != ERROR_SUCCESS) {
305
        return -1;
306
    }
307

    
308
    while (!stop)
309
    {
310
        char enum_name[256];
311
        char connection_string[256];
312
        HKEY connection_key;
313
        char name_data[256];
314
        DWORD name_type;
315
        const char name_string[] = "Name";
316

    
317
        len = sizeof (enum_name);
318
        status = RegEnumKeyEx(
319
            control_net_key,
320
            i,
321
            enum_name,
322
            &len,
323
            NULL,
324
            NULL,
325
            NULL,
326
            NULL);
327

    
328
        if (status == ERROR_NO_MORE_ITEMS)
329
            break;
330
        else if (status != ERROR_SUCCESS) {
331
            return -1;
332
        }
333

    
334
        snprintf(connection_string, 
335
             sizeof(connection_string),
336
             "%s\\%s\\Connection",
337
             NETWORK_CONNECTIONS_KEY, enum_name);
338

    
339
        status = RegOpenKeyEx(
340
            HKEY_LOCAL_MACHINE,
341
            connection_string,
342
            0,
343
            KEY_READ,
344
            &connection_key);
345
        
346
        if (status == ERROR_SUCCESS) {
347
            len = sizeof (name_data);
348
            status = RegQueryValueEx(
349
                connection_key,
350
                name_string,
351
                NULL,
352
                &name_type,
353
                name_data,
354
                &len);
355

    
356
            if (status != ERROR_SUCCESS || name_type != REG_SZ) {
357
                    return -1;
358
            }
359
            else {
360
                if (is_tap_win32_dev(enum_name)) {
361
                    snprintf(name, name_size, "%s", enum_name);
362
                    if (actual_name) {
363
                        if (strcmp(actual_name, "") != 0) {
364
                            if (strcmp(name_data, actual_name) != 0) {
365
                                RegCloseKey (connection_key);
366
                                ++i;
367
                                continue;
368
                            }
369
                        }
370
                        else {
371
                            snprintf(actual_name, actual_name_size, "%s", name_data);
372
                        }
373
                    }
374
                    stop = 1;
375
                }
376
            }
377

    
378
            RegCloseKey (connection_key);
379
        }
380
        ++i;
381
    }
382

    
383
    RegCloseKey (control_net_key);
384

    
385
    if (stop == 0)
386
        return -1;
387

    
388
    return 0;
389
}
390

    
391
static int tap_win32_set_status(HANDLE handle, int status)
392
{
393
    unsigned long len = 0;
394

    
395
    return DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS,
396
                &status, sizeof (status),
397
                &status, sizeof (status), &len, NULL);
398
}
399

    
400
static void tap_win32_overlapped_init(tap_win32_overlapped_t* const overlapped, const HANDLE handle)
401
{
402
    overlapped->handle = handle;
403

    
404
    overlapped->read_event = CreateEvent(NULL, FALSE, FALSE, NULL);
405
    overlapped->write_event = CreateEvent(NULL, FALSE, FALSE, NULL);
406

    
407
    overlapped->read_overlapped.Offset = 0;
408
    overlapped->read_overlapped.OffsetHigh = 0;
409
    overlapped->read_overlapped.hEvent = overlapped->read_event;
410

    
411
    overlapped->write_overlapped.Offset = 0;
412
    overlapped->write_overlapped.OffsetHigh = 0;
413
    overlapped->write_overlapped.hEvent = overlapped->write_event;
414

    
415
    InitializeCriticalSection(&overlapped->output_queue_cs);
416
    InitializeCriticalSection(&overlapped->free_list_cs);
417

    
418
    overlapped->output_queue_semaphore = CreateSemaphore( 
419
        NULL,   // default security attributes
420
        0,   // initial count
421
        TUN_MAX_BUFFER_COUNT,   // maximum count
422
        NULL);  // unnamed semaphore
423

    
424
    if(!overlapped->output_queue_semaphore)  {
425
        fprintf(stderr, "error creating output queue semaphore!\n");
426
    }
427

    
428
    overlapped->free_list_semaphore = CreateSemaphore( 
429
        NULL,   // default security attributes
430
        TUN_MAX_BUFFER_COUNT,   // initial count
431
        TUN_MAX_BUFFER_COUNT,   // maximum count
432
        NULL);  // unnamed semaphore
433

    
434
    if(!overlapped->free_list_semaphore)  {
435
        fprintf(stderr, "error creating free list semaphore!\n");
436
    }
437

    
438
    overlapped->free_list = overlapped->output_queue_front = overlapped->output_queue_back = NULL;
439

    
440
    {
441
        unsigned index;
442
        for(index = 0; index < TUN_MAX_BUFFER_COUNT; index++) {
443
            tun_buffer_t* element = &overlapped->buffers[index];
444
            element->next = overlapped->free_list;
445
            overlapped->free_list = element;
446
        }
447
    }
448
}
449

    
450
static int tap_win32_write(tap_win32_overlapped_t *overlapped, 
451
                           const void *buffer, unsigned long size)
452
{
453
    unsigned long write_size;
454
    BOOL result;
455
    DWORD error;
456

    
457
    result = GetOverlappedResult( overlapped->handle, &overlapped->write_overlapped,
458
                                  &write_size, FALSE);
459

    
460
    if (!result && GetLastError() == ERROR_IO_INCOMPLETE)
461
        WaitForSingleObject(overlapped->write_event, INFINITE);
462

    
463
    result = WriteFile(overlapped->handle, buffer, size,
464
                       &write_size, &overlapped->write_overlapped);
465
    
466
    if (!result) { 
467
        switch (error = GetLastError())
468
        { 
469
        case ERROR_IO_PENDING: 
470
#ifndef TUN_ASYNCHRONOUS_WRITES
471
            WaitForSingleObject(overlapped->write_event, INFINITE);
472
#endif
473
            break;
474
        default:
475
            return -1;
476
        } 
477
    }
478

    
479
    return 0;
480
}
481

    
482
static DWORD WINAPI tap_win32_thread_entry(LPVOID param)
483
{
484
    tap_win32_overlapped_t *overlapped = (tap_win32_overlapped_t*)param;
485
    unsigned long read_size;
486
    BOOL result;
487
    DWORD dwError;
488
    tun_buffer_t* buffer = get_buffer_from_free_list(overlapped);
489

    
490

    
491
    for (;;) {
492
        result = ReadFile(overlapped->handle,
493
                          buffer->buffer,
494
                          sizeof(buffer->buffer),
495
                          &read_size,
496
                          &overlapped->read_overlapped);
497
        if (!result) {
498
            dwError = GetLastError();
499
            if (dwError == ERROR_IO_PENDING) {
500
                WaitForSingleObject(overlapped->read_event, INFINITE);
501
                result = GetOverlappedResult( overlapped->handle, &overlapped->read_overlapped,
502
                                              &read_size, FALSE);
503
                if (!result) {
504
#if DEBUG_TAP_WIN32
505
                    LPVOID lpBuffer;
506
                    dwError = GetLastError();
507
                    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
508
                                   NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
509
                                   (LPTSTR) & lpBuffer, 0, NULL );
510
                    fprintf(stderr, "Tap-Win32: Error GetOverlappedResult %d - %s\n", dwError, lpBuffer);
511
                    LocalFree( lpBuffer );
512
#endif
513
                }
514
            } else {
515
#if DEBUG_TAP_WIN32
516
                LPVOID lpBuffer;
517
                FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
518
                               NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
519
                               (LPTSTR) & lpBuffer, 0, NULL );
520
                fprintf(stderr, "Tap-Win32: Error ReadFile %d - %s\n", dwError, lpBuffer);
521
                LocalFree( lpBuffer );
522
#endif
523
            }
524
        }
525

    
526
        if(read_size > 0) {
527
            buffer->read_size = read_size;
528
            put_buffer_on_output_queue(overlapped, buffer);
529
            buffer = get_buffer_from_free_list(overlapped);
530
        }
531
    }
532

    
533
    return 0;
534
}
535

    
536
static int tap_win32_read(tap_win32_overlapped_t *overlapped, 
537
                          uint8_t **pbuf, int max_size)
538
{
539
    int size = 0;
540

    
541
    tun_buffer_t* buffer = get_buffer_from_output_queue_immediate(overlapped);
542

    
543
    if(buffer != NULL) {
544
        *pbuf = buffer->buffer;
545
        size = (int)buffer->read_size;
546
        if(size > max_size) {
547
            size = max_size;
548
        }
549
    }
550

    
551
    return size;
552
}
553

    
554
static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped, 
555
                                  char* pbuf) 
556
{
557
    tun_buffer_t* buffer = (tun_buffer_t*)pbuf;
558
    put_buffer_on_free_list(overlapped, buffer);
559
}
560

    
561
static int tap_win32_open(tap_win32_overlapped_t **phandle, 
562
                          const char *prefered_name)
563
{
564
    char device_path[256];
565
    char device_guid[0x100];
566
    int rc;
567
    HANDLE handle;
568
    BOOL bret;
569
    char name_buffer[0x100] = {0, };
570
    struct {
571
        unsigned long major;
572
        unsigned long minor;
573
        unsigned long debug;
574
    } version;
575
    LONG version_len;
576
    DWORD idThread;
577
    HANDLE hThread;
578

    
579
    if (prefered_name != NULL)
580
        snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name);
581

    
582
    rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer));
583
    if (rc)
584
        return -1;
585

    
586
    snprintf (device_path, sizeof(device_path), "%s%s%s",
587
              USERMODEDEVICEDIR,
588
              device_guid,
589
              TAPSUFFIX);
590

    
591
    handle = CreateFile (
592
        device_path,
593
        GENERIC_READ | GENERIC_WRITE,
594
        0,
595
        0,
596
        OPEN_EXISTING,
597
        FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
598
        0 );
599

    
600
    if (handle == INVALID_HANDLE_VALUE) {
601
        return -1;
602
    }
603

    
604
    bret = DeviceIoControl(handle, TAP_IOCTL_GET_VERSION,
605
                           &version, sizeof (version),
606
                           &version, sizeof (version), &version_len, NULL);
607

    
608
    if (bret == FALSE) {
609
        CloseHandle(handle);
610
        return -1;
611
    }
612

    
613
    if (!tap_win32_set_status(handle, TRUE)) {
614
        return -1;
615
    }
616

    
617
    tap_win32_overlapped_init(&tap_overlapped, handle);
618

    
619
    *phandle = &tap_overlapped;
620

    
621
    hThread = CreateThread(NULL, 0, tap_win32_thread_entry,
622
                           (LPVOID)&tap_overlapped, 0, &idThread);
623
    SetThreadPriority(hThread,THREAD_PRIORITY_TIME_CRITICAL);
624

    
625
    return 0;
626
}
627

    
628
/********************************************/
629

    
630
 typedef struct TAPState {
631
     VLANClientState *vc;
632
     tap_win32_overlapped_t *handle;
633
     HANDLE tap_event;
634
 } TAPState;
635

    
636
static TAPState *tap_win32_state = NULL;
637

    
638
static void tap_receive(void *opaque, const uint8_t *buf, int size)
639
{
640
    TAPState *s = opaque;
641

    
642
    tap_win32_write(s->handle, buf, size);
643
}
644

    
645
/* XXX: horrible, suppress this by using proper thread signaling */
646
void tap_win32_poll(void)
647
{
648
    TAPState *s = tap_win32_state;
649
    uint8_t *buf;
650
    int max_size = 4096;
651
    int size;
652

    
653
    if (!s)
654
        return;
655

    
656
    size = tap_win32_read(s->handle, &buf, max_size);
657
    if (size > 0) {
658
        qemu_send_packet(s->vc, buf, size);
659
        tap_win32_free_buffer(s->handle, buf);
660
        SetEvent(s->tap_event);
661
    }
662
}
663

    
664
int tap_win32_init(VLANState *vlan, const char *ifname)
665
{
666
    TAPState *s;
667
    
668
    s = qemu_mallocz(sizeof(TAPState));
669
    if (!s)
670
        return -1;
671
    if (tap_win32_open(&s->handle, ifname) < 0) {
672
        printf("tap: Could not open '%s'\n", ifname);
673
        return -1;
674
    }
675

    
676
    s->vc = qemu_new_vlan_client(vlan, tap_receive, NULL, s);
677
    
678
    snprintf(s->vc->info_str, sizeof(s->vc->info_str),
679
             "tap: ifname=%s", ifname);
680
    tap_win32_state = s;
681

    
682
    s->tap_event = CreateEvent(NULL, FALSE, FALSE, NULL);
683
    if (!s->tap_event) {
684
        fprintf(stderr, "tap-win32: Failed CreateEvent\n");
685
    }
686
    qemu_add_wait_object(s->tap_event, NULL, NULL);
687
    return 0;
688
}