1209139Srpaulo/*
2209139Srpaulo * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO
3209139Srpaulo * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
4209139Srpaulo *
5209139Srpaulo * This program is free software; you can redistribute it and/or modify
6209139Srpaulo * it under the terms of the GNU General Public License version 2 as
7209139Srpaulo * published by the Free Software Foundation.
8209139Srpaulo *
9209139Srpaulo * Alternatively, this software may be distributed under the terms of BSD
10209139Srpaulo * license.
11209139Srpaulo *
12209139Srpaulo * See README and COPYING for more details.
13209139Srpaulo *
14209139Srpaulo * This implementation requires Windows specific event loop implementation,
15209139Srpaulo * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with
16209139Srpaulo * driver_ndis.c, so only that driver interface can be used and
17209139Srpaulo * CONFIG_USE_NDISUIO must be defined.
18209139Srpaulo *
19209139Srpaulo * WinXP version of the code uses overlapped I/O and a single threaded design
20209139Srpaulo * with callback functions from I/O code. WinCE version uses a separate RX
21209139Srpaulo * thread that blocks on ReadFile() whenever the media status is connected.
22209139Srpaulo */
23209139Srpaulo
24209139Srpaulo#include "includes.h"
25209139Srpaulo#include <winsock2.h>
26209139Srpaulo#include <ntddndis.h>
27209139Srpaulo
28209139Srpaulo#ifdef _WIN32_WCE
29209139Srpaulo#include <winioctl.h>
30209139Srpaulo#include <nuiouser.h>
31209139Srpaulo#endif /* _WIN32_WCE */
32209139Srpaulo
33209139Srpaulo#include "common.h"
34209139Srpaulo#include "eloop.h"
35209139Srpaulo#include "l2_packet.h"
36209139Srpaulo
37209139Srpaulo#ifndef _WIN32_WCE
38209139Srpaulo/* from nuiouser.h */
39209139Srpaulo#define FSCTL_NDISUIO_BASE      FILE_DEVICE_NETWORK
40209139Srpaulo#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
41209139Srpaulo	CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
42209139Srpaulo#define IOCTL_NDISUIO_SET_ETHER_TYPE \
43209139Srpaulo	_NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
44209139Srpaulo			  FILE_READ_ACCESS | FILE_WRITE_ACCESS)
45209139Srpaulo#endif /* _WIN32_WCE */
46209139Srpaulo
47209139Srpaulo/* From driver_ndis.c to shared the handle to NDISUIO */
48209139SrpauloHANDLE driver_ndis_get_ndisuio_handle(void);
49209139Srpaulo
50209139Srpaulo/*
51209139Srpaulo * NDISUIO supports filtering of only one ethertype at the time, so we must
52209139Srpaulo * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth
53209139Srpaulo * whenever wpa_supplicant is trying to pre-authenticate and then switching
54209139Srpaulo * back to EAPOL when pre-authentication has been completed.
55209139Srpaulo */
56209139Srpaulo
57209139Srpaulostruct l2_packet_data;
58209139Srpaulo
59209139Srpaulostruct l2_packet_ndisuio_global {
60209139Srpaulo	int refcount;
61209139Srpaulo	unsigned short first_proto;
62209139Srpaulo	struct l2_packet_data *l2[2];
63209139Srpaulo#ifdef _WIN32_WCE
64209139Srpaulo	HANDLE rx_thread;
65209139Srpaulo	HANDLE stop_request;
66209139Srpaulo	HANDLE ready_for_read;
67209139Srpaulo	HANDLE rx_processed;
68209139Srpaulo#endif /* _WIN32_WCE */
69209139Srpaulo};
70209139Srpaulo
71209139Srpaulostatic struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL;
72209139Srpaulo
73209139Srpaulostruct l2_packet_data {
74209139Srpaulo	char ifname[100];
75209139Srpaulo	u8 own_addr[ETH_ALEN];
76209139Srpaulo	void (*rx_callback)(void *ctx, const u8 *src_addr,
77209139Srpaulo			    const u8 *buf, size_t len);
78209139Srpaulo	void *rx_callback_ctx;
79209139Srpaulo	int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to
80209139Srpaulo		     * rx_callback and l2_packet_send() */
81209139Srpaulo	HANDLE rx_avail;
82209139Srpaulo#ifndef _WIN32_WCE
83209139Srpaulo	OVERLAPPED rx_overlapped;
84209139Srpaulo#endif /* _WIN32_WCE */
85209139Srpaulo	u8 rx_buf[1514];
86209139Srpaulo	DWORD rx_written;
87209139Srpaulo};
88209139Srpaulo
89209139Srpaulo
90209139Srpauloint l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
91209139Srpaulo{
92209139Srpaulo	os_memcpy(addr, l2->own_addr, ETH_ALEN);
93209139Srpaulo	return 0;
94209139Srpaulo}
95209139Srpaulo
96209139Srpaulo
97209139Srpauloint l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
98209139Srpaulo		   const u8 *buf, size_t len)
99209139Srpaulo{
100209139Srpaulo	BOOL res;
101209139Srpaulo	DWORD written;
102209139Srpaulo	struct l2_ethhdr *eth;
103209139Srpaulo#ifndef _WIN32_WCE
104209139Srpaulo	OVERLAPPED overlapped;
105209139Srpaulo#endif /* _WIN32_WCE */
106209139Srpaulo	OVERLAPPED *o;
107209139Srpaulo
108209139Srpaulo	if (l2 == NULL)
109209139Srpaulo		return -1;
110209139Srpaulo
111209139Srpaulo#ifdef _WIN32_WCE
112209139Srpaulo	o = NULL;
113209139Srpaulo#else /* _WIN32_WCE */
114209139Srpaulo	os_memset(&overlapped, 0, sizeof(overlapped));
115209139Srpaulo	o = &overlapped;
116209139Srpaulo#endif /* _WIN32_WCE */
117209139Srpaulo
118209139Srpaulo	if (l2->l2_hdr) {
119209139Srpaulo		res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len,
120209139Srpaulo				&written, o);
121209139Srpaulo	} else {
122209139Srpaulo		size_t mlen = sizeof(*eth) + len;
123209139Srpaulo		eth = os_malloc(mlen);
124209139Srpaulo		if (eth == NULL)
125209139Srpaulo			return -1;
126209139Srpaulo
127209139Srpaulo		os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
128209139Srpaulo		os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
129209139Srpaulo		eth->h_proto = htons(proto);
130209139Srpaulo		os_memcpy(eth + 1, buf, len);
131209139Srpaulo		res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen,
132209139Srpaulo				&written, o);
133209139Srpaulo		os_free(eth);
134209139Srpaulo	}
135209139Srpaulo
136209139Srpaulo	if (!res) {
137209139Srpaulo		DWORD err = GetLastError();
138209139Srpaulo#ifndef _WIN32_WCE
139209139Srpaulo		if (err == ERROR_IO_PENDING) {
140214734Srpaulo			wpa_printf(MSG_DEBUG, "L2(NDISUIO): Wait for pending "
141214734Srpaulo				   "write to complete");
142214734Srpaulo			res = GetOverlappedResult(
143214734Srpaulo				driver_ndis_get_ndisuio_handle(), &overlapped,
144214734Srpaulo				&written, TRUE);
145214734Srpaulo			if (!res) {
146214734Srpaulo				wpa_printf(MSG_DEBUG, "L2(NDISUIO): "
147214734Srpaulo					   "GetOverlappedResult failed: %d",
148214734Srpaulo					   (int) GetLastError());
149214734Srpaulo				return -1;
150214734Srpaulo			}
151209139Srpaulo			return 0;
152209139Srpaulo		}
153209139Srpaulo#endif /* _WIN32_WCE */
154209139Srpaulo		wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d",
155209139Srpaulo			   (int) GetLastError());
156209139Srpaulo		return -1;
157209139Srpaulo	}
158209139Srpaulo
159209139Srpaulo	return 0;
160209139Srpaulo}
161209139Srpaulo
162209139Srpaulo
163209139Srpaulostatic void l2_packet_callback(struct l2_packet_data *l2);
164209139Srpaulo
165209139Srpaulo#ifdef _WIN32_WCE
166209139Srpaulostatic void l2_packet_rx_thread_try_read(struct l2_packet_data *l2)
167209139Srpaulo{
168209139Srpaulo	HANDLE handles[2];
169209139Srpaulo
170209139Srpaulo	wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile");
171209139Srpaulo	if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
172209139Srpaulo		      sizeof(l2->rx_buf), &l2->rx_written, NULL)) {
173209139Srpaulo		DWORD err = GetLastError();
174209139Srpaulo		wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: "
175209139Srpaulo			   "%d", (int) err);
176209139Srpaulo		/*
177209139Srpaulo		 * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED
178209139Srpaulo		 * error whenever the connection is not up. Yield the thread to
179209139Srpaulo		 * avoid triggering a busy loop. Connection event should stop
180209139Srpaulo		 * us from looping for long, but we need to allow enough CPU
181209139Srpaulo		 * for the main thread to process the media disconnection.
182209139Srpaulo		 */
183209139Srpaulo		Sleep(100);
184209139Srpaulo		return;
185209139Srpaulo	}
186209139Srpaulo
187209139Srpaulo	wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet",
188209139Srpaulo		   (int) l2->rx_written);
189209139Srpaulo
190209139Srpaulo	/*
191209139Srpaulo	 * Notify the main thread about the availability of a frame and wait
192209139Srpaulo	 * for the frame to be processed.
193209139Srpaulo	 */
194209139Srpaulo	SetEvent(l2->rx_avail);
195209139Srpaulo	handles[0] = l2_ndisuio_global->stop_request;
196209139Srpaulo	handles[1] = l2_ndisuio_global->rx_processed;
197209139Srpaulo	WaitForMultipleObjects(2, handles, FALSE, INFINITE);
198209139Srpaulo	ResetEvent(l2_ndisuio_global->rx_processed);
199209139Srpaulo}
200209139Srpaulo
201209139Srpaulo
202209139Srpaulostatic DWORD WINAPI l2_packet_rx_thread(LPVOID arg)
203209139Srpaulo{
204209139Srpaulo	struct l2_packet_data *l2 = arg;
205209139Srpaulo	DWORD res;
206209139Srpaulo	HANDLE handles[2];
207209139Srpaulo	int run = 1;
208209139Srpaulo
209209139Srpaulo	wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started");
210209139Srpaulo	handles[0] = l2_ndisuio_global->stop_request;
211209139Srpaulo	handles[1] = l2_ndisuio_global->ready_for_read;
212209139Srpaulo
213209139Srpaulo	/*
214209139Srpaulo	 * Unfortunately, NDISUIO on WinCE does not seem to support waiting
215209139Srpaulo	 * on the handle. There do not seem to be anything else that we could
216209139Srpaulo	 * wait for either. If one were to modify NDISUIO to set a named event
217209139Srpaulo	 * whenever packets are available, this event could be used here to
218209139Srpaulo	 * avoid having to poll for new packets or we could even move to use a
219209139Srpaulo	 * single threaded design.
220209139Srpaulo	 *
221209139Srpaulo	 * In addition, NDISUIO on WinCE is returning
222209139Srpaulo	 * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while
223209139Srpaulo	 * the adapter is not in connected state. For now, we are just using a
224209139Srpaulo	 * local event to allow ReadFile calls only after having received NDIS
225209139Srpaulo	 * media connect event. This event could be easily converted to handle
226209139Srpaulo	 * another event if the protocol driver is replaced with somewhat more
227209139Srpaulo	 * useful design.
228209139Srpaulo	 */
229209139Srpaulo
230209139Srpaulo	while (l2_ndisuio_global && run) {
231209139Srpaulo		res = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
232209139Srpaulo		switch (res) {
233209139Srpaulo		case WAIT_OBJECT_0:
234209139Srpaulo			wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received "
235209139Srpaulo				   "request to stop RX thread");
236209139Srpaulo			run = 0;
237209139Srpaulo			break;
238209139Srpaulo		case WAIT_OBJECT_0 + 1:
239209139Srpaulo			l2_packet_rx_thread_try_read(l2);
240209139Srpaulo			break;
241209139Srpaulo		case WAIT_FAILED:
242209139Srpaulo		default:
243209139Srpaulo			wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: "
244209139Srpaulo				   "WaitForMultipleObjects failed: %d",
245209139Srpaulo				   (int) GetLastError());
246209139Srpaulo			run = 0;
247209139Srpaulo			break;
248209139Srpaulo		}
249209139Srpaulo	}
250209139Srpaulo
251209139Srpaulo	wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped");
252209139Srpaulo
253209139Srpaulo	return 0;
254209139Srpaulo}
255209139Srpaulo#else /* _WIN32_WCE */
256209139Srpaulostatic int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive)
257209139Srpaulo{
258209139Srpaulo	os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped));
259209139Srpaulo	l2->rx_overlapped.hEvent = l2->rx_avail;
260209139Srpaulo	if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
261209139Srpaulo		      sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped))
262209139Srpaulo	{
263209139Srpaulo		DWORD err = GetLastError();
264209139Srpaulo		if (err != ERROR_IO_PENDING) {
265209139Srpaulo			wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: "
266209139Srpaulo				   "%d", (int) err);
267209139Srpaulo			return -1;
268209139Srpaulo		}
269209139Srpaulo		/*
270209139Srpaulo		 * Once read is completed, l2_packet_rx_event() will be
271209139Srpaulo		 * called.
272209139Srpaulo		 */
273209139Srpaulo	} else {
274209139Srpaulo		wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data "
275209139Srpaulo			   "without wait for completion");
276209139Srpaulo		if (!recursive)
277209139Srpaulo			l2_packet_callback(l2);
278209139Srpaulo	}
279209139Srpaulo
280209139Srpaulo	return 0;
281209139Srpaulo}
282209139Srpaulo#endif /* _WIN32_WCE */
283209139Srpaulo
284209139Srpaulo
285209139Srpaulostatic void l2_packet_callback(struct l2_packet_data *l2)
286209139Srpaulo{
287209139Srpaulo	const u8 *rx_buf, *rx_src;
288209139Srpaulo	size_t rx_len;
289209139Srpaulo	struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf;
290209139Srpaulo
291209139Srpaulo	wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes",
292209139Srpaulo		   (int) l2->rx_written);
293209139Srpaulo
294209139Srpaulo	if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) {
295209139Srpaulo		rx_buf = (u8 *) ethhdr;
296209139Srpaulo		rx_len = l2->rx_written;
297209139Srpaulo	} else {
298209139Srpaulo		rx_buf = (u8 *) (ethhdr + 1);
299209139Srpaulo		rx_len = l2->rx_written - sizeof(*ethhdr);
300209139Srpaulo	}
301209139Srpaulo	rx_src = ethhdr->h_source;
302209139Srpaulo
303209139Srpaulo	l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len);
304209139Srpaulo#ifndef _WIN32_WCE
305209139Srpaulo	l2_ndisuio_start_read(l2, 1);
306209139Srpaulo#endif /* _WIN32_WCE */
307209139Srpaulo}
308209139Srpaulo
309209139Srpaulo
310209139Srpaulostatic void l2_packet_rx_event(void *eloop_data, void *user_data)
311209139Srpaulo{
312209139Srpaulo	struct l2_packet_data *l2 = eloop_data;
313209139Srpaulo
314209139Srpaulo	if (l2_ndisuio_global)
315209139Srpaulo		l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1];
316209139Srpaulo
317209139Srpaulo	ResetEvent(l2->rx_avail);
318209139Srpaulo
319209139Srpaulo#ifndef _WIN32_WCE
320209139Srpaulo	if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(),
321209139Srpaulo				 &l2->rx_overlapped, &l2->rx_written, FALSE)) {
322209139Srpaulo		wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult "
323209139Srpaulo			   "failed: %d", (int) GetLastError());
324209139Srpaulo		return;
325209139Srpaulo	}
326209139Srpaulo#endif /* _WIN32_WCE */
327209139Srpaulo
328209139Srpaulo	l2_packet_callback(l2);
329209139Srpaulo
330209139Srpaulo#ifdef _WIN32_WCE
331209139Srpaulo	SetEvent(l2_ndisuio_global->rx_processed);
332209139Srpaulo#endif /* _WIN32_WCE */
333209139Srpaulo}
334209139Srpaulo
335209139Srpaulo
336209139Srpaulostatic int l2_ndisuio_set_ether_type(unsigned short protocol)
337209139Srpaulo{
338209139Srpaulo	USHORT proto = htons(protocol);
339209139Srpaulo	DWORD written;
340209139Srpaulo
341209139Srpaulo	if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
342209139Srpaulo			     IOCTL_NDISUIO_SET_ETHER_TYPE, &proto,
343209139Srpaulo			     sizeof(proto), NULL, 0, &written, NULL)) {
344209139Srpaulo		wpa_printf(MSG_ERROR, "L2(NDISUIO): "
345209139Srpaulo			   "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d",
346209139Srpaulo			   (int) GetLastError());
347209139Srpaulo		return -1;
348209139Srpaulo	}
349209139Srpaulo
350209139Srpaulo	return 0;
351209139Srpaulo}
352209139Srpaulo
353209139Srpaulo
354209139Srpaulostruct l2_packet_data * l2_packet_init(
355209139Srpaulo	const char *ifname, const u8 *own_addr, unsigned short protocol,
356209139Srpaulo	void (*rx_callback)(void *ctx, const u8 *src_addr,
357209139Srpaulo			    const u8 *buf, size_t len),
358209139Srpaulo	void *rx_callback_ctx, int l2_hdr)
359209139Srpaulo{
360209139Srpaulo	struct l2_packet_data *l2;
361209139Srpaulo
362209139Srpaulo	if (l2_ndisuio_global == NULL) {
363209139Srpaulo		l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global));
364209139Srpaulo		if (l2_ndisuio_global == NULL)
365209139Srpaulo			return NULL;
366209139Srpaulo		l2_ndisuio_global->first_proto = protocol;
367209139Srpaulo	}
368209139Srpaulo	if (l2_ndisuio_global->refcount >= 2) {
369209139Srpaulo		wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two "
370209139Srpaulo			   "simultaneous connections allowed");
371209139Srpaulo		return NULL;
372209139Srpaulo	}
373209139Srpaulo	l2_ndisuio_global->refcount++;
374209139Srpaulo
375209139Srpaulo	l2 = os_zalloc(sizeof(struct l2_packet_data));
376209139Srpaulo	if (l2 == NULL)
377209139Srpaulo		return NULL;
378209139Srpaulo	l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2;
379209139Srpaulo
380209139Srpaulo	os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
381209139Srpaulo	l2->rx_callback = rx_callback;
382209139Srpaulo	l2->rx_callback_ctx = rx_callback_ctx;
383209139Srpaulo	l2->l2_hdr = l2_hdr;
384209139Srpaulo
385209139Srpaulo	if (own_addr)
386209139Srpaulo		os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
387209139Srpaulo
388209139Srpaulo	if (l2_ndisuio_set_ether_type(protocol) < 0) {
389209139Srpaulo		os_free(l2);
390209139Srpaulo		return NULL;
391209139Srpaulo	}
392209139Srpaulo
393209139Srpaulo	if (l2_ndisuio_global->refcount > 1) {
394209139Srpaulo		wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting "
395209139Srpaulo			   "filtering ethertype to %04x", protocol);
396209139Srpaulo		if (l2_ndisuio_global->l2[0])
397209139Srpaulo			l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail;
398209139Srpaulo		return l2;
399209139Srpaulo	}
400209139Srpaulo
401209139Srpaulo	l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
402209139Srpaulo	if (l2->rx_avail == NULL) {
403209139Srpaulo		os_free(l2);
404209139Srpaulo		return NULL;
405209139Srpaulo	}
406209139Srpaulo
407209139Srpaulo	eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
408209139Srpaulo			     l2_packet_rx_event, l2, NULL);
409209139Srpaulo
410209139Srpaulo#ifdef _WIN32_WCE
411209139Srpaulo	l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL);
412209139Srpaulo	/*
413209139Srpaulo	 * This event is being set based on media connect/disconnect
414209139Srpaulo	 * notifications in driver_ndis.c.
415209139Srpaulo	 */
416209139Srpaulo	l2_ndisuio_global->ready_for_read =
417209139Srpaulo		CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
418209139Srpaulo	l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL);
419209139Srpaulo	if (l2_ndisuio_global->stop_request == NULL ||
420209139Srpaulo	    l2_ndisuio_global->ready_for_read == NULL ||
421209139Srpaulo	    l2_ndisuio_global->rx_processed == NULL) {
422209139Srpaulo		if (l2_ndisuio_global->stop_request) {
423209139Srpaulo			CloseHandle(l2_ndisuio_global->stop_request);
424209139Srpaulo			l2_ndisuio_global->stop_request = NULL;
425209139Srpaulo		}
426209139Srpaulo		if (l2_ndisuio_global->ready_for_read) {
427209139Srpaulo			CloseHandle(l2_ndisuio_global->ready_for_read);
428209139Srpaulo			l2_ndisuio_global->ready_for_read = NULL;
429209139Srpaulo		}
430209139Srpaulo		if (l2_ndisuio_global->rx_processed) {
431209139Srpaulo			CloseHandle(l2_ndisuio_global->rx_processed);
432209139Srpaulo			l2_ndisuio_global->rx_processed = NULL;
433209139Srpaulo		}
434209139Srpaulo		eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
435209139Srpaulo		os_free(l2);
436209139Srpaulo		return NULL;
437209139Srpaulo	}
438209139Srpaulo
439209139Srpaulo	l2_ndisuio_global->rx_thread = CreateThread(NULL, 0,
440209139Srpaulo						    l2_packet_rx_thread, l2, 0,
441209139Srpaulo						    NULL);
442209139Srpaulo	if (l2_ndisuio_global->rx_thread == NULL) {
443209139Srpaulo		wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX "
444209139Srpaulo			   "thread: %d", (int) GetLastError());
445209139Srpaulo		eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
446209139Srpaulo		CloseHandle(l2_ndisuio_global->stop_request);
447209139Srpaulo		l2_ndisuio_global->stop_request = NULL;
448209139Srpaulo		os_free(l2);
449209139Srpaulo		return NULL;
450209139Srpaulo	}
451209139Srpaulo#else /* _WIN32_WCE */
452209139Srpaulo	l2_ndisuio_start_read(l2, 0);
453209139Srpaulo#endif /* _WIN32_WCE */
454209139Srpaulo
455209139Srpaulo	return l2;
456209139Srpaulo}
457209139Srpaulo
458209139Srpaulo
459209139Srpaulovoid l2_packet_deinit(struct l2_packet_data *l2)
460209139Srpaulo{
461209139Srpaulo	if (l2 == NULL)
462209139Srpaulo		return;
463209139Srpaulo
464209139Srpaulo	if (l2_ndisuio_global) {
465209139Srpaulo		l2_ndisuio_global->refcount--;
466209139Srpaulo		l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL;
467209139Srpaulo		if (l2_ndisuio_global->refcount) {
468209139Srpaulo			wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering "
469209139Srpaulo				   "ethertype to %04x",
470209139Srpaulo				   l2_ndisuio_global->first_proto);
471209139Srpaulo			l2_ndisuio_set_ether_type(
472209139Srpaulo				l2_ndisuio_global->first_proto);
473209139Srpaulo			return;
474209139Srpaulo		}
475209139Srpaulo
476209139Srpaulo#ifdef _WIN32_WCE
477209139Srpaulo		wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to "
478209139Srpaulo			   "stop");
479209139Srpaulo		SetEvent(l2_ndisuio_global->stop_request);
480209139Srpaulo		/*
481209139Srpaulo		 * Cancel pending ReadFile() in the RX thread (if we were still
482209139Srpaulo		 * connected at this point).
483209139Srpaulo		 */
484209139Srpaulo		if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
485209139Srpaulo				     IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL,
486209139Srpaulo				     NULL)) {
487209139Srpaulo			wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ "
488209139Srpaulo				   "failed: %d", (int) GetLastError());
489209139Srpaulo			/* RX thread will exit blocking ReadFile once NDISUIO
490209139Srpaulo			 * notices that the adapter is disconnected. */
491209139Srpaulo		}
492209139Srpaulo		WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE);
493209139Srpaulo		wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited");
494209139Srpaulo		CloseHandle(l2_ndisuio_global->rx_thread);
495209139Srpaulo		CloseHandle(l2_ndisuio_global->stop_request);
496209139Srpaulo		CloseHandle(l2_ndisuio_global->ready_for_read);
497209139Srpaulo		CloseHandle(l2_ndisuio_global->rx_processed);
498209139Srpaulo#endif /* _WIN32_WCE */
499209139Srpaulo
500209139Srpaulo		os_free(l2_ndisuio_global);
501209139Srpaulo		l2_ndisuio_global = NULL;
502209139Srpaulo	}
503209139Srpaulo
504209139Srpaulo#ifndef _WIN32_WCE
505209139Srpaulo	CancelIo(driver_ndis_get_ndisuio_handle());
506209139Srpaulo#endif /* _WIN32_WCE */
507209139Srpaulo
508209139Srpaulo	eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
509209139Srpaulo	CloseHandle(l2->rx_avail);
510209139Srpaulo	os_free(l2);
511209139Srpaulo}
512209139Srpaulo
513209139Srpaulo
514209139Srpauloint l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
515209139Srpaulo{
516209139Srpaulo	return -1;
517209139Srpaulo}
518209139Srpaulo
519209139Srpaulo
520209139Srpaulovoid l2_packet_notify_auth_start(struct l2_packet_data *l2)
521209139Srpaulo{
522209139Srpaulo}
523