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