Sophie

Sophie

distrib > Mageia > 8 > i586 > by-pkgid > f65f1d773383ac49d6fcc3657259ff43 > files > 13

kernel-5.10.45-2.mga8.src.rpm

diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/crt.c linux-5.6.11-ndis/3rdparty/ndiswrapper/crt.c
--- linux-5.6.11/3rdparty/ndiswrapper/crt.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/crt.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,589 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include "ntoskernel.h"
+#include "crt_exports.h"
+
+#ifdef CONFIG_X86_64
+/* Windows long is 32-bit, so strip single 'l' in integer formats */
+static void strip_l_modifier(char *str)
+{
+	char *ptr = str;
+	int in_format = 0;
+	char *lptr = NULL;
+	char last = 0;
+	char *end_ptr;
+	char *wptr;
+
+	/* Replace single 'l' inside integer formats with '\0' */
+	for (ptr = str; *ptr; ptr++) {
+		if (!in_format) {
+			if (*ptr == '%')
+				in_format = 1;
+			last = *ptr;
+			continue;
+		}
+		switch (*ptr) {
+		case 'd':
+		case 'i':
+		case 'o':
+		case 'u':
+		case 'x':
+		case 'X':
+		case 'p':
+		case 'n':
+		case 'm':
+			if (lptr) {
+				*lptr = '\0';
+				lptr = NULL;
+			}
+			in_format = 0;
+			break;
+		case 'c':
+		case 'C':
+		case 's':
+		case 'S':
+		case 'f':
+		case 'e':
+		case 'E':
+		case 'g':
+		case 'G':
+		case 'a':
+		case 'A':
+			lptr = NULL;
+			in_format = 0;
+			break;
+		case '%':
+			lptr = NULL;
+			if (last == '%')
+				in_format = 0;
+			else
+				in_format = 1;	/* ignore previous junk */
+			break;
+		case 'l':
+			if (last == 'l')
+				lptr = NULL;
+			else
+				lptr = ptr;
+			break;
+		default:
+			break;
+		}
+		last = *ptr;
+	}
+
+	/* Purge zeroes from the resulting string */
+	end_ptr = ptr;
+	wptr = str;
+	for (ptr = str; ptr < end_ptr; ptr++)
+		if (*ptr != 0)
+			*(wptr++) = *ptr;
+	*wptr = 0;
+}
+
+/*
+ * va_list on x86_64 Linux is designed to allow passing arguments in registers
+ * even to variadic functions.  va_list is a structure holding pointers to the
+ * register save area, which holds the arguments passed in registers, and to
+ * the stack, which may have the arguments that did not fit the registers.
+ * va_list also holds offsets in the register save area for the next general
+ * purpose and floating point registers that the next va_arg() would fetch.
+ *
+ * Unlike Linux, the Windows va_list is just a pointer to the stack.  No
+ * arguments are passed in the registers.  That's why we construct the Linux
+ * va_list so that the register save area is never used.  For that goal, we set
+ * the offsets to the maximal allowed values, meaning that the arguments passed
+ * in the registers have been exhausted.  The values are 48 for general purpose
+ * registers (6 registers, 8 bytes each) and 304 for floating point registers
+ * (16 registers, 16 bytes each, on top of general purpose register).
+ */
+
+struct x86_64_va_list {
+	int gp_offset;
+	int fp_offset;
+	void *overflow_arg_area;
+	void *reg_save_area;
+};
+
+#define VA_LIST_DECL(_args) \
+	va_list _args##new; \
+	struct x86_64_va_list *_args##x;
+#define VA_LIST_PREP(_args) \
+do { \
+	_args##x = (struct x86_64_va_list *)&_args##new; \
+	_args##x->gp_offset = 6 * 8;		/* GP registers exhausted */ \
+	_args##x->fp_offset = 6 * 8 + 16 * 16;	/* FP registers exhausted */ \
+	_args##x->overflow_arg_area = (void *)_args; \
+	_args##x->reg_save_area = NULL; \
+} while (0)
+#define VA_LIST_CONV(_args) (_args##new)
+#define VA_LIST_FREE(_args)
+#define FMT_DECL(_fmt) \
+	char *_fmt##copy; \
+	int _fmt##len;
+#define FMT_PREP(_fmt) \
+do { \
+	_fmt##len = strlen(format) + 1; \
+	_fmt##copy = kmalloc(_fmt##len, irql_gfp()); \
+	if (_fmt##copy) { \
+		memcpy(_fmt##copy, format, _fmt##len); \
+		strip_l_modifier(_fmt##copy); \
+	} \
+} while (0)
+#define FMT_CONV(_fmt) (_fmt##copy ? _fmt##copy : format)
+#define FMT_FREE(_fmt) kfree(_fmt##copy)
+
+#else /* !CONFIG_X86_64 */
+
+#define VA_LIST_DECL(_args)
+#define VA_LIST_PREP(_args)
+#define VA_LIST_CONV(_args) (_args)
+#define VA_LIST_FREE(_args)
+#define FMT_DECL(_fmt)
+#define FMT_PREP(_fmt)
+#define FMT_CONV(_fmt) (format)
+#define FMT_FREE(_fmt)
+
+#endif /* !CONFIG_X86_64 */
+
+__attribute__((format(printf, 2, 3)))
+noregparm INT WIN_FUNC(_win_sprintf,12)
+	(char *buf, const char *format, ...)
+{
+	va_list args;
+	int res;
+	FMT_DECL(format)
+
+	FMT_PREP(format);
+	va_start(args, format);
+	res = vsprintf(buf, FMT_CONV(format), args);
+	va_end(args);
+	FMT_FREE(format);
+
+	TRACE2("buf: %p: %s", buf, buf);
+	return res;
+}
+
+noregparm INT WIN_FUNC(swprintf,12)
+	(wchar_t *buf, const wchar_t *format, ...)
+{
+	TODO();
+	EXIT2(return 0);
+}
+
+noregparm INT WIN_FUNC(_win_vsprintf,3)
+	(char *str, const char *format, va_list ap)
+{
+	INT i;
+	VA_LIST_DECL(ap)
+	FMT_DECL(format)
+
+	VA_LIST_PREP(ap);
+	FMT_PREP(format);
+
+	i = vsprintf(str, FMT_CONV(format), VA_LIST_CONV(ap));
+	TRACE2("str: %p: %s", str, str);
+
+	FMT_FREE(format);
+	VA_LIST_FREE(ap);
+	EXIT2(return i);
+}
+
+__attribute__((format(printf, 3, 4)))
+noregparm INT WIN_FUNC(_win_snprintf,12)
+	(char *buf, SIZE_T count, const char *format, ...)
+{
+	va_list args;
+	int res;
+	FMT_DECL(format)
+
+	FMT_PREP(format);
+	va_start(args, format);
+	res = vsnprintf(buf, count, FMT_CONV(format), args);
+	va_end(args);
+	TRACE2("buf: %p: %s", buf, buf);
+
+	FMT_FREE(format);
+	return res;
+}
+
+__attribute__((format(printf, 3, 4)))
+noregparm INT WIN_FUNC(_win__snprintf,12)
+	(char *buf, SIZE_T count, const char *format, ...)
+{
+	va_list args;
+	int res;
+	FMT_DECL(format)
+
+	FMT_PREP(format);
+	va_start(args, format);
+	res = vsnprintf(buf, count, FMT_CONV(format), args);
+	va_end(args);
+	TRACE2("buf: %p: %s", buf, buf);
+
+	FMT_FREE(format);
+	return res;
+}
+
+noregparm INT WIN_FUNC(_win_vsnprintf,4)
+	(char *str, SIZE_T size, const char *format, va_list ap)
+{
+	INT i;
+	VA_LIST_DECL(ap)
+	FMT_DECL(format)
+
+	VA_LIST_PREP(ap);
+	FMT_PREP(format);
+
+	i = vsnprintf(str, size, FMT_CONV(format), VA_LIST_CONV(ap));
+	TRACE2("str: %p: %s", str, str);
+
+	FMT_FREE(format);
+	VA_LIST_FREE(ap);
+	EXIT2(return i);
+}
+
+noregparm INT WIN_FUNC(_win__vsnprintf,4)
+	(char *str, SIZE_T size, const char *format, va_list ap)
+{
+	INT i;
+	VA_LIST_DECL(ap)
+	FMT_DECL(format)
+
+	VA_LIST_PREP(ap);
+	FMT_PREP(format);
+
+	i = vsnprintf(str, size, FMT_CONV(format), VA_LIST_CONV(ap));
+	TRACE2("str: %p: %s", str, str);
+
+	FMT_FREE(format);
+	VA_LIST_FREE(ap);
+	EXIT2(return i);
+}
+
+noregparm INT WIN_FUNC(_win__vsnwprintf,4)
+	(wchar_t *str, SIZE_T size, const wchar_t *format, va_list ap)
+{
+	int ret;
+
+	TODO();		/* format expansion not implemented */
+	_win_wcsncpy(str, format, size);
+	ret = _win_wcslen(format);
+	if (ret >= size)
+		ret = -1;
+	return ret;
+}
+
+noregparm char *WIN_FUNC(_win_strncpy,3)
+	(char *dst, char *src, SIZE_T n)
+{
+	return strncpy(dst, src, n);
+}
+
+noregparm SIZE_T WIN_FUNC(_win_strlen,1)
+	(const char *s)
+{
+	return strlen(s);
+}
+
+noregparm INT WIN_FUNC(_win_strncmp,3)
+	(const char *s1, const char *s2, SIZE_T n)
+{
+	return strncmp(s1, s2, n);
+}
+
+noregparm INT WIN_FUNC(_win_strcmp,2)
+	(const char *s1, const char *s2)
+{
+	return strcmp(s1, s2);
+}
+
+noregparm INT WIN_FUNC(_win_stricmp,2)
+	(const char *s1, const char *s2)
+{
+	return stricmp(s1, s2);
+}
+
+noregparm char *WIN_FUNC(_win_strncat,3)
+	(char *dest, const char *src, SIZE_T n)
+{
+	return strncat(dest, src, n);
+}
+
+noregparm INT WIN_FUNC(_win_wcscmp,2)
+	(const wchar_t *s1, const wchar_t *s2)
+{
+	while (*s1 && *s1 == *s2) {
+		s1++;
+		s2++;
+	}
+	return *s1 - *s2;
+}
+
+noregparm INT WIN_FUNC(_win_wcsicmp,2)
+	(const wchar_t *s1, const wchar_t *s2)
+{
+	while (*s1 && tolower((char)*s1) == tolower((char)*s2)) {
+		s1++;
+		s2++;
+	}
+	return tolower((char)*s1) - tolower((char)*s2);
+}
+
+noregparm SIZE_T WIN_FUNC(_win_wcslen,1)
+	(const wchar_t *s)
+{
+	const wchar_t *t = s;
+	while (*t)
+		t++;
+	return t - s;
+}
+
+noregparm wchar_t *WIN_FUNC(_win_wcsncpy,3)
+	(wchar_t *dest, const wchar_t *src, SIZE_T n)
+{
+	const wchar_t *s;
+	wchar_t *d;
+	s = src + n;
+	d = dest;
+	while (src < s && (*d++ = *src++))
+		;
+	if (s > src)
+		memset(d, 0, (s - src) * sizeof(wchar_t));
+	return dest;
+}
+
+noregparm wchar_t *WIN_FUNC(_win_wcscpy,2)
+	(wchar_t *dest, const wchar_t *src)
+{
+	wchar_t *d = dest;
+	while ((*d++ = *src++))
+		;
+	return dest;
+}
+
+noregparm wchar_t *WIN_FUNC(_win_wcscat,2)
+	(wchar_t *dest, const wchar_t *src)
+{
+	wchar_t *d;
+	d = dest;
+	while (*d)
+		d++;
+	while ((*d++ = *src++))
+		;
+	return dest;
+}
+
+noregparm INT WIN_FUNC(_win_towupper,1)
+	(wchar_t c)
+{
+	return toupper(c);
+}
+
+noregparm INT WIN_FUNC(_win_towlower,1)
+	(wchar_t c)
+{
+	return tolower(c);
+}
+
+noregparm INT WIN_FUNC(_win_tolower,1)
+	(INT c)
+{
+	return tolower(c);
+}
+
+noregparm INT WIN_FUNC(_win_toupper,1)
+	(INT c)
+{
+	return toupper(c);
+}
+
+noregparm void *WIN_FUNC(_win_strcpy,2)
+	(void *to, const void *from)
+{
+	return strcpy(to, from);
+}
+
+noregparm char *WIN_FUNC(_win_strstr,2)
+	(const char *s1, const char *s2)
+{
+	return strstr(s1, s2);
+}
+
+noregparm char *WIN_FUNC(_win_strchr,2)
+	(const char *s, int c)
+{
+	return strchr(s, c);
+}
+
+noregparm char *WIN_FUNC(_win_strrchr,2)
+	(const char *s, int c)
+{
+	return strrchr(s, c);
+}
+
+noregparm void *WIN_FUNC(_win_memmove,3)
+	(void *to, void *from, SIZE_T count)
+{
+	return memmove(to, from, count);
+}
+
+noregparm void *WIN_FUNC(_win_memchr,3)
+	(const void *s, INT c, SIZE_T n)
+{
+	return memchr(s, c, n);
+}
+
+noregparm void *WIN_FUNC(_win_memcpy,3)
+	(void *to, const void *from, SIZE_T n)
+{
+	return memcpy(to, from, n);
+}
+
+noregparm void *WIN_FUNC(_win_memset,3)
+	(void *s, char c, SIZE_T count)
+{
+	return memset(s, c, count);
+}
+
+noregparm int WIN_FUNC(_win_memcmp,3)
+	(void *s1, void *s2, SIZE_T n)
+{
+	return memcmp(s1, s2, n);
+}
+
+noregparm void WIN_FUNC(_win_srand,1)
+	(UINT seed)
+{
+	prandom_seed(seed);
+}
+
+noregparm int WIN_FUNC(rand,0)
+	(void)
+{
+	char buf[6];
+	int i, n;
+
+	get_random_bytes(buf, sizeof(buf));
+	for (n = i = 0; i < sizeof(buf); i++)
+		n += buf[i];
+	return n;
+}
+
+noregparm int WIN_FUNC(_win_atoi,1)
+	(const char *ptr)
+{
+	int i = simple_strtol(ptr, NULL, 10);
+	return i;
+}
+
+noregparm int WIN_FUNC(_win_isdigit,1)
+	(int c)
+{
+	return isdigit(c);
+}
+
+noregparm int WIN_FUNC(_win_isprint,1)
+	(int c)
+{
+	return isprint(c);
+}
+
+wstdcall s64 WIN_FUNC(_alldiv,2)
+	(s64 a, s64 b)
+{
+	return a / b;
+}
+
+wstdcall u64 WIN_FUNC(_aulldiv,2)
+	(u64 a, u64 b)
+{
+	return a / b;
+}
+
+wstdcall s64 WIN_FUNC(_allmul,2)
+	(s64 a, s64 b)
+{
+	return a * b;
+}
+
+wstdcall u64 WIN_FUNC(_aullmul,2)
+	(u64 a, u64 b)
+{
+	return a * b;
+}
+
+wstdcall s64 WIN_FUNC(_allrem,2)
+	(s64 a, s64 b)
+{
+	return a % b;
+}
+
+wstdcall u64 WIN_FUNC(_aullrem,2)
+	(u64 a, u64 b)
+{
+	return a % b;
+}
+
+regparm3 s64 WIN_FUNC(_allshl,2)
+	(s64 a, u8 b)
+{
+	return a << b;
+}
+
+regparm3 u64 WIN_FUNC(_aullshl,2)
+	(u64 a, u8 b)
+{
+	return a << b;
+}
+
+regparm3 s64 WIN_FUNC(_allshr,2)
+	(s64 a, u8 b)
+{
+	return a >> b;
+}
+
+regparm3 u64 WIN_FUNC(_aullshr,2)
+	(u64 a, u8 b)
+{
+	return a >> b;
+}
+
+int stricmp(const char *s1, const char *s2)
+{
+	while (*s1 && tolower(*s1) == tolower(*s2)) {
+		s1++;
+		s2++;
+	}
+	return *s1 - *s2;
+}
+
+void dump_bytes(const char *ctx, const u8 *from, int len)
+{
+	int i, j;
+	u8 *buf;
+
+	buf = kmalloc(len * 3 + 1, irql_gfp());
+	if (!buf) {
+		ERROR("couldn't allocate memory");
+		return;
+	}
+	for (i = j = 0; i < len; i++, j += 3) {
+		sprintf(&buf[j], "%02x ", from[i]);
+	}
+	buf[j] = 0;
+	printk(KERN_DEBUG "%s: %p: %s\n", ctx, from, buf);
+	kfree(buf);
+}
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/divdi3.c linux-5.6.11-ndis/3rdparty/ndiswrapper/divdi3.c
--- linux-5.6.11/3rdparty/ndiswrapper/divdi3.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/divdi3.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,329 @@
+/* 64-bit multiplication and division
+   Copyright (C) 1989, 1992-1999, 2000, 2001, 2002, 2003
+   Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#if BITS_PER_LONG != 32
+#error This is for 32-bit targets only
+#endif
+
+typedef unsigned int UQItype	__attribute__ ((mode (QI)));
+typedef          int SItype	__attribute__ ((mode (SI)));
+typedef unsigned int USItype	__attribute__ ((mode (SI)));
+typedef          int DItype	__attribute__ ((mode (DI)));
+typedef unsigned int UDItype	__attribute__ ((mode (DI)));
+#define Wtype SItype
+#define HWtype SItype
+#define DWtype DItype
+#define UWtype USItype
+#define UHWtype USItype
+#define UDWtype UDItype
+#define W_TYPE_SIZE 32
+
+#include "longlong.h"
+
+#if defined(__BIG_ENDIAN)
+struct DWstruct { Wtype high, low;};
+#elif defined(__LITTLE_ENDIAN)
+struct DWstruct { Wtype low, high;};
+#else
+#error Unhandled endianity
+#endif
+typedef union { struct DWstruct s; DWtype ll; } DWunion;
+
+/* Prototypes of exported functions.  */
+extern DWtype __divdi3 (DWtype u, DWtype v);
+extern DWtype __moddi3 (DWtype u, DWtype v);
+extern UDWtype __udivdi3 (UDWtype u, UDWtype v);
+extern UDWtype __umoddi3 (UDWtype u, UDWtype v);
+
+static UDWtype
+__udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp)
+{
+  DWunion ww;
+  DWunion nn, dd;
+  DWunion rr;
+  UWtype d0, d1, n0, n1, n2;
+  UWtype q0, q1;
+  UWtype b, bm;
+
+  nn.ll = n;
+  dd.ll = d;
+
+  d0 = dd.s.low;
+  d1 = dd.s.high;
+  n0 = nn.s.low;
+  n1 = nn.s.high;
+
+#if !UDIV_NEEDS_NORMALIZATION
+  if (d1 == 0)
+    {
+      if (d0 > n1)
+	{
+	  /* 0q = nn / 0D */
+
+	  udiv_qrnnd (q0, n0, n1, n0, d0);
+	  q1 = 0;
+
+	  /* Remainder in n0.  */
+	}
+      else
+	{
+	  /* qq = NN / 0d */
+
+	  if (d0 == 0)
+	    d0 = 1 / d0;	/* Divide intentionally by zero.  */
+
+	  udiv_qrnnd (q1, n1, 0, n1, d0);
+	  udiv_qrnnd (q0, n0, n1, n0, d0);
+
+	  /* Remainder in n0.  */
+	}
+
+      if (rp != NULL)
+	{
+	  rr.s.low = n0;
+	  rr.s.high = 0;
+	  *rp = rr.ll;
+	}
+    }
+
+#else /* UDIV_NEEDS_NORMALIZATION */
+
+  if (d1 == 0)
+    {
+      if (d0 > n1)
+	{
+	  /* 0q = nn / 0D */
+
+	  count_leading_zeros (bm, d0);
+
+	  if (bm != 0)
+	    {
+	      /* Normalize, i.e. make the most significant bit of the
+		 denominator set.  */
+
+	      d0 = d0 << bm;
+	      n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm));
+	      n0 = n0 << bm;
+	    }
+
+	  udiv_qrnnd (q0, n0, n1, n0, d0);
+	  q1 = 0;
+
+	  /* Remainder in n0 >> bm.  */
+	}
+      else
+	{
+	  /* qq = NN / 0d */
+
+	  if (d0 == 0)
+	    d0 = 1 / d0;	/* Divide intentionally by zero.  */
+
+	  count_leading_zeros (bm, d0);
+
+	  if (bm == 0)
+	    {
+	      /* From (n1 >= d0) /\ (the most significant bit of d0 is set),
+		 conclude (the most significant bit of n1 is set) /\ (the
+		 leading quotient digit q1 = 1).
+
+		 This special case is necessary, not an optimization.
+		 (Shifts counts of W_TYPE_SIZE are undefined.)  */
+
+	      n1 -= d0;
+	      q1 = 1;
+	    }
+	  else
+	    {
+	      /* Normalize.  */
+
+	      b = W_TYPE_SIZE - bm;
+
+	      d0 = d0 << bm;
+	      n2 = n1 >> b;
+	      n1 = (n1 << bm) | (n0 >> b);
+	      n0 = n0 << bm;
+
+	      udiv_qrnnd (q1, n1, n2, n1, d0);
+	    }
+
+	  /* n1 != d0...  */
+
+	  udiv_qrnnd (q0, n0, n1, n0, d0);
+
+	  /* Remainder in n0 >> bm.  */
+	}
+
+      if (rp != NULL)
+	{
+	  rr.s.low = n0 >> bm;
+	  rr.s.high = 0;
+	  *rp = rr.ll;
+	}
+    }
+#endif /* UDIV_NEEDS_NORMALIZATION */
+
+  else
+    {
+      if (d1 > n1)
+	{
+	  /* 00 = nn / DD */
+
+	  q0 = 0;
+	  q1 = 0;
+
+	  /* Remainder in n1n0.  */
+	  if (rp != NULL)
+	    {
+	      rr.s.low = n0;
+	      rr.s.high = n1;
+	      *rp = rr.ll;
+	    }
+	}
+      else
+	{
+	  /* 0q = NN / dd */
+
+	  count_leading_zeros (bm, d1);
+	  if (bm == 0)
+	    {
+	      /* From (n1 >= d1) /\ (the most significant bit of d1 is set),
+		 conclude (the most significant bit of n1 is set) /\ (the
+		 quotient digit q0 = 0 or 1).
+
+		 This special case is necessary, not an optimization.  */
+
+	      /* The condition on the next line takes advantage of that
+		 n1 >= d1 (true due to program flow).  */
+	      if (n1 > d1 || n0 >= d0)
+		{
+		  q0 = 1;
+		  sub_ddmmss (n1, n0, n1, n0, d1, d0);
+		}
+	      else
+		q0 = 0;
+
+	      q1 = 0;
+
+	      if (rp != NULL)
+		{
+		  rr.s.low = n0;
+		  rr.s.high = n1;
+		  *rp = rr.ll;
+		}
+	    }
+	  else
+	    {
+	      UWtype m1, m0;
+	      /* Normalize.  */
+
+	      b = W_TYPE_SIZE - bm;
+
+	      d1 = (d1 << bm) | (d0 >> b);
+	      d0 = d0 << bm;
+	      n2 = n1 >> b;
+	      n1 = (n1 << bm) | (n0 >> b);
+	      n0 = n0 << bm;
+
+	      udiv_qrnnd (q0, n1, n2, n1, d1);
+	      umul_ppmm (m1, m0, q0, d0);
+
+	      if (m1 > n1 || (m1 == n1 && m0 > n0))
+		{
+		  q0--;
+		  sub_ddmmss (m1, m0, m1, m0, d1, d0);
+		}
+
+	      q1 = 0;
+
+	      /* Remainder in (n1n0 - m1m0) >> bm.  */
+	      if (rp != NULL)
+		{
+		  sub_ddmmss (n1, n0, n1, n0, m1, m0);
+		  rr.s.low = (n1 << b) | (n0 >> bm);
+		  rr.s.high = n1 >> bm;
+		  *rp = rr.ll;
+		}
+	    }
+	}
+    }
+
+  ww.s.low = q0;
+  ww.s.high = q1;
+  return ww.ll;
+}
+
+DWtype
+__divdi3 (DWtype u, DWtype v)
+{
+  Wtype c = 0;
+  DWtype w;
+
+  if (u < 0)
+    {
+      c = ~c;
+      u = -u;
+    }
+  if (v < 0)
+    {
+      c = ~c;
+      v = -v;
+    }
+  w = __udivmoddi4 (u, v, NULL);
+  if (c)
+    w = -w;
+  return w;
+}
+
+DWtype
+__moddi3 (DWtype u, DWtype v)
+{
+  Wtype c = 0;
+  DWtype w;
+
+  if (u < 0)
+    {
+      c = ~c;
+      u = -u;
+    }
+  if (v < 0)
+    v = -v;
+  __udivmoddi4 (u, v, &w);
+  if (c)
+    w = -w;
+  return w;
+}
+
+UDWtype
+__udivdi3 (UDWtype u, UDWtype v)
+{
+  return __udivmoddi4 (u, v, NULL);
+}
+
+UDWtype
+__umoddi3 (UDWtype u, UDWtype v)
+{
+  UDWtype w;
+
+  __udivmoddi4 (u, v, &w);
+  return w;
+}
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/hal.c linux-5.6.11-ndis/3rdparty/ndiswrapper/hal.c
--- linux-5.6.11/3rdparty/ndiswrapper/hal.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/hal.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,157 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include "ntoskernel.h"
+#include "hal_exports.h"
+
+wstdcall void WIN_FUNC(WRITE_PORT_ULONG,2)
+	(ULONG_PTR port, ULONG value)
+{
+	outl(value, port);
+}
+
+wstdcall ULONG WIN_FUNC(READ_PORT_ULONG,1)
+	(ULONG_PTR port)
+{
+	return inl(port);
+}
+
+wstdcall void WIN_FUNC(WRITE_PORT_USHORT,2)
+	(ULONG_PTR port, USHORT value)
+{
+	outw(value, port);
+}
+
+wstdcall USHORT WIN_FUNC(READ_PORT_USHORT,1)
+	(ULONG_PTR port)
+{
+	return inw(port);
+}
+
+wstdcall void WIN_FUNC(WRITE_PORT_UCHAR,2)
+	(ULONG_PTR port, UCHAR value)
+{
+	outb(value, port);
+}
+
+wstdcall UCHAR WIN_FUNC(READ_PORT_UCHAR,1)
+	(ULONG_PTR port)
+{
+	return inb(port);
+}
+
+wstdcall void WIN_FUNC(WRITE_PORT_BUFFER_USHORT,3)
+	(ULONG_PTR port, USHORT *buf, ULONG count)
+{
+	outsw(port, buf, count);
+}
+
+wstdcall void WIN_FUNC(READ_PORT_BUFFER_USHORT,3)
+	(ULONG_PTR port, USHORT *buf, ULONG count)
+{
+	insw(port, buf, count);
+}
+
+wstdcall void WIN_FUNC(WRITE_PORT_BUFFER_ULONG,3)
+	(ULONG_PTR port, ULONG *buf, ULONG count)
+{
+	outsl(port, buf, count);
+}
+
+wstdcall void WIN_FUNC(READ_PORT_BUFFER_ULONG,3)
+	(ULONG_PTR port, ULONG *buf, ULONG count)
+{
+	insl(port, buf, count);
+}
+
+wstdcall USHORT WIN_FUNC(READ_REGISTER_USHORT,1)
+	(void __iomem *reg)
+{
+	return readw(reg);
+}
+
+wstdcall void WIN_FUNC(WRITE_REGISTER_ULONG,2)
+	(void __iomem *reg, UINT val)
+{
+	writel(val, reg);
+}
+
+wstdcall void WIN_FUNC(WRITE_REGISTER_USHORT,2)
+	(void __iomem *reg, USHORT val)
+{
+	writew(val, reg);
+}
+
+wstdcall void WIN_FUNC(WRITE_REGISTER_UCHAR,2)
+	(void __iomem *reg, UCHAR val)
+{
+	writeb(val, reg);
+}
+
+wstdcall void WIN_FUNC(KeStallExecutionProcessor,1)
+	(ULONG usecs)
+{
+	udelay(usecs);
+}
+
+wstdcall KIRQL WIN_FUNC(KeGetCurrentIrql,0)
+	(void)
+{
+	return current_irql();
+}
+
+wfastcall KIRQL WIN_FUNC(KfRaiseIrql,1)
+	(KIRQL newirql)
+{
+	return raise_irql(newirql);
+}
+
+wfastcall void WIN_FUNC(KfLowerIrql,1)
+	(KIRQL oldirql)
+{
+	lower_irql(oldirql);
+}
+
+wfastcall KIRQL WIN_FUNC(KfAcquireSpinLock,1)
+	(NT_SPIN_LOCK *lock)
+{
+	return nt_spin_lock_irql(lock, DISPATCH_LEVEL);
+}
+
+wfastcall void WIN_FUNC(KfReleaseSpinLock,2)
+	(NT_SPIN_LOCK *lock, KIRQL oldirql)
+{
+	nt_spin_unlock_irql(lock, oldirql);
+}
+
+wfastcall void WIN_FUNC(KefAcquireSpinLockAtDpcLevel,1)
+	(NT_SPIN_LOCK *lock)
+{
+#ifdef DEBUG_IRQL
+	if (current_irql() != DISPATCH_LEVEL)
+		ERROR("irql != DISPATCH_LEVEL");
+#endif
+	nt_spin_lock(lock);
+}
+
+wfastcall void WIN_FUNC(KefReleaseSpinLockFromDpcLevel,1)
+	(NT_SPIN_LOCK *lock)
+{
+#ifdef DEBUG_IRQL
+	if (current_irql() != DISPATCH_LEVEL)
+		ERROR("irql != DISPATCH_LEVEL");
+#endif
+	nt_spin_unlock(lock);
+}
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/iw_ndis.c linux-5.6.11-ndis/3rdparty/ndiswrapper/iw_ndis.c
--- linux-5.6.11/3rdparty/ndiswrapper/iw_ndis.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/iw_ndis.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,2002 @@
+ /*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include <linux/wireless.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_arp.h>
+#include <linux/usb.h>
+#include <linux/random.h>
+
+#include <net/iw_handler.h>
+#include <linux/rtnetlink.h>
+#include <asm/uaccess.h>
+
+#include "iw_ndis.h"
+#include "wrapndis.h"
+
+#ifdef CONFIG_WIRELESS_EXT
+
+static int freq_chan[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442,
+			   2447, 2452, 2457, 2462, 2467, 2472, 2484 };
+
+static const char *network_names[] = {"IEEE 802.11FH", "IEEE 802.11b",
+				      "IEEE 802.11a", "IEEE 802.11g", "Auto"};
+
+static int set_essid(struct ndis_device *wnd, const char *ssid, int ssid_len)
+{
+	NDIS_STATUS res;
+	struct ndis_essid req;
+
+	if (ssid_len > NDIS_ESSID_MAX_SIZE)
+		return -EINVAL;
+
+	memset(&req, 0, sizeof(req));
+	req.length = ssid_len;
+	if (ssid_len)
+		memcpy(&req.essid, ssid, ssid_len);
+
+	res = mp_set(wnd, OID_802_11_SSID, &req, sizeof(req));
+	if (res) {
+		WARNING("setting essid failed (%08X)", res);
+		EXIT2(return -EINVAL);
+	}
+	memcpy(&wnd->essid, &req, sizeof(req));
+	EXIT2(return 0);
+}
+
+static int set_iw_auth_mode(struct ndis_device *wnd, int wpa_version,
+			    int auth_80211_alg)
+{
+	NDIS_STATUS res;
+	ULONG auth_mode;
+
+	ENTER2("%d, %d", wpa_version, auth_80211_alg);
+	if (wpa_version & IW_AUTH_WPA_VERSION_WPA2) {
+		if (wnd->iw_auth_key_mgmt & IW_AUTH_KEY_MGMT_802_1X)
+			auth_mode = Ndis802_11AuthModeWPA2;
+		else
+			auth_mode = Ndis802_11AuthModeWPA2PSK;
+	} else if (wpa_version & IW_AUTH_WPA_VERSION_WPA) {
+		if (wnd->iw_auth_key_mgmt & IW_AUTH_KEY_MGMT_802_1X)
+			auth_mode = Ndis802_11AuthModeWPA;
+		else if (wnd->iw_auth_key_mgmt & IW_AUTH_KEY_MGMT_PSK)
+			auth_mode = Ndis802_11AuthModeWPAPSK;
+		else
+			auth_mode = Ndis802_11AuthModeWPANone;
+	} else if (auth_80211_alg & IW_AUTH_ALG_SHARED_KEY) {
+		if (auth_80211_alg & IW_AUTH_ALG_OPEN_SYSTEM)
+			auth_mode = Ndis802_11AuthModeAutoSwitch;
+		else
+			auth_mode = Ndis802_11AuthModeShared;
+	} else
+		auth_mode = Ndis802_11AuthModeOpen;
+
+	res = mp_set_int(wnd, OID_802_11_AUTHENTICATION_MODE, auth_mode);
+	if (res) {
+		WARNING("setting auth mode to %u failed (%08X)",
+			auth_mode, res);
+		if (res == NDIS_STATUS_INVALID_DATA)
+			EXIT2(return -EINVAL);
+		return -EOPNOTSUPP;
+	}
+	wnd->iw_auth_wpa_version = wpa_version;
+	wnd->iw_auth_80211_alg = auth_80211_alg;
+	EXIT2(return 0);
+}
+
+static int set_auth_mode(struct ndis_device *wnd)
+{
+	return set_iw_auth_mode(wnd, wnd->iw_auth_wpa_version,
+				wnd->iw_auth_80211_alg);
+}
+
+static enum ndis_priv_filter ndis_priv_mode(struct ndis_device *wnd)
+{
+	if (wnd->iw_auth_wpa_version & IW_AUTH_WPA_VERSION_WPA2 ||
+	    wnd->iw_auth_wpa_version & IW_AUTH_WPA_VERSION_WPA)
+		return Ndis802_11PrivFilter8021xWEP;
+	else
+		return Ndis802_11PrivFilterAcceptAll;
+}
+
+static int set_priv_filter(struct ndis_device *wnd)
+{
+	NDIS_STATUS res;
+	ULONG flags;
+
+	flags = ndis_priv_mode(wnd);
+	ENTER2("filter: %d", flags);
+	res = mp_set_int(wnd, OID_802_11_PRIVACY_FILTER, flags);
+	if (res)
+		TRACE2("setting privacy filter to %d failed (%08X)",
+		       flags, res);
+	EXIT2(return 0);
+}
+
+static int set_encr_mode(struct ndis_device *wnd)
+{
+	return set_iw_encr_mode(wnd, wnd->iw_auth_cipher_pairwise,
+				wnd->iw_auth_cipher_group);
+}
+
+static int set_assoc_params(struct ndis_device *wnd)
+{
+	TRACE2("wpa_version=0x%x auth_alg=0x%x key_mgmt=0x%x "
+	       "cipher_pairwise=0x%x cipher_group=0x%x",
+	       wnd->iw_auth_wpa_version, wnd->iw_auth_80211_alg,
+	       wnd->iw_auth_key_mgmt, wnd->iw_auth_cipher_pairwise,
+	       wnd->iw_auth_cipher_group);
+	set_auth_mode(wnd);
+	set_priv_filter(wnd);
+	set_encr_mode(wnd);
+	return 0;
+}
+
+static int iw_set_essid(struct net_device *dev, struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	int length;
+
+	ENTER2("");
+	/* there is no way to turn off essid other than to set to
+	 * random bytes; instead, we use off to mean any */
+	if (wrqu->essid.flags) {
+		length = wrqu->essid.length;
+		/* Strip '\0' appended by wireless extensions 19 and older */
+		if (length > 0 && extra[length - 1] == '\0')
+			length--;
+		TRACE2("%d", length);
+		if (length <= 0 || length > NDIS_ESSID_MAX_SIZE)
+			EXIT2(return -EINVAL);
+	} else
+		length = 0;
+
+	set_assoc_params(wnd);
+
+	if (set_essid(wnd, extra, length))
+		EXIT2(return -EINVAL);
+
+	EXIT2(return 0);
+}
+
+static int iw_get_essid(struct net_device *dev, struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	NDIS_STATUS res;
+	struct ndis_essid req;
+
+	ENTER2("");
+	memset(&req, 0, sizeof(req));
+	res = mp_query(wnd, OID_802_11_SSID, &req, sizeof(req));
+	if (res) {
+		WARNING("getting essid failed (%08X)", res);
+		EXIT2(return -EOPNOTSUPP);
+	}
+	memcpy(extra, req.essid, req.length);
+	if (req.length > 0)
+		wrqu->essid.flags = 1;
+	else
+		wrqu->essid.flags = 0;
+	wrqu->essid.length = req.length;
+	EXIT2(return 0);
+}
+
+/* index must be 0 - N, as per NDIS */
+static int add_wep_key(struct ndis_device *wnd, char *key, int key_len,
+		       int index)
+{
+	struct ndis_encr_key ndis_key;
+	NDIS_STATUS res;
+
+	ENTER2("key index: %d, length: %d", index, key_len);
+	if (key_len <= 0 || key_len > NDIS_ENCODING_TOKEN_MAX) {
+		WARNING("invalid key length (%d)", key_len);
+		EXIT2(return -EINVAL);
+	}
+	if (index < 0 || index >= MAX_ENCR_KEYS) {
+		WARNING("invalid key index (%d)", index);
+		EXIT2(return -EINVAL);
+	}
+	ndis_key.struct_size = sizeof(ndis_key);
+	ndis_key.length = key_len;
+	memcpy(&ndis_key.key, key, key_len);
+	ndis_key.index = index;
+
+	if (index == wnd->encr_info.tx_key_index) {
+		ndis_key.index |= (1 << 31);
+		res = set_iw_encr_mode(wnd, IW_AUTH_CIPHER_WEP104,
+				       IW_AUTH_CIPHER_NONE);
+		if (res)
+			WARNING("encryption couldn't be enabled (%08X)", res);
+	}
+	TRACE2("key %d: " MACSTRSEP, index, MAC2STR(key));
+	res = mp_set(wnd, OID_802_11_ADD_WEP, &ndis_key, sizeof(ndis_key));
+	if (res) {
+		WARNING("adding encryption key %d failed (%08X)",
+			index+1, res);
+		EXIT2(return -EINVAL);
+	}
+
+	/* Atheros driver messes up ndis_key during ADD_WEP, so
+	 * don't rely on that; instead use info in key and key_len */
+	wnd->encr_info.keys[index].length = key_len;
+	memcpy(&wnd->encr_info.keys[index].key, key, key_len);
+
+	EXIT2(return 0);
+}
+
+static int set_infra_mode(struct ndis_device *wnd,
+			  enum ndis_infrastructure_mode mode)
+{
+	NDIS_STATUS res;
+	unsigned int i;
+
+	ENTER2("%d", mode);
+	res = mp_query_int(wnd, OID_802_11_INFRASTRUCTURE_MODE,
+			   &wnd->infrastructure_mode);
+	if (res != NDIS_STATUS_SUCCESS) {
+		WARNING("getting operating mode failed (%08X)", res);
+		EXIT2(return -EINVAL);
+	}
+	if (wnd->infrastructure_mode == mode)
+		EXIT2(return 0);
+	res = mp_set_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, mode);
+	if (res) {
+		WARNING("setting operating mode to %d failed (%08X)",
+			mode, res);
+		EXIT2(return -EINVAL);
+	}
+	/* NDIS drivers clear keys when infrastructure mode is
+	 * changed. But Linux tools assume otherwise. So set the
+	 * keys */
+	if (wnd->iw_auth_key_mgmt == 0 ||
+	    wnd->iw_auth_key_mgmt == IW_AUTH_KEY_MGMT_802_1X) {
+		for (i = 0; i < MAX_ENCR_KEYS; i++) {
+			if (wnd->encr_info.keys[i].length > 0)
+				add_wep_key(wnd, wnd->encr_info.keys[i].key,
+					    wnd->encr_info.keys[i].length, i);
+		}
+	}
+	wnd->infrastructure_mode = mode;
+	EXIT2(return 0);
+}
+
+static int iw_set_infra_mode(struct net_device *dev,
+			     struct iw_request_info *info,
+			     union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	enum ndis_infrastructure_mode ndis_mode;
+
+	ENTER2("%d", wrqu->mode);
+	switch (wrqu->mode) {
+	case IW_MODE_ADHOC:
+		ndis_mode = Ndis802_11IBSS;
+		break;
+	case IW_MODE_INFRA:
+		ndis_mode = Ndis802_11Infrastructure;
+		break;
+	case IW_MODE_AUTO:
+		ndis_mode = Ndis802_11AutoUnknown;
+		break;
+	default:
+		EXIT2(return -EINVAL);
+	}
+
+	if (set_infra_mode(wnd, ndis_mode))
+		EXIT2(return -EINVAL);
+
+	EXIT2(return 0);
+}
+
+static int iw_get_infra_mode(struct net_device *dev,
+			     struct iw_request_info *info,
+			     union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	int ndis_mode, iw_mode;
+	NDIS_STATUS res;
+
+	ENTER2("");
+	res = mp_query_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, &ndis_mode);
+	if (res) {
+		WARNING("getting operating mode failed (%08X)", res);
+		EXIT2(return -EOPNOTSUPP);
+	}
+
+	switch (ndis_mode) {
+	case Ndis802_11IBSS:
+		iw_mode = IW_MODE_ADHOC;
+		break;
+	case Ndis802_11Infrastructure:
+		iw_mode = IW_MODE_INFRA;
+		break;
+	case Ndis802_11AutoUnknown:
+		iw_mode = IW_MODE_AUTO;
+		break;
+	default:
+		ERROR("invalid operating mode (%u)", ndis_mode);
+		EXIT2(return -EINVAL);
+	}
+	wrqu->mode = iw_mode;
+	EXIT2(return 0);
+}
+
+static const char *network_type_to_name(int net_type)
+{
+	if (net_type >= 0 && net_type < ARRAY_SIZE(network_names))
+		return network_names[net_type];
+	else
+		return network_names[ARRAY_SIZE(network_names) - 1];
+}
+
+static int iw_get_network_type(struct net_device *dev,
+			       struct iw_request_info *info,
+			       union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	unsigned int network_type;
+	NDIS_STATUS res;
+
+	ENTER2("");
+	res = mp_query_int(wnd, OID_802_11_NETWORK_TYPE_IN_USE,
+			   &network_type);
+	if (res) {
+		WARNING("getting network type failed: %08X", res);
+		network_type = -1;
+	}
+	strncpy(wrqu->name, network_type_to_name(network_type),
+		sizeof(wrqu->name) - 1);
+	wrqu->name[sizeof(wrqu->name)-1] = 0;
+	return 0;
+}
+
+static int iw_get_freq(struct net_device *dev, struct iw_request_info *info,
+		       union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	NDIS_STATUS res;
+	struct ndis_configuration req;
+
+	ENTER2("");
+	memset(&req, 0, sizeof(req));
+	res = mp_query(wnd, OID_802_11_CONFIGURATION, &req, sizeof(req));
+	if (res) {
+		WARNING("getting configuration failed (%08X)", res);
+		EXIT2(return -EOPNOTSUPP);
+	}
+
+	memset(&(wrqu->freq), 0, sizeof(struct iw_freq));
+
+	/* see comment in wireless.h above the "struct iw_freq"
+	   definition for an explanation of this if
+	   NOTE: 1000000 is due to the kHz
+	*/
+	if (req.ds_config > 1000000) {
+		wrqu->freq.m = req.ds_config / 10;
+		wrqu->freq.e = 1;
+	}
+	else
+		wrqu->freq.m = req.ds_config;
+
+	/* convert from kHz to Hz */
+	wrqu->freq.e += 3;
+
+	return 0;
+}
+
+static int iw_set_freq(struct net_device *dev, struct iw_request_info *info,
+		       union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	NDIS_STATUS res;
+	struct ndis_configuration req;
+
+	ENTER2("");
+	/* this OID is valid only when not associated */
+	if (netif_carrier_ok(wnd->net_dev))
+		EXIT2(return 0);
+	memset(&req, 0, sizeof(req));
+	res = mp_query(wnd, OID_802_11_CONFIGURATION, &req, sizeof(req));
+	if (res) {
+		WARNING("getting configuration failed (%08X)", res);
+		EXIT2(return 0);
+	}
+
+	if (wrqu->freq.m < 1000 && wrqu->freq.e == 0) {
+		if (wrqu->freq.m >= 1 && wrqu->freq.m <= ARRAY_SIZE(freq_chan))
+			req.ds_config = freq_chan[wrqu->freq.m - 1] * 1000;
+		else
+			return -EINVAL;
+	} else {
+		int i;
+		req.ds_config = wrqu->freq.m;
+		for (i = wrqu->freq.e; i > 0; i--)
+			req.ds_config *= 10;
+		req.ds_config /= 1000;
+	}
+	res = mp_set(wnd, OID_802_11_CONFIGURATION, &req, sizeof(req));
+	if (res)
+		WARNING("setting configuration failed (%08X)", res);
+	return 0;
+}
+
+static int iw_get_tx_power(struct net_device *dev, struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	ndis_tx_power_level ndis_power;
+	NDIS_STATUS res;
+
+	ENTER2("");
+	res = mp_query(wnd, OID_802_11_TX_POWER_LEVEL,
+		       &ndis_power, sizeof(ndis_power));
+	if (res)
+		return -EOPNOTSUPP;
+	wrqu->txpower.flags = IW_TXPOW_MWATT;
+	wrqu->txpower.disabled = 0;
+	wrqu->txpower.fixed = 0;
+	wrqu->txpower.value = ndis_power;
+	return 0;
+}
+
+static int iw_set_tx_power(struct net_device *dev, struct iw_request_info *info,
+			   union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	ndis_tx_power_level ndis_power;
+	NDIS_STATUS res;
+
+	ENTER2("");
+	if (wrqu->txpower.disabled)
+		ndis_power = 0;
+	else {
+		if (wrqu->txpower.flags == IW_TXPOW_MWATT)
+			ndis_power = wrqu->txpower.value;
+		else { // wrqu->txpower.flags == IW_TXPOW_DBM
+			if (wrqu->txpower.value > 20)
+				ndis_power = 128;
+			else if (wrqu->txpower.value < -43)
+				ndis_power = 127;
+			else {
+				signed char tmp;
+				tmp = wrqu->txpower.value;
+				tmp = -12 - tmp;
+				tmp <<= 2;
+				ndis_power = (unsigned char)tmp;
+			}
+		}
+	}
+	TRACE2("%d", ndis_power);
+	res = mp_set(wnd, OID_802_11_TX_POWER_LEVEL,
+		     &ndis_power, sizeof(ndis_power));
+	if (res)
+		EXIT2(return -EOPNOTSUPP);
+	if (ndis_power == 0)
+		res = disassociate(wnd, 0);
+	EXIT2(return 0);
+}
+
+static int iw_get_bitrate(struct net_device *dev, struct iw_request_info *info,
+			  union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	ULONG ndis_rate;
+	int res;
+
+	ENTER2("");
+	res = mp_query(wnd, OID_GEN_LINK_SPEED, &ndis_rate, sizeof(ndis_rate));
+	if (res) {
+		WARNING("getting bitrate failed (%08X)", res);
+		ndis_rate = 0;
+	}
+
+	wrqu->bitrate.value = ndis_rate * 100;
+	return 0;
+}
+
+static int iw_set_bitrate(struct net_device *dev, struct iw_request_info *info,
+			  union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	int i, n;
+	NDIS_STATUS res;
+	UCHAR rates[NDIS_MAX_RATES_EX];
+
+	ENTER2("");
+	if (wrqu->bitrate.fixed == 0)
+		EXIT2(return 0);
+
+	res = mp_query_info(wnd, OID_802_11_SUPPORTED_RATES, &rates,
+			    sizeof(rates), &n, NULL);
+	if (res) {
+		WARNING("getting bit rate failed (%08X)", res);
+		EXIT2(return 0);
+	}
+	for (i = 0; i < n; i++) {
+		if (rates[i] & 0x80)
+			continue;
+		if ((rates[i] & 0x7f) * 500000 > wrqu->bitrate.value) {
+			TRACE2("setting rate %d to 0",
+			       (rates[i] & 0x7f) * 500000);
+			rates[i] = 0;
+		}
+	}
+
+	res = mp_set(wnd, OID_802_11_DESIRED_RATES, &rates, n);
+	if (res) {
+		WARNING("setting bit rate failed (%08X)", res);
+		EXIT2(return 0);
+	}
+
+	return 0;
+}
+
+static int iw_set_dummy(struct net_device *dev, struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
+{
+	/* Do nothing. Used for ioctls that are not implemented. */
+	return 0;
+}
+
+static int iw_get_rts_threshold(struct net_device *dev,
+				struct iw_request_info *info,
+				union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	ndis_rts_threshold threshold;
+	NDIS_STATUS res;
+
+	ENTER2("");
+	res = mp_query(wnd, OID_802_11_RTS_THRESHOLD,
+		       &threshold, sizeof(threshold));
+	if (res)
+		return -EOPNOTSUPP;
+
+	wrqu->rts.value = threshold;
+	return 0;
+}
+
+static int iw_set_rts_threshold(struct net_device *dev,
+				struct iw_request_info *info,
+				union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	ndis_rts_threshold threshold;
+	NDIS_STATUS res;
+
+	ENTER2("");
+	threshold = wrqu->rts.value;
+	res = mp_set(wnd, OID_802_11_RTS_THRESHOLD,
+		     &threshold, sizeof(threshold));
+	if (res == NDIS_STATUS_INVALID_DATA)
+		return -EINVAL;
+	if (res)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static int iw_get_frag_threshold(struct net_device *dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	ndis_fragmentation_threshold frag_threshold;
+	NDIS_STATUS res;
+
+	ENTER2("");
+	res = mp_query(wnd, OID_802_11_FRAGMENTATION_THRESHOLD,
+		       &frag_threshold, sizeof(frag_threshold));
+	if (res)
+		return -ENOTSUPP;
+
+	wrqu->frag.value = frag_threshold;
+	return 0;
+}
+
+static int iw_set_frag_threshold(struct net_device *dev,
+				 struct iw_request_info *info,
+				 union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	ndis_rts_threshold threshold;
+	NDIS_STATUS res;
+
+	ENTER2("");
+	threshold = wrqu->frag.value;
+	res = mp_set(wnd, OID_802_11_FRAGMENTATION_THRESHOLD,
+		     &threshold, sizeof(threshold));
+	if (res == NDIS_STATUS_INVALID_DATA)
+		return -EINVAL;
+	if (res)
+		return -EOPNOTSUPP;
+	return 0;
+}
+
+int get_ap_address(struct ndis_device *wnd, mac_address ap_addr)
+{
+	NDIS_STATUS res;
+
+	res = mp_query(wnd, OID_802_11_BSSID, ap_addr, ETH_ALEN);
+	TRACE2(MACSTRSEP, MAC2STR(ap_addr));
+	if (res) {
+		TRACE2("res: %08X", res);
+		memset(ap_addr, 0x0, ETH_ALEN);
+		EXIT2(return -EOPNOTSUPP);
+	}
+	EXIT2(return 0);
+}
+
+static int iw_get_ap_address(struct net_device *dev,
+			     struct iw_request_info *info,
+			     union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	mac_address ap_addr;
+
+	ENTER2("");
+	get_ap_address(wnd, ap_addr);
+	memcpy(wrqu->ap_addr.sa_data, ap_addr, ETH_ALEN);
+	wrqu->ap_addr.sa_family = ARPHRD_ETHER;
+	EXIT2(return 0);
+}
+
+static int iw_set_ap_address(struct net_device *dev,
+			     struct iw_request_info *info,
+			     union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	NDIS_STATUS res;
+	mac_address ap_addr;
+
+	ENTER2("");
+	memcpy(ap_addr, wrqu->ap_addr.sa_data, ETH_ALEN);
+	TRACE2(MACSTRSEP, MAC2STR(ap_addr));
+	res = mp_set(wnd, OID_802_11_BSSID, ap_addr, ETH_ALEN);
+	/* user apps may set ap's mac address, which is not required;
+	 * they may fail to work if this function fails, so return
+	 * success */
+	if (res)
+		WARNING("setting AP mac address failed (%08X)", res);
+
+	EXIT2(return 0);
+}
+
+int set_ndis_auth_mode(struct ndis_device *wnd, ULONG auth_mode)
+{
+	NDIS_STATUS res;
+
+	ENTER2("%d", auth_mode);
+	res = mp_set_int(wnd, OID_802_11_AUTHENTICATION_MODE, auth_mode);
+	if (res) {
+		WARNING("setting auth mode to %u failed (%08X)",
+			auth_mode, res);
+		if (res == NDIS_STATUS_INVALID_DATA)
+			EXIT2(return -EINVAL);
+		return -EOPNOTSUPP;
+	}
+	switch (auth_mode) {
+	case Ndis802_11AuthModeWPA:
+		wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA;
+		wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_802_1X;
+		break;
+	case Ndis802_11AuthModeWPAPSK:
+		wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA;
+		wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_PSK;
+        break;
+	case Ndis802_11AuthModeWPANone:
+		wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED;
+		wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_PSK;
+		break;
+	case Ndis802_11AuthModeWPA2:
+		wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA2;
+		wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_802_1X;
+		break;
+	case Ndis802_11AuthModeWPA2PSK:
+		wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA2;
+		wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_PSK;
+		break;
+	case Ndis802_11AuthModeOpen:
+		wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED;
+		wnd->iw_auth_80211_alg = IW_AUTH_ALG_OPEN_SYSTEM;
+		break;
+	case Ndis802_11AuthModeShared:
+		wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED;
+		wnd->iw_auth_80211_alg = IW_AUTH_ALG_SHARED_KEY;
+		break;
+	case Ndis802_11AuthModeAutoSwitch:
+		wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED;
+		wnd->iw_auth_80211_alg = IW_AUTH_ALG_SHARED_KEY;
+		wnd->iw_auth_80211_alg |= IW_AUTH_ALG_OPEN_SYSTEM;
+		break;
+	default:
+		WARNING("invalid authentication algorithm: %d", auth_mode);
+		break;
+	}
+	EXIT2(return 0);
+}
+
+int get_ndis_auth_mode(struct ndis_device *wnd)
+{
+	ULONG mode;
+	NDIS_STATUS res;
+
+	res = mp_query_int(wnd, OID_802_11_AUTHENTICATION_MODE, &mode);
+	if (res) {
+		WARNING("getting authentication mode failed (%08X)", res);
+		EXIT2(return -EOPNOTSUPP);
+	}
+	TRACE2("%d", mode);
+	return mode;
+}
+
+int set_iw_encr_mode(struct ndis_device *wnd, int cipher_pairwise,
+		     int cipher_groupwise)
+{
+	NDIS_STATUS res;
+	ULONG ndis_mode;
+
+	ENTER2("%d, %d", cipher_pairwise, cipher_groupwise);
+	if (cipher_pairwise & IW_AUTH_CIPHER_CCMP)
+		ndis_mode = Ndis802_11Encryption3Enabled;
+	else if (cipher_pairwise & IW_AUTH_CIPHER_TKIP)
+		ndis_mode = Ndis802_11Encryption2Enabled;
+	else if (cipher_pairwise &
+		 (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104))
+		ndis_mode = Ndis802_11Encryption1Enabled;
+	else if (cipher_groupwise & IW_AUTH_CIPHER_CCMP)
+		ndis_mode = Ndis802_11Encryption3Enabled;
+	else if (cipher_groupwise & IW_AUTH_CIPHER_TKIP)
+		ndis_mode = Ndis802_11Encryption2Enabled;
+	else
+		ndis_mode = Ndis802_11EncryptionDisabled;
+
+	res = mp_set_int(wnd, OID_802_11_ENCRYPTION_STATUS, ndis_mode);
+	if (res) {
+		WARNING("setting encryption mode to %u failed (%08X)",
+			ndis_mode, res);
+		if (res == NDIS_STATUS_INVALID_DATA)
+			EXIT2(return -EINVAL);
+		return -EOPNOTSUPP;
+	}
+	wnd->iw_auth_cipher_pairwise = cipher_pairwise;
+	wnd->iw_auth_cipher_group = cipher_groupwise;
+	EXIT2(return 0);
+}
+
+int get_ndis_encr_mode(struct ndis_device *wnd)
+{
+	ULONG mode;
+	NDIS_STATUS res;
+
+	ENTER2("");
+	res = mp_query_int(wnd, OID_802_11_ENCRYPTION_STATUS, &mode);
+	if (res) {
+		WARNING("getting encryption status failed (%08X)", res);
+		EXIT2(return -EOPNOTSUPP);
+	} else
+		EXIT2(return mode);
+}
+
+static int iw_get_encr(struct net_device *dev, struct iw_request_info *info,
+		       union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	int index, mode;
+	struct encr_info *encr_info = &wnd->encr_info;
+
+	ENTER2("wnd = %p", wnd);
+	wrqu->data.length = 0;
+	extra[0] = 0;
+
+	index = (wrqu->encoding.flags & IW_ENCODE_INDEX);
+	TRACE2("index = %u", index);
+	if (index > 0)
+		index--;
+	else
+		index = encr_info->tx_key_index;
+
+	if (index < 0 || index >= MAX_ENCR_KEYS) {
+		WARNING("encryption index out of range (%u)", index);
+		EXIT2(return -EINVAL);
+	}
+
+	if (index != encr_info->tx_key_index) {
+		if (encr_info->keys[index].length > 0) {
+			wrqu->data.flags |= IW_ENCODE_ENABLED;
+			wrqu->data.length = encr_info->keys[index].length;
+			memcpy(extra, encr_info->keys[index].key,
+			       encr_info->keys[index].length);
+		}
+		else
+			wrqu->data.flags |= IW_ENCODE_DISABLED;
+
+		EXIT2(return 0);
+	}
+
+	/* transmit key */
+	mode = get_ndis_encr_mode(wnd);
+	if (mode < 0)
+		EXIT2(return -EOPNOTSUPP);
+
+	if (mode == Ndis802_11EncryptionDisabled ||
+	    mode == Ndis802_11EncryptionNotSupported)
+		wrqu->data.flags |= IW_ENCODE_DISABLED;
+	else {
+		if (mode == Ndis802_11Encryption1KeyAbsent ||
+		    mode == Ndis802_11Encryption2KeyAbsent ||
+		    mode == Ndis802_11Encryption3KeyAbsent)
+			wrqu->data.flags |= IW_ENCODE_NOKEY;
+		else {
+			wrqu->data.flags |= IW_ENCODE_ENABLED;
+			wrqu->encoding.flags |= index+1;
+			wrqu->data.length = encr_info->keys[index].length;
+			memcpy(extra, encr_info->keys[index].key,
+			       encr_info->keys[index].length);
+		}
+	}
+	mode = get_ndis_auth_mode(wnd);
+	if (mode < 0)
+		EXIT2(return -EOPNOTSUPP);
+
+	if (mode == Ndis802_11AuthModeOpen)
+		wrqu->data.flags |= IW_ENCODE_OPEN;
+	else if (mode == Ndis802_11AuthModeAutoSwitch)
+		wrqu->data.flags |= IW_ENCODE_RESTRICTED;
+	else // Ndis802_11AuthModeAutoSwitch, Ndis802_11AuthModeWPA etc.
+		wrqu->data.flags |= IW_ENCODE_RESTRICTED;
+
+	EXIT2(return 0);
+}
+
+/* remove_key is for both wep and wpa */
+static int remove_key(struct ndis_device *wnd, int index,
+		      mac_address bssid)
+{
+	NDIS_STATUS res;
+	if (wnd->encr_info.keys[index].length == 0)
+		EXIT2(return 0);
+	wnd->encr_info.keys[index].length = 0;
+	memset(&wnd->encr_info.keys[index].key, 0,
+	       sizeof(wnd->encr_info.keys[index].length));
+	if (wnd->iw_auth_cipher_pairwise == IW_AUTH_CIPHER_TKIP ||
+	    wnd->iw_auth_cipher_pairwise == IW_AUTH_CIPHER_CCMP ||
+	    wnd->iw_auth_cipher_group == IW_AUTH_CIPHER_TKIP ||
+	    wnd->iw_auth_cipher_group == IW_AUTH_CIPHER_CCMP) {
+		struct ndis_remove_key rmkey;
+		rmkey.struct_size = sizeof(rmkey);
+		rmkey.index = index;
+		if (bssid) {
+			/* pairwise key */
+			if (memcmp(bssid, "\xff\xff\xff\xff\xff\xff",
+				   ETH_ALEN) != 0)
+				rmkey.index |= (1 << 30);
+			memcpy(rmkey.bssid, bssid, sizeof(rmkey.bssid));
+		} else
+			memset(rmkey.bssid, 0xff, sizeof(rmkey.bssid));
+		if (mp_set(wnd, OID_802_11_REMOVE_KEY, &rmkey, sizeof(rmkey)))
+			EXIT2(return -EINVAL);
+	} else {
+		ndis_key_index keyindex = index;
+		res = mp_set_int(wnd, OID_802_11_REMOVE_WEP, keyindex);
+		if (res) {
+			WARNING("removing encryption key %d failed (%08X)",
+				keyindex, res);
+			EXIT2(return -EINVAL);
+		}
+	}
+	/* if it is transmit key, disable encryption */
+	if (index == wnd->encr_info.tx_key_index) {
+		res = set_iw_encr_mode(wnd, IW_AUTH_CIPHER_NONE,
+				       IW_AUTH_CIPHER_NONE);
+		if (res)
+			WARNING("changing encr status failed (%08X)", res);
+	}
+	TRACE2("key %d removed", index);
+	EXIT2(return 0);
+}
+
+static int iw_set_wep(struct net_device *dev, struct iw_request_info *info,
+		      union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	NDIS_STATUS res;
+	unsigned int index, key_len;
+	struct encr_info *encr_info = &wnd->encr_info;
+	unsigned char *key;
+
+	ENTER2("");
+	index = (wrqu->encoding.flags & IW_ENCODE_INDEX);
+	TRACE2("index = %u", index);
+
+	/* iwconfig gives index as 1 - N */
+	if (index > 0)
+		index--;
+	else
+		index = encr_info->tx_key_index;
+
+	if (index >= MAX_ENCR_KEYS) {
+		WARNING("encryption index out of range (%u)", index);
+		EXIT2(return -EINVAL);
+	}
+
+	/* remove key if disabled */
+	if (wrqu->data.flags & IW_ENCODE_DISABLED) {
+		if (remove_key(wnd, index, NULL))
+			EXIT2(return -EINVAL);
+		else
+			EXIT2(return 0);
+	}
+
+	/* global encryption state (for all keys) */
+	if (wrqu->data.flags & IW_ENCODE_OPEN)
+		res = set_ndis_auth_mode(wnd, Ndis802_11AuthModeOpen);
+	else // if (wrqu->data.flags & IW_ENCODE_RESTRICTED)
+		res = set_ndis_auth_mode(wnd, Ndis802_11AuthModeShared);
+	if (res) {
+		WARNING("setting authentication mode failed (%08X)", res);
+		EXIT2(return -EINVAL);
+	}
+
+	TRACE2("key length: %d", wrqu->data.length);
+
+	if (wrqu->data.length > 0) {
+		key_len = wrqu->data.length;
+		key = extra;
+	} else { // must be set as tx key
+		if (encr_info->keys[index].length == 0) {
+			WARNING("key %d is not set", index+1);
+			EXIT2(return -EINVAL);
+		}
+		key_len = encr_info->keys[index].length;
+		key = encr_info->keys[index].key;
+		encr_info->tx_key_index = index;
+	}
+
+	if (add_wep_key(wnd, key, key_len, index))
+		EXIT2(return -EINVAL);
+
+	if (index == encr_info->tx_key_index) {
+		/* if transmit key is at index other than 0, some
+		 * drivers, at least Atheros and TI, want another
+		 * (global) non-transmit key to be set; don't know why */
+		if (index != 0) {
+			int i;
+			for (i = 0; i < MAX_ENCR_KEYS; i++)
+				if (i != index &&
+				    encr_info->keys[i].length != 0)
+					break;
+			if (i == MAX_ENCR_KEYS) {
+				if (index == 0)
+					i = index + 1;
+				else
+					i = index - 1;
+				if (add_wep_key(wnd, key, key_len, i))
+					WARNING("couldn't add broadcast key"
+						" at %d", i);
+			}
+		}
+		/* ndis drivers want essid to be set after setting encr */
+		set_essid(wnd, wnd->essid.essid, wnd->essid.length);
+	}
+	EXIT2(return 0);
+}
+
+static int iw_set_nick(struct net_device *dev, struct iw_request_info *info,
+		       union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+
+	if (wrqu->data.length >= sizeof(wnd->nick))
+		return -EINVAL;
+	memcpy(wnd->nick, extra, wrqu->data.length);
+	wnd->nick[wrqu->data.length] = 0;
+	return 0;
+}
+
+static int iw_get_nick(struct net_device *dev, struct iw_request_info *info,
+		       union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+
+	wrqu->data.length = strlen(wnd->nick);
+	memcpy(extra, wnd->nick, wrqu->data.length);
+	return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) && !defined(IW_REQUEST_FLAG_COMPAT)
+#define	iwe_stream_add_event(a, b, c, d, e)	iwe_stream_add_event(b, c, d, e)
+#define	iwe_stream_add_point(a, b, c, d, e)	iwe_stream_add_point(b, c, d, e)
+#define	iwe_stream_add_value(a, b, c, d, e, f)	\
+	iwe_stream_add_value(b, c, d, e, f)
+#define	iwe_stream_lcp_len(a)			IW_EV_LCP_LEN
+#endif
+
+static char *ndis_translate_scan(struct net_device *dev,
+				 struct iw_request_info *info, char *event,
+				 char *end_buf, void *item)
+{
+	struct iw_event iwe;
+	char *current_val;
+	char *ret;
+	int i, nrates;
+	unsigned char custom_str[64];
+	struct ndis_wlan_bssid *bssid;
+	struct ndis_wlan_bssid_ex *bssid_ex;
+	int extended;
+
+	ENTER2("%p, %p", event, item);
+	bssid = item;
+	bssid_ex = item;
+	extended = (bssid->length > offsetof(struct ndis_wlan_bssid_ex, var));
+
+	/* add mac address */
+	memset(&iwe, 0, sizeof(iwe));
+	iwe.cmd = SIOCGIWAP;
+	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+	iwe.len = IW_EV_ADDR_LEN;
+	memcpy(iwe.u.ap_addr.sa_data, bssid->mac, ETH_ALEN);
+	ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_ADDR_LEN);
+	if (ret == event)
+		return NULL;
+	event = ret;
+
+	/* add essid */
+	memset(&iwe, 0, sizeof(iwe));
+	iwe.cmd = SIOCGIWESSID;
+	iwe.u.data.length = bssid->ssid.length;
+	if (iwe.u.data.length > IW_ESSID_MAX_SIZE)
+		iwe.u.data.length = IW_ESSID_MAX_SIZE;
+	iwe.u.data.flags = 1;
+	iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+	ret = iwe_stream_add_point(info, event, end_buf, &iwe,
+				   bssid->ssid.essid);
+	if (ret == event)
+		return NULL;
+	event = ret;
+
+	/* add protocol name */
+	memset(&iwe, 0, sizeof(iwe));
+	iwe.cmd = SIOCGIWNAME;
+	strncpy(iwe.u.name, network_type_to_name(bssid->net_type), IFNAMSIZ - 1);
+	ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_CHAR_LEN);
+	if (ret == event)
+		return NULL;
+	event = ret;
+
+	/* add mode */
+	memset(&iwe, 0, sizeof(iwe));
+	iwe.cmd = SIOCGIWMODE;
+	if (bssid->mode == Ndis802_11IBSS)
+		iwe.u.mode = IW_MODE_ADHOC;
+	else if (bssid->mode == Ndis802_11Infrastructure)
+		iwe.u.mode = IW_MODE_MASTER;
+	else // if (bssid->mode == Ndis802_11AutoUnknown)
+		iwe.u.mode = IW_MODE_AUTO;
+	ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_UINT_LEN);
+	if (ret == event)
+		return NULL;
+	event = ret;
+
+	/* add freq */
+	memset(&iwe, 0, sizeof(iwe));
+	iwe.cmd = SIOCGIWFREQ;
+	iwe.u.freq.m = bssid->config.ds_config;
+	if (bssid->config.ds_config > 1000000) {
+		iwe.u.freq.m = bssid->config.ds_config / 10;
+		iwe.u.freq.e = 1;
+	}
+	else
+		iwe.u.freq.m = bssid->config.ds_config;
+	/* convert from kHz to Hz */
+	iwe.u.freq.e += 3;
+	iwe.len = IW_EV_FREQ_LEN;
+	ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_FREQ_LEN);
+	if (ret == event)
+		return NULL;
+	event = ret;
+
+	/* add qual */
+	memset(&iwe, 0, sizeof(iwe));
+	iwe.cmd = IWEVQUAL;
+	i = 100 * (bssid->rssi - WL_NOISE) / (WL_SIGMAX - WL_NOISE);
+	if (i < 0)
+		i = 0;
+	else if (i > 100)
+		i = 100;
+	iwe.u.qual.level = bssid->rssi;
+	iwe.u.qual.noise = WL_NOISE;
+	iwe.u.qual.qual = i;
+	iwe.len = IW_EV_QUAL_LEN;
+	ret = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_QUAL_LEN);
+	if (ret == event)
+		return NULL;
+	event = ret;
+
+	/* add key info */
+	memset(&iwe, 0, sizeof(iwe));
+	iwe.cmd = SIOCGIWENCODE;
+	if (bssid->privacy == Ndis802_11PrivFilterAcceptAll)
+		iwe.u.data.flags = IW_ENCODE_DISABLED;
+	else
+		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+	iwe.u.data.length = 0;
+	iwe.len = IW_EV_POINT_LEN;
+	ret = iwe_stream_add_point(info, event, end_buf, &iwe,
+				   bssid->ssid.essid);
+	if (ret == event)
+		return NULL;
+	event = ret;
+
+	/* add rate */
+	memset(&iwe, 0, sizeof(iwe));
+	current_val = event + iwe_stream_lcp_len(info);
+	iwe.cmd = SIOCGIWRATE;
+	if (extended)
+		nrates = ARRAY_SIZE(bssid->rates);
+	else
+		nrates = ARRAY_SIZE(bssid_ex->rates_ex);
+	for (i = 0; i < nrates; i++) {
+		if (bssid_ex->rates_ex[i] & 0x7f) {
+			iwe.u.bitrate.value = ((bssid->rates[i] & 0x7f) *
+					       500000);
+			ret = iwe_stream_add_value(info, event, current_val,
+						   end_buf, &iwe,
+						   IW_EV_PARAM_LEN);
+			if (ret == current_val)
+				return NULL;
+			current_val = ret;
+		}
+	}
+
+	if ((current_val - event) > iwe_stream_lcp_len(info))
+		event = current_val;
+
+	memset(&iwe, 0, sizeof(iwe));
+	iwe.cmd = IWEVCUSTOM;
+	sprintf(custom_str, "bcn_int=%d", bssid->config.beacon_period);
+	iwe.u.data.length = strlen(custom_str);
+	ret = iwe_stream_add_point(info, event, end_buf, &iwe, custom_str);
+	if (ret == event)
+		return NULL;
+	event = ret;
+
+	memset(&iwe, 0, sizeof(iwe));
+	iwe.cmd = IWEVCUSTOM;
+	sprintf(custom_str, "atim=%u", bssid->config.atim_window);
+	iwe.u.data.length = strlen(custom_str);
+	ret = iwe_stream_add_point(info, event, end_buf, &iwe, custom_str);
+	if (ret == event)
+		return NULL;
+	event = ret;
+
+	TRACE2("%d, %zu", bssid->length, sizeof(*bssid));
+	if (extended) {
+		struct ndis_variable_ies *iep = bssid_ex->var;
+		unsigned char *end = (unsigned char *)&bssid_ex->fixed +
+			bssid_ex->ie_length;
+
+		while (&iep->length < end && &iep->data[iep->length] <= end) {
+			unsigned char ielen = iep->length + 2;
+
+			memset(&iwe, 0, sizeof(iwe));
+			iwe.cmd = IWEVGENIE;
+			iwe.u.data.length = ielen;
+			ret = iwe_stream_add_point(info, event, end_buf, &iwe,
+						   (char *)iep);
+			if (ret == event)
+				return NULL;
+			event = ret;
+			iep = (typeof(iep))&iep->data[iep->length];
+		}
+	}
+	TRACE2("event = %p, current_val = %p", event, current_val);
+	EXIT2(return event);
+}
+
+static int set_scan(struct ndis_device *wnd)
+{
+	NDIS_STATUS res;
+
+	ENTER2("");
+	res = mp_set(wnd, OID_802_11_BSSID_LIST_SCAN, NULL, 0);
+	if (res) {
+		WARNING("scanning failed (%08X)", res);
+		EXIT2(return -EOPNOTSUPP);
+	}
+	wnd->scan_timestamp = jiffies;
+	EXIT2(return 0);
+}
+
+static int iw_set_scan(struct net_device *dev, struct iw_request_info *info,
+		       union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	return set_scan(wnd);
+}
+
+static int iw_get_scan(struct net_device *dev, struct iw_request_info *info,
+		       union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	unsigned int i, buf_len, needed, data_len;
+	NDIS_STATUS res;
+	struct ndis_bssid_list *bssid_list = NULL;
+	char *event = extra;
+	struct ndis_wlan_bssid *cur_item;
+
+	ENTER2("");
+	if (time_before(jiffies, wnd->scan_timestamp + 3 * HZ))
+		return -EAGAIN;
+	/* try with space for a few scan items */
+	buf_len = sizeof(ULONG) + offsetof(struct ndis_wlan_bssid_ex, var) * 8;
+
+	/* Try many times, as the needed space may grow between queries */
+	for (i = 0; i < 10; i++) {
+		bssid_list = kzalloc(buf_len, GFP_KERNEL);
+		if (!bssid_list) {
+			ERROR("couldn't allocate %u bytes for scan results",
+			      buf_len);
+			return -ENOMEM;
+		}
+
+		needed = 0;
+		data_len = 0;
+		res = mp_query_info(wnd, OID_802_11_BSSID_LIST, bssid_list,
+				    buf_len, &data_len, &needed);
+		TRACE2("try %d: given %d bytes, needed %d, written %d",
+		       i, buf_len, needed, data_len);
+		if (needed <= buf_len)
+			break;
+		kfree(bssid_list);
+		buf_len = needed;
+	}
+	if (res) {
+		WARNING("getting BSSID list failed (%08X)", res);
+		kfree(bssid_list);
+		EXIT2(return -EOPNOTSUPP);
+	}
+
+	/* some drivers don't set bssid_list->num_items to 0 if
+	   OID_802_11_BSSID_LIST returns no items (prism54 driver, e.g.,) */
+	TRACE2("items: %d", bssid_list->num_items);
+	cur_item = &bssid_list->bssid[0];
+	for (i = 0; i < bssid_list->num_items; i++) {
+		TRACE2("item %d: len %d, remaining data %d",
+		       i, cur_item->length, data_len);
+		/* drop truncated items */
+		if (cur_item->length > data_len)
+			break;
+		event = ndis_translate_scan(dev, info, event,
+					    extra + wrqu->data.length,
+					    cur_item);
+		if (!event) {
+			kfree(bssid_list);
+			return -E2BIG;
+		}
+		data_len -= cur_item->length;
+		cur_item = (struct ndis_wlan_bssid *)((char *)cur_item +
+						      cur_item->length);
+	}
+	wrqu->data.length = event - extra;
+	wrqu->data.flags = 0;
+	kfree(bssid_list);
+	EXIT2(return 0);
+}
+
+static int iw_set_power_mode(struct net_device *dev,
+			     struct iw_request_info *info,
+			     union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	NDIS_STATUS res;
+	enum ndis_power power_mode;
+
+	if (wrqu->power.disabled == 1)
+		power_mode = NDIS_POWER_OFF;
+	else if (wrqu->power.flags & IW_POWER_MIN)
+		power_mode = NDIS_POWER_MIN;
+	else // if (wrqu->power.flags & IW_POWER_MAX)
+		power_mode = NDIS_POWER_MAX;
+
+	TRACE2("%d", power_mode);
+	res = mp_set(wnd, OID_802_11_POWER_MODE,
+		     &power_mode, sizeof(power_mode));
+	if (res)
+		WARNING("setting power mode failed (%08X)", res);
+	return 0;
+}
+
+static int iw_get_power_mode(struct net_device *dev,
+			     struct iw_request_info *info,
+			     union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	NDIS_STATUS res;
+	enum ndis_power power_mode;
+
+	ENTER2("");
+	res = mp_query(wnd, OID_802_11_POWER_MODE,
+		       &power_mode, sizeof(power_mode));
+	if (res)
+		return -ENOTSUPP;
+
+	if (power_mode == NDIS_POWER_OFF)
+		wrqu->power.disabled = 1;
+	else {
+		if (wrqu->power.flags != 0)
+			return 0;
+		wrqu->power.flags |= IW_POWER_ALL_R;
+		wrqu->power.flags |= IW_POWER_TIMEOUT;
+		wrqu->power.value = 0;
+		wrqu->power.disabled = 0;
+
+		if (power_mode == NDIS_POWER_MIN)
+			wrqu->power.flags |= IW_POWER_MIN;
+		else // if (power_mode == NDIS_POWER_MAX)
+			wrqu->power.flags |= IW_POWER_MAX;
+	}
+	return 0;
+}
+
+static int iw_get_sensitivity(struct net_device *dev,
+			      struct iw_request_info *info,
+			      union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	NDIS_STATUS res;
+	ndis_rssi rssi_trigger;
+
+	ENTER2("");
+	res = mp_query(wnd, OID_802_11_RSSI_TRIGGER,
+		       &rssi_trigger, sizeof(rssi_trigger));
+	if (res)
+		return -EOPNOTSUPP;
+	wrqu->param.value = rssi_trigger;
+	wrqu->param.disabled = (rssi_trigger == 0);
+	wrqu->param.fixed = 1;
+	return 0;
+}
+
+static int iw_set_sensitivity(struct net_device *dev,
+			      struct iw_request_info *info,
+			      union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	NDIS_STATUS res;
+	ndis_rssi rssi_trigger;
+
+	ENTER2("");
+	if (wrqu->param.disabled)
+		rssi_trigger = 0;
+	else
+		rssi_trigger = wrqu->param.value;
+	res = mp_set(wnd, OID_802_11_RSSI_TRIGGER,
+		     &rssi_trigger, sizeof(rssi_trigger));
+	if (res == NDIS_STATUS_INVALID_DATA)
+		return -EINVAL;
+	if (res)
+		return -EOPNOTSUPP;
+	return 0;
+}
+
+static int iw_get_ndis_stats(struct net_device *dev,
+			     struct iw_request_info *info,
+			     union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	struct iw_statistics *stats = &wnd->iw_stats;
+	memcpy(&wrqu->qual, &stats->qual, sizeof(stats->qual));
+	return 0;
+}
+
+static int iw_get_range(struct net_device *dev, struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
+{
+	struct iw_range *range = (struct iw_range *)extra;
+	struct iw_point *data = &wrqu->data;
+	struct ndis_device *wnd = netdev_priv(dev);
+	unsigned int i, n;
+	NDIS_STATUS res;
+	UCHAR rates[NDIS_MAX_RATES_EX];
+	ndis_tx_power_level tx_power;
+
+	ENTER2("");
+	data->length = sizeof(struct iw_range);
+	memset(range, 0, sizeof(struct iw_range));
+
+	range->txpower_capa = IW_TXPOW_MWATT;
+	range->num_txpower = 0;
+
+	res = mp_query(wnd, OID_802_11_TX_POWER_LEVEL,
+		       &tx_power, sizeof(tx_power));
+	if (!res) {
+		range->num_txpower = 1;
+		range->txpower[0] = tx_power;
+	}
+
+	range->we_version_compiled = WIRELESS_EXT;
+	range->we_version_source = 19;
+
+	range->retry_capa = IW_RETRY_LIMIT;
+	range->retry_flags = IW_RETRY_LIMIT;
+	range->min_retry = 0;
+	range->max_retry = 255;
+
+	range->num_channels = 1;
+
+	range->max_qual.qual = 100;
+	range->max_qual.level = 154;
+	range->max_qual.noise = 154;
+	range->sensitivity = 3;
+
+	range->max_encoding_tokens = 4;
+	range->num_encoding_sizes = 2;
+	range->encoding_size[0] = 5;
+	range->encoding_size[1] = 13;
+
+	range->num_bitrates = 0;
+	memset(&rates, 0, sizeof(rates));
+	res = mp_query_info(wnd, OID_802_11_SUPPORTED_RATES,
+			    &rates, sizeof(rates), &n, NULL);
+	if (res)
+		WARNING("getting bit rates failed: %08X", res);
+	else {
+		for (i = 0; i < n && range->num_bitrates < IW_MAX_BITRATES; i++)
+			if (rates[i] & 0x80)
+				continue;
+			else if (rates[i] & 0x7f) {
+				range->bitrate[range->num_bitrates] =
+					(rates[i] & 0x7f) * 500000;
+				range->num_bitrates++;
+			}
+	}
+
+	range->num_channels = ARRAY_SIZE(freq_chan);
+
+	for (i = 0; i < ARRAY_SIZE(freq_chan) && i < IW_MAX_FREQUENCIES; i++) {
+		range->freq[i].i = i + 1;
+		range->freq[i].m = freq_chan[i] * 100000;
+		range->freq[i].e = 1;
+	}
+	range->num_frequency = i;
+
+	range->min_rts = 0;
+	range->max_rts = 2347;
+	range->min_frag = 256;
+	range->max_frag = 2346;
+
+	/* Event capability (kernel + driver) */
+	range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+				IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) |
+				IW_EVENT_CAPA_MASK(SIOCGIWAP) |
+				IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
+	range->event_capa[1] = IW_EVENT_CAPA_K_1;
+	range->event_capa[4] = (IW_EVENT_CAPA_MASK(IWEVTXDROP) |
+				IW_EVENT_CAPA_MASK(IWEVCUSTOM) |
+				IW_EVENT_CAPA_MASK(IWEVREGISTERED) |
+				IW_EVENT_CAPA_MASK(IWEVEXPIRED));
+
+	range->enc_capa = 0;
+
+	if (test_bit(Ndis802_11Encryption2Enabled, &wnd->capa.encr))
+		range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP;
+	if (test_bit(Ndis802_11Encryption3Enabled, &wnd->capa.encr))
+		range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP;
+
+	if (test_bit(Ndis802_11AuthModeWPA, &wnd->capa.auth) ||
+	    test_bit(Ndis802_11AuthModeWPAPSK, &wnd->capa.auth))
+		range->enc_capa |= IW_ENC_CAPA_WPA;
+	if (test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ||
+	    test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth))
+		range->enc_capa |= IW_ENC_CAPA_WPA2;
+
+	return 0;
+}
+
+void set_default_iw_params(struct ndis_device *wnd)
+{
+	wnd->iw_auth_key_mgmt = 0;
+	wnd->iw_auth_wpa_version = 0;
+	set_infra_mode(wnd, Ndis802_11Infrastructure);
+	set_ndis_auth_mode(wnd, Ndis802_11AuthModeOpen);
+	set_priv_filter(wnd);
+	set_iw_encr_mode(wnd, IW_AUTH_CIPHER_NONE, IW_AUTH_CIPHER_NONE);
+}
+
+static int deauthenticate(struct ndis_device *wnd)
+{
+	int ret;
+
+	ENTER2("");
+	ret = disassociate(wnd, 1);
+	set_default_iw_params(wnd);
+	EXIT2(return ret);
+}
+
+NDIS_STATUS disassociate(struct ndis_device *wnd, int reset_ssid)
+{
+	NDIS_STATUS res;
+	u8 buf[NDIS_ESSID_MAX_SIZE];
+	int i;
+
+	TRACE2("");
+	res = mp_set(wnd, OID_802_11_DISASSOCIATE, NULL, 0);
+	/* disassociate causes radio to be turned off; if reset_ssid
+	 * is given, set ssid to random to enable radio */
+	if (reset_ssid) {
+		get_random_bytes(buf, sizeof(buf));
+		for (i = 0; i < sizeof(buf); i++)
+			buf[i] = 'a' + (buf[i] % 26);
+		set_essid(wnd, buf, sizeof(buf));
+	}
+	return res;
+}
+
+static int iw_set_mlme(struct net_device *dev, struct iw_request_info *info,
+		       union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	struct iw_mlme *mlme = (struct iw_mlme *)extra;
+
+	ENTER2("");
+	switch (mlme->cmd) {
+	case IW_MLME_DEAUTH:
+		return deauthenticate(wnd);
+	case IW_MLME_DISASSOC:
+		TRACE2("cmd=%d reason_code=%d", mlme->cmd, mlme->reason_code);
+		return disassociate(wnd, 1);
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int iw_set_genie(struct net_device *dev,
+			struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
+{
+	/*
+	 * NDIS drivers do not allow IEs to be configured; this is
+	 * done by the driver based on other configuration. Return 0
+	 * to avoid causing issues with user space programs that
+	 * expect this function to succeed.
+	 */
+	return 0;
+}
+
+static int iw_set_auth(struct net_device *dev,
+		       struct iw_request_info *info,
+		       union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	TRACE2("index=%d value=%d", wrqu->param.flags & IW_AUTH_INDEX,
+	       wrqu->param.value);
+	switch (wrqu->param.flags & IW_AUTH_INDEX) {
+	case IW_AUTH_WPA_VERSION:
+		wnd->iw_auth_wpa_version = wrqu->param.value;
+		break;
+	case IW_AUTH_CIPHER_PAIRWISE:
+		wnd->iw_auth_cipher_pairwise = wrqu->param.value;
+		break;
+	case IW_AUTH_CIPHER_GROUP:
+		wnd->iw_auth_cipher_group = wrqu->param.value;
+		break;
+	case IW_AUTH_KEY_MGMT:
+		wnd->iw_auth_key_mgmt = wrqu->param.value;
+		break;
+	case IW_AUTH_80211_AUTH_ALG:
+		wnd->iw_auth_80211_alg = wrqu->param.value;
+		break;
+	case IW_AUTH_WPA_ENABLED:
+		if (wrqu->param.value)
+			deauthenticate(wnd);
+		break;
+#ifdef IW_AUTH_MFP
+	case IW_AUTH_MFP:
+		if (wrqu->param.value == IW_AUTH_MFP_DISABLED ||
+		    wrqu->param.value == IW_AUTH_MFP_OPTIONAL)
+			break;
+		WARNING("MFP not implemented");
+		return -EOPNOTSUPP;
+#endif
+	case IW_AUTH_TKIP_COUNTERMEASURES:
+	case IW_AUTH_DROP_UNENCRYPTED:
+	case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+	case IW_AUTH_PRIVACY_INVOKED:
+		TRACE2("%d not implemented: %d",
+		       wrqu->param.flags & IW_AUTH_INDEX, wrqu->param.value);
+		break;
+	default:
+		WARNING("invalid cmd %d", wrqu->param.flags & IW_AUTH_INDEX);
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
+
+static int iw_get_auth(struct net_device *dev,
+		       struct iw_request_info *info,
+		       union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+
+	ENTER2("index=%d", wrqu->param.flags & IW_AUTH_INDEX);
+	switch (wrqu->param.flags & IW_AUTH_INDEX) {
+	case IW_AUTH_WPA_VERSION:
+		wrqu->param.value = wnd->iw_auth_wpa_version;
+		break;
+	case IW_AUTH_CIPHER_PAIRWISE:
+		wrqu->param.value = wnd->iw_auth_cipher_pairwise;
+		break;
+	case IW_AUTH_CIPHER_GROUP:
+		wrqu->param.value = wnd->iw_auth_cipher_group;
+		break;
+	case IW_AUTH_KEY_MGMT:
+		wrqu->param.value = wnd->iw_auth_key_mgmt;
+		break;
+	case IW_AUTH_80211_AUTH_ALG:
+		wrqu->param.value = wnd->iw_auth_80211_alg;
+		break;
+	default:
+		WARNING("invalid cmd %d", wrqu->param.flags & IW_AUTH_INDEX);
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
+
+static int iw_set_encodeext(struct net_device *dev,
+			    struct iw_request_info *info,
+			    union iwreq_data *wrqu, char *extra)
+{
+	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+	struct ndis_device *wnd = netdev_priv(dev);
+	struct ndis_add_key ndis_key;
+	int i, keyidx;
+	NDIS_STATUS res;
+	u8 *addr;
+
+	keyidx = wrqu->encoding.flags & IW_ENCODE_INDEX;
+	ENTER2("%d", keyidx);
+	if (keyidx)
+		keyidx--;
+	else
+		keyidx = wnd->encr_info.tx_key_index;
+
+	if (keyidx < 0 || keyidx >= MAX_ENCR_KEYS)
+		return -EINVAL;
+
+	if (ext->alg == WPA_ALG_WEP) {
+		if (!test_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr))
+			EXIT2(return -1);
+		if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
+			wnd->encr_info.tx_key_index = keyidx;
+		if (add_wep_key(wnd, ext->key, ext->key_len, keyidx))
+			EXIT2(return -1);
+		else
+			EXIT2(return 0);
+	}
+	if ((wrqu->encoding.flags & IW_ENCODE_DISABLED) ||
+	    ext->alg == IW_ENCODE_ALG_NONE || ext->key_len == 0)
+		EXIT2(return remove_key(wnd, keyidx, ndis_key.bssid));
+
+	if (ext->key_len > sizeof(ndis_key.key)) {
+		TRACE2("incorrect key length (%u)", ext->key_len);
+		EXIT2(return -1);
+	}
+
+	memset(&ndis_key, 0, sizeof(ndis_key));
+
+	ndis_key.struct_size =
+		sizeof(ndis_key) - sizeof(ndis_key.key) + ext->key_len;
+	ndis_key.length = ext->key_len;
+	ndis_key.index = keyidx;
+
+	if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
+		for (i = 0; i < 6; i++)
+			ndis_key.rsc |= (((u64)ext->rx_seq[i]) << (i * 8));
+		TRACE2("0x%llx", ndis_key.rsc);
+		ndis_key.index |= 1 << 29;
+	}
+
+	addr = ext->addr.sa_data;
+	if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+		/* group key */
+		if (wnd->infrastructure_mode == Ndis802_11IBSS)
+			memset(ndis_key.bssid, 0xff, ETH_ALEN);
+		else
+			get_ap_address(wnd, ndis_key.bssid);
+	} else {
+		/* pairwise key */
+		ndis_key.index |= (1 << 30);
+		memcpy(ndis_key.bssid, addr, ETH_ALEN);
+	}
+	TRACE2(MACSTRSEP, MAC2STR(ndis_key.bssid));
+
+	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
+		ndis_key.index |= (1 << 31);
+
+	if (ext->alg == IW_ENCODE_ALG_TKIP && ext->key_len == 32) {
+		/* wpa_supplicant gives us the Michael MIC RX/TX keys in
+		 * different order than NDIS spec, so swap the order here. */
+		memcpy(ndis_key.key, ext->key, 16);
+		memcpy(ndis_key.key + 16, ext->key + 24, 8);
+		memcpy(ndis_key.key + 24, ext->key + 16, 8);
+	} else
+		memcpy(ndis_key.key, ext->key, ext->key_len);
+
+	res = mp_set(wnd, OID_802_11_ADD_KEY, &ndis_key, ndis_key.struct_size);
+	if (res) {
+		TRACE2("adding key failed (%08X), %u",
+		       res, ndis_key.struct_size);
+		EXIT2(return -1);
+	}
+	wnd->encr_info.keys[keyidx].length = ext->key_len;
+	memcpy(&wnd->encr_info.keys[keyidx].key, ndis_key.key, ext->key_len);
+	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)
+		wnd->encr_info.tx_key_index = keyidx;
+	TRACE2("key %d added", keyidx);
+
+	EXIT2(return 0);
+}
+
+static int iw_get_encodeext(struct net_device *dev,
+			    struct iw_request_info *info,
+			    union iwreq_data *wrqu, char *extra)
+{
+	/* struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; */
+	/* TODO */
+	ENTER2("");
+	return 0;
+}
+
+static int iw_set_pmksa(struct net_device *dev, struct iw_request_info *info,
+			union iwreq_data *wrqu, char *extra)
+{
+	struct iw_pmksa *pmksa = (struct iw_pmksa *)extra;
+	struct ndis_pmkid pmkid;
+	NDIS_STATUS res;
+	struct ndis_device *wnd = netdev_priv(dev);
+
+	/* TODO: must keep local list of PMKIDs since NDIS drivers
+	 * expect that all PMKID entries are included whenever a new
+	 * one is added. */
+
+	ENTER2("%d", pmksa->cmd);
+	if ((pmksa->cmd == IW_PMKSA_ADD || pmksa->cmd == IW_PMKSA_REMOVE) &&
+	    (!(wnd->iw_auth_wpa_version & IW_AUTH_WPA_VERSION_WPA2)))
+		EXIT2(return -EOPNOTSUPP);
+
+	memset(&pmkid, 0, sizeof(pmkid));
+	if (pmksa->cmd == IW_PMKSA_ADD) {
+		pmkid.bssid_info_count = 1;
+		memcpy(pmkid.bssid_info[0].bssid, pmksa->bssid.sa_data,
+		       ETH_ALEN);
+		memcpy(pmkid.bssid_info[0].pmkid, pmksa->pmkid, IW_PMKID_LEN);
+	}
+	pmkid.length = sizeof(pmkid);
+
+	res = mp_set(wnd, OID_802_11_PMKID, &pmkid, pmkid.length);
+	if (res == NDIS_STATUS_FAILURE)
+		EXIT2(return -EOPNOTSUPP);
+	TRACE2("OID_802_11_PMKID -> %d", res);
+	if (res)
+		return -EINVAL;
+
+	return 0;
+}
+
+#define WEXT(id) [id - SIOCIWFIRST]
+
+static const iw_handler	ndis_handler[] = {
+	WEXT(SIOCGIWNAME)	= iw_get_network_type,
+	WEXT(SIOCSIWESSID)	= iw_set_essid,
+	WEXT(SIOCGIWESSID)	= iw_get_essid,
+	WEXT(SIOCSIWMODE)	= iw_set_infra_mode,
+	WEXT(SIOCGIWMODE)	= iw_get_infra_mode,
+	WEXT(SIOCGIWFREQ)	= iw_get_freq,
+	WEXT(SIOCSIWFREQ)	= iw_set_freq,
+	WEXT(SIOCGIWTXPOW)	= iw_get_tx_power,
+	WEXT(SIOCSIWTXPOW)	= iw_set_tx_power,
+	WEXT(SIOCGIWRATE)	= iw_get_bitrate,
+	WEXT(SIOCSIWRATE)	= iw_set_bitrate,
+	WEXT(SIOCGIWRTS)	= iw_get_rts_threshold,
+	WEXT(SIOCSIWRTS)	= iw_set_rts_threshold,
+	WEXT(SIOCGIWFRAG)	= iw_get_frag_threshold,
+	WEXT(SIOCSIWFRAG)	= iw_set_frag_threshold,
+	WEXT(SIOCGIWAP)		= iw_get_ap_address,
+	WEXT(SIOCSIWAP)		= iw_set_ap_address,
+	WEXT(SIOCSIWENCODE)	= iw_set_wep,
+	WEXT(SIOCGIWENCODE)	= iw_get_encr,
+	WEXT(SIOCSIWSCAN)	= iw_set_scan,
+	WEXT(SIOCGIWSCAN)	= iw_get_scan,
+	WEXT(SIOCGIWPOWER)	= iw_get_power_mode,
+	WEXT(SIOCSIWPOWER)	= iw_set_power_mode,
+	WEXT(SIOCGIWRANGE)	= iw_get_range,
+	WEXT(SIOCGIWSTATS)	= iw_get_ndis_stats,
+	WEXT(SIOCGIWSENS)	= iw_get_sensitivity,
+	WEXT(SIOCSIWSENS)	= iw_set_sensitivity,
+	WEXT(SIOCGIWNICKN)	= iw_get_nick,
+	WEXT(SIOCSIWNICKN)	= iw_set_nick,
+	WEXT(SIOCSIWCOMMIT)	= iw_set_dummy,
+	WEXT(SIOCSIWMLME)	= iw_set_mlme,
+	WEXT(SIOCSIWGENIE)	= iw_set_genie,
+	WEXT(SIOCSIWAUTH)	= iw_set_auth,
+	WEXT(SIOCGIWAUTH)	= iw_get_auth,
+	WEXT(SIOCSIWENCODEEXT)	= iw_set_encodeext,
+	WEXT(SIOCGIWENCODEEXT)	= iw_get_encodeext,
+	WEXT(SIOCSIWPMKSA)	= iw_set_pmksa,
+};
+
+/* private ioctl's */
+
+static int priv_reset(struct net_device *dev, struct iw_request_info *info,
+		      union iwreq_data *wrqu, char *extra)
+{
+	int res;
+	ENTER2("");
+	res = mp_reset(netdev_priv(dev));
+	if (res) {
+		WARNING("reset failed: %08X", res);
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
+
+static int priv_deauthenticate(struct net_device *dev,
+			       struct iw_request_info *info,
+			       union iwreq_data *wrqu, char *extra)
+{
+	int res;
+	ENTER2("");
+	res = deauthenticate(netdev_priv(dev));
+	return res;
+}
+
+static int priv_power_profile(struct net_device *dev,
+			      struct iw_request_info *info,
+			      union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	struct miniport *mp;
+	ULONG profile_inf;
+
+	ENTER2("");
+	mp = &wnd->wd->driver->ndis_driver->mp;
+	if (!mp->pnp_event_notify)
+		EXIT2(return -EOPNOTSUPP);
+
+	/* 1 for AC and 0 for Battery */
+	if (wrqu->param.value)
+		profile_inf = NdisPowerProfileAcOnLine;
+	else
+		profile_inf = NdisPowerProfileBattery;
+
+	LIN2WIN4(mp->pnp_event_notify, wnd->nmb->mp_ctx,
+		 NdisDevicePnPEventPowerProfileChanged,
+		 &profile_inf, sizeof(profile_inf));
+	EXIT2(return 0);
+}
+
+static int priv_network_type(struct net_device *dev,
+			     struct iw_request_info *info,
+			     union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	enum network_type network_type;
+	NDIS_STATUS res;
+	char type;
+
+	ENTER2("");
+	type = wrqu->param.value;
+	if (type == 'f')
+		network_type = Ndis802_11FH;
+	else if (type == 'b')
+		network_type = Ndis802_11DS;
+	else if (type == 'a')
+		network_type = Ndis802_11OFDM5;
+	else if (type == 'g' || type == 'n')
+		network_type = Ndis802_11OFDM24;
+	else
+		network_type = Ndis802_11Automode;
+
+	res = mp_set_int(wnd, OID_802_11_NETWORK_TYPE_IN_USE, network_type);
+	if (res) {
+		WARNING("setting network type to %d failed (%08X)",
+			network_type, res);
+		EXIT2(return -EINVAL);
+	}
+
+	EXIT2(return 0);
+}
+
+static int priv_media_stream_mode(struct net_device *dev,
+				  struct iw_request_info *info,
+				  union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	NDIS_STATUS res;
+	int mode;
+
+	ENTER2("");
+	if (wrqu->param.value > 0)
+		mode = Ndis802_11MediaStreamOn;
+	else
+		mode = Ndis802_11MediaStreamOff;
+	res = mp_set_int(wnd, OID_802_11_MEDIA_STREAM_MODE, mode);
+	if (res) {
+		WARNING("oid failed (%08X)", res);
+		EXIT2(return -EINVAL);
+	}
+	EXIT2(return 0);
+}
+
+static int priv_reload_defaults(struct net_device *dev,
+				struct iw_request_info *info,
+				union iwreq_data *wrqu, char *extra)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	int res;
+	ENTER2("");
+	res = mp_set_int(wnd, OID_802_11_RELOAD_DEFAULTS,
+			 Ndis802_11ReloadWEPKeys);
+	if (res) {
+		WARNING("reloading defaults failed: %08X", res);
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
+
+static const struct iw_priv_args priv_args[] = {
+	{PRIV_RESET, 0, 0, "ndis_reset"},
+	{PRIV_POWER_PROFILE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
+	 "power_profile"},
+	{PRIV_DEAUTHENTICATE, 0, 0, "deauthenticate"},
+	{PRIV_NETWORK_TYPE, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 1, 0,
+	 "network_type"},
+	{PRIV_MEDIA_STREAM_MODE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
+	 "media_stream"},
+
+	{PRIV_RELOAD_DEFAULTS, 0, 0, "reload_defaults"},
+};
+
+#define WEPRIV(id) [id - SIOCIWFIRSTPRIV]
+
+static const iw_handler priv_handler[] = {
+	WEPRIV(PRIV_RESET)		= priv_reset,
+	WEPRIV(PRIV_POWER_PROFILE)	= priv_power_profile,
+	WEPRIV(PRIV_DEAUTHENTICATE)	= priv_deauthenticate,
+	WEPRIV(PRIV_NETWORK_TYPE)	= priv_network_type,
+	WEPRIV(PRIV_MEDIA_STREAM_MODE)	= priv_media_stream_mode,
+	WEPRIV(PRIV_RELOAD_DEFAULTS)	= priv_reload_defaults,
+};
+
+const struct iw_handler_def ndis_handler_def = {
+	.num_standard	= ARRAY_SIZE(ndis_handler),
+	.num_private	= ARRAY_SIZE(priv_handler),
+	.num_private_args = ARRAY_SIZE(priv_args),
+
+	.standard	= (iw_handler *)ndis_handler,
+	.private	= (iw_handler *)priv_handler,
+	.private_args	= (struct iw_priv_args *)priv_args,
+	.get_wireless_stats = get_iw_stats,
+};
+
+#endif
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/iw_ndis.h linux-5.6.11-ndis/3rdparty/ndiswrapper/iw_ndis.h
--- linux-5.6.11/3rdparty/ndiswrapper/iw_ndis.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/iw_ndis.h	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,194 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#ifndef _IW_NDIS_H_
+#define _IW_NDIS_H_
+
+#include "ndis.h"
+
+#define	WL_NOISE	-96	/* typical noise level in dBm */
+#define	WL_SIGMAX	-32	/* typical maximum signal level in dBm */
+
+struct ndis_encr_key {
+	ULONG struct_size;
+	ULONG index;
+	ULONG length;
+	UCHAR key[NDIS_ENCODING_TOKEN_MAX];
+};
+
+struct ndis_add_key {
+	ULONG struct_size;
+	ndis_key_index index;
+	ULONG length;
+	mac_address bssid;
+	UCHAR pad[6];
+	ndis_key_rsc rsc;
+	UCHAR key[NDIS_ENCODING_TOKEN_MAX];
+};
+
+struct ndis_remove_key {
+	ULONG struct_size;
+	ndis_key_index index;
+	mac_address bssid;
+};
+
+struct ndis_fixed_ies {
+	UCHAR time_stamp[8];
+	USHORT beacon_interval;
+	USHORT capa;
+};
+
+struct ndis_variable_ies {
+	UCHAR elem_id;
+	UCHAR length;
+	UCHAR data[];
+};
+
+enum ndis_reload_defaults { Ndis802_11ReloadWEPKeys };
+
+struct ndis_assoc_info {
+	ULONG length;
+	USHORT req_ies;
+	struct req_ie {
+		USHORT capa;
+		USHORT listen_interval;
+		mac_address cur_ap_address;
+	} req_ie;
+	ULONG req_ie_length;
+	ULONG offset_req_ies;
+	USHORT resp_ies;
+	struct resp_ie {
+		USHORT capa;
+		USHORT status_code;
+		USHORT assoc_id;
+	} resp_ie;
+	ULONG resp_ie_length;
+	ULONG offset_resp_ies;
+};
+
+struct ndis_configuration_fh {
+	ULONG length;
+	ULONG hop_pattern;
+	ULONG hop_set;
+	ULONG dwell_time;
+};
+
+struct ndis_configuration {
+	ULONG length;
+	ULONG beacon_period;
+	ULONG atim_window;
+	ULONG ds_config;
+	struct ndis_configuration_fh fh_config;
+};
+
+struct ndis_wlan_bssid {
+	ULONG length;
+	mac_address mac;
+	UCHAR reserved[2];
+	struct ndis_essid ssid;
+	ULONG privacy;
+	ndis_rssi rssi;
+	UINT net_type;
+	struct ndis_configuration config;
+	UINT mode;
+	UCHAR rates[NDIS_MAX_RATES];
+};
+
+struct ndis_wlan_bssid_ex {
+	ULONG length;
+	mac_address mac;
+	UCHAR reserved[2];
+	struct ndis_essid ssid;
+	ULONG privacy;
+	ndis_rssi rssi;
+	UINT net_type;
+	struct ndis_configuration config;
+	UINT mode;
+	UCHAR rates_ex[NDIS_MAX_RATES_EX];
+	ULONG ie_length;
+	struct ndis_fixed_ies fixed;
+	struct ndis_variable_ies var[];
+};
+
+/* we use bssid_list as bssid_list_ex also */
+struct ndis_bssid_list {
+	ULONG num_items;
+	struct ndis_wlan_bssid bssid[1];
+};
+
+enum ndis_priv_filter {
+	Ndis802_11PrivFilterAcceptAll, Ndis802_11PrivFilter8021xWEP
+};
+
+enum network_type {
+	Ndis802_11FH, Ndis802_11DS, Ndis802_11OFDM5, Ndis802_11OFDM24,
+	/* MSDN site uses Ndis802_11Automode, which is not mentioned
+	 * in DDK, so add one and assign it to
+	 * Ndis802_11NetworkTypeMax */
+	Ndis802_11Automode, Ndis802_11NetworkTypeMax = Ndis802_11Automode
+};
+
+struct network_type_list {
+	ULONG num;
+	enum network_type types[1];
+};
+
+enum ndis_power {
+	NDIS_POWER_OFF = 0, NDIS_POWER_MAX, NDIS_POWER_MIN,
+};
+
+struct ndis_auth_req {
+	ULONG length;
+	mac_address bssid;
+	ULONG flags;
+};
+
+struct ndis_bssid_info {
+	mac_address bssid;
+	UCHAR pmkid[IW_PMKID_LEN];
+};
+
+struct ndis_pmkid {
+	ULONG length;
+	ULONG bssid_info_count;
+	struct ndis_bssid_info bssid_info[1];
+};
+
+int get_ap_address(struct ndis_device *wnd, mac_address mac);
+int set_ndis_auth_mode(struct ndis_device *wnd, ULONG auth_mode);
+int get_ndis_encr_mode(struct ndis_device *wnd);
+int set_iw_encr_mode(struct ndis_device *wnd, int cipher_pairwise,
+		     int cipher_groupwise);
+int get_ndis_auth_mode(struct ndis_device *wnd);
+NDIS_STATUS disassociate(struct ndis_device *wnd, int reset_ssid);
+void set_default_iw_params(struct ndis_device *wnd);
+extern const struct iw_handler_def ndis_handler_def;
+
+#define PRIV_RESET			SIOCIWFIRSTPRIV+16
+#define PRIV_POWER_PROFILE		SIOCIWFIRSTPRIV+17
+#define PRIV_NETWORK_TYPE		SIOCIWFIRSTPRIV+18
+#define PRIV_DEAUTHENTICATE		SIOCIWFIRSTPRIV+19
+#define PRIV_MEDIA_STREAM_MODE		SIOCIWFIRSTPRIV+20
+#define PRIV_RELOAD_DEFAULTS		SIOCIWFIRSTPRIV+23
+
+/* these have to match what is in wpa_supplicant */
+
+enum wpa_alg { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP };
+enum wpa_cipher { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP,
+		  CIPHER_WEP104 };
+enum wpa_key_mgmt { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE,
+		    KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE };
+
+#endif // IW_NDIS_H
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/lin2win.h linux-5.6.11-ndis/3rdparty/ndiswrapper/lin2win.h
--- linux-5.6.11/3rdparty/ndiswrapper/lin2win.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/lin2win.h	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,111 @@
+/*
+ *  Copyright (C) 2006 Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#ifdef CONFIG_X86_64
+
+u64 lin2win0(void *func);
+u64 lin2win1(void *func, u64 arg1);
+u64 lin2win2(void *func, u64 arg1, u64 arg2);
+u64 lin2win3(void *func, u64 arg1, u64 arg2, u64 arg3);
+u64 lin2win4(void *func, u64 arg1, u64 arg2, u64 arg3, u64 arg4);
+u64 lin2win5(void *func, u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5);
+u64 lin2win6(void *func, u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5,
+	     u64 arg6);
+
+#define LIN2WIN0(func)							\
+({									\
+	if (0)								\
+		func();							\
+	lin2win0(func);							\
+})
+
+#define LIN2WIN1(func, arg1)						\
+({									\
+	if (0)								\
+		func(arg1);						\
+	lin2win1(func, (u64)arg1);					\
+})
+
+#define LIN2WIN2(func, arg1, arg2)					\
+({									\
+	if (0)								\
+		func(arg1, arg2);					\
+	lin2win2(func, (u64)arg1, (u64)arg2);			\
+})
+
+#define LIN2WIN3(func, arg1, arg2, arg3)				\
+({									\
+	if (0)								\
+		func(arg1, arg2, arg3);					\
+	lin2win3(func, (u64)arg1, (u64)arg2, (u64)arg3);		\
+})
+
+#define LIN2WIN4(func, arg1, arg2, arg3, arg4)				\
+({									\
+	if (0)								\
+		func(arg1, arg2, arg3, arg4);				\
+	lin2win4(func, (u64)arg1, (u64)arg2, (u64)arg3, (u64)arg4);	\
+})
+
+#define LIN2WIN5(func, arg1, arg2, arg3, arg4, arg5)			\
+({									\
+	if (0)								\
+		func(arg1, arg2, arg3, arg4, arg5);			\
+	lin2win5(func, (u64)arg1, (u64)arg2, (u64)arg3, (u64)arg4,	\
+		 (u64)arg5);						\
+})
+
+#define LIN2WIN6(func, arg1, arg2, arg3, arg4, arg5, arg6)		\
+({									\
+	if (0)								\
+		func(arg1, arg2, arg3, arg4, arg5, arg6);		\
+	lin2win6(func, (u64)arg1, (u64)arg2, (u64)arg3, (u64)arg4,	\
+		 (u64)arg5, (u64)arg6);					\
+})
+
+#else // CONFIG_X86_64
+
+#define LIN2WIN1(func, arg1)						\
+({									\
+	TRACE6("calling %p", func);					\
+	func(arg1);							\
+})
+#define LIN2WIN2(func, arg1, arg2)					\
+({									\
+	TRACE6("calling %p", func);					\
+	func(arg1, arg2);						\
+})
+#define LIN2WIN3(func, arg1, arg2, arg3)				\
+({									\
+	TRACE6("calling %p", func);					\
+	func(arg1, arg2, arg3);						\
+})
+#define LIN2WIN4(func, arg1, arg2, arg3, arg4)				\
+({									\
+	TRACE6("calling %p", func);					\
+	func(arg1, arg2, arg3, arg4);					\
+})
+#define LIN2WIN5(func, arg1, arg2, arg3, arg4, arg5)			\
+({									\
+	TRACE6("calling %p", func);					\
+	func(arg1, arg2, arg3, arg4, arg5);				\
+})
+#define LIN2WIN6(func, arg1, arg2, arg3, arg4, arg5, arg6)		\
+({									\
+	TRACE6("calling %p", func);					\
+	func(arg1, arg2, arg3, arg4, arg5, arg6);			\
+})
+
+#endif // CONFIG_X86_64
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/lin2win.S linux-5.6.11-ndis/3rdparty/ndiswrapper/lin2win.S
--- linux-5.6.11/3rdparty/ndiswrapper/lin2win.S	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/lin2win.S	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,147 @@
+/*
+ *  Copyright (C) 2011 Pavel Roskin
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <linux/version.h>
+
+	.text
+
+#define WORD_BYTES 8
+#define LINUX_REG_ARGS 6
+#define WINDOWS_REG_ARGS 4
+
+/* %rbp is saved to create a stack frame, which can help with debugging */
+#define SAVED_REGS 1
+
+/*
+ * When calling a Windows function, stack space is allocated for at least 4
+ * arguments even if the number of arguments is less than 4.  The value of
+ * true is -1 in assembler, so we multiply it by another true value.
+ */
+#define stack_args(argc)						\
+	(WINDOWS_REG_ARGS +						\
+	 (0 < 1) * (argc > WINDOWS_REG_ARGS) * (argc - WINDOWS_REG_ARGS))
+
+/* Full required change of stack pointer, in words */
+#define stack_words_raw(argc) (stack_args(argc) + SAVED_REGS + 1)
+
+/* Full actual change of stack pointer, in words (must be even) */
+#define stack_words_aligned(argc) ((stack_words_raw(argc) + 1) & ~1)
+
+/* Space allocated for Linux arguments on stack */
+#define stack_space(argc) \
+	((stack_words_aligned(argc) - SAVED_REGS - 1) * WORD_BYTES)
+
+/*
+ * lin2win_win_arg(N) gives the address of the Nth Windows argument on our
+ * stack frame.  %rsp points to the first argument.  The Nth argument is
+ * therefore at ((N - 1) * 8)(%rsp).
+ *
+ * Don't call with N less than 5!
+ */
+#define lin2win_win_arg(n) ((n - 1) * WORD_BYTES)(%rsp)
+
+/*
+ * lin2win_lin_arg(N, ARGC) gives the address of the Nth Linux argument after
+ * the stack has been prepared for a Windows function call with ARGC arguments.
+ *
+ * When called from Linux, the Nth argument is at ((N - 6) * 8)(%rsp).  We add
+ * the allocated stack space and saved registers to compensate for %rsp change.
+ *
+ * Don't call with N less than 7!
+ */
+#define lin2win_lin_arg(n, argc)					\
+	(stack_space(argc) +						\
+	 (SAVED_REGS + n - LINUX_REG_ARGS) * WORD_BYTES)(%rsp)
+
+/*
+ * lin2win(func, winarg1, winarg2, ...)
+ * Call Windows FUNC function with ARGC arguments WINARG1, WINARG2, ...
+ * We get (ARGC + 1) arguments.
+ */
+.macro lin2win name, argc
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,5,0)
+	SYM_FUNC_START(\name)
+#else
+	.type \name, @function
+	ENTRY(\name)
+#endif
+
+	/* Create a call frame - it's optional, but good for debugging */
+	.cfi_startproc
+	push %rbp
+	.cfi_def_cfa %rsp, 2 * WORD_BYTES
+	.cfi_offset %rbp, -2 * WORD_BYTES
+	mov %rsp, %rbp
+	.cfi_def_cfa %rbp, 2 * WORD_BYTES
+
+	/* Allocate space for Windows arguments */
+	sub $stack_space(\argc), %rsp
+
+	/* arg7 to winarg6 */
+	.if (\argc >= 6)
+		mov lin2win_lin_arg(7, \argc), %r11
+		mov %r11, lin2win_win_arg(6)
+	.endif
+
+	/* arg6 to winarg5 */
+	.if (\argc >= 5)
+		mov %r9, lin2win_win_arg(5)
+	.endif
+
+	/* arg5 to winarg4 */
+	.if (\argc >= 4)
+		mov %r8, %r9
+	.endif
+
+	/* arg4 to winarg3 */
+	.if (\argc >= 3)
+		mov %rcx, %r8
+	.endif
+
+	/* arg3 to winarg2 - nothing needed, both are in %rdx */
+
+	/* arg2 to winarg1 */
+	.if (\argc >= 1)
+		mov %rsi, %rcx
+	.endif
+
+	/* Call function (arg1) */
+	call *%rdi
+
+	/* Reclaim space for Windows arguments */
+	add $stack_space(\argc), %rsp
+
+	/* Return to the caller */
+	leave
+	.cfi_def_cfa %rsp, WORD_BYTES
+	.cfi_restore %rbp
+	ret
+	.cfi_endproc
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,5,0)
+	SYM_FUNC_END(\name)
+#else
+	.size	\name, (. - \name)
+#endif
+.endm
+
+/* Define lin2winN functions */
+lin2win lin2win0, 0
+lin2win lin2win1, 1
+lin2win lin2win2, 2
+lin2win lin2win3, 3
+lin2win lin2win4, 4
+lin2win lin2win5, 5
+lin2win lin2win6, 6
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/loader.c linux-5.6.11-ndis/3rdparty/ndiswrapper/loader.c
--- linux-5.6.11/3rdparty/ndiswrapper/loader.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/loader.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,967 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include "ndis.h"
+#include "loader.h"
+#include "wrapndis.h"
+#include "pnp.h"
+
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/miscdevice.h>
+#include <asm/uaccess.h>
+
+/*
+  Network adapter: ClassGuid = {4d36e972-e325-11ce-bfc1-08002be10318}
+  Network client: ClassGuid = {4d36e973-e325-11ce-bfc1-08002be10318}
+  PCMCIA adapter: ClassGuid = {4d36e977-e325-11ce-bfc1-08002be10318}
+  USB: ClassGuid = {36fc9e60-c465-11cf-8056-444553540000}
+*/
+
+/* the indices used here must match macros WRAP_NDIS_DEVICE etc. */
+static struct guid class_guids[] = {
+	/* Network */
+	{ .data1 = 0x4d36e972, .data2 = 0xe325, .data3 = 0x11ce },
+	/* USB WDM */
+	{ .data1 = 0x36fc9e60, .data2 = 0xc465, .data3 = 0x11cf },
+	/* Bluetooth */
+	{ .data1 = 0xe0cbf06c, .data2 = 0xcd8b, .data3 = 0x4647 },
+	/* ivtcorporation.com's bluetooth device claims this is
+	 * bluetooth guid */
+	{ .data1 = 0xf12d3cf8, .data2 = 0xb11d, .data3 = 0x457e},
+};
+
+struct mutex loader_mutex;
+static struct completion loader_complete;
+
+static struct nt_list wrap_devices;
+static struct nt_list wrap_drivers;
+
+static int wrap_device_type(int data1)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(class_guids); i++)
+		if (data1 == class_guids[i].data1)
+			return i;
+	ERROR("unknown device: 0x%x\n", data1);
+	return -1;
+}
+
+/* load driver for given device, if not already loaded */
+struct wrap_driver *load_wrap_driver(struct wrap_device *wd)
+{
+	int ret;
+	struct nt_list *cur;
+	struct wrap_driver *wrap_driver;
+
+	ENTER1("device: %04X:%04X:%04X:%04X", wd->vendor, wd->device,
+	       wd->subvendor, wd->subdevice);
+	mutex_lock(&loader_mutex);
+	wrap_driver = NULL;
+	nt_list_for_each(cur, &wrap_drivers) {
+		wrap_driver = container_of(cur, struct wrap_driver, list);
+		if (!stricmp(wrap_driver->name, wd->driver_name)) {
+			TRACE1("driver %s already loaded", wrap_driver->name);
+			break;
+		} else
+			wrap_driver = NULL;
+	}
+	mutex_unlock(&loader_mutex);
+
+	if (!wrap_driver) {
+		char *argv[] = {"loadndisdriver", WRAP_CMD_LOAD_DRIVER,
+#if DEBUG >= 1
+				"1",
+#else
+				"0",
+#endif
+				UTILS_VERSION, wd->driver_name,
+				wd->conf_file_name, NULL};
+		char *env[] = {NULL};
+
+		TRACE1("loading driver %s", wd->driver_name);
+		mutex_lock(&loader_mutex);
+		reinit_completion(&loader_complete);
+		ret = call_usermodehelper("/sbin/loadndisdriver", argv, env,
+					  UMH_WAIT_PROC);
+		if (ret) {
+			mutex_unlock(&loader_mutex);
+			ERROR("couldn't load driver %s; check system log "
+			      "for messages from 'loadndisdriver'",
+			      wd->driver_name);
+			EXIT1(return NULL);
+		}
+		wait_for_completion(&loader_complete);
+		TRACE1("%s", wd->driver_name);
+		wrap_driver = NULL;
+		nt_list_for_each(cur, &wrap_drivers) {
+			wrap_driver = container_of(cur, struct wrap_driver,
+						   list);
+			if (!stricmp(wrap_driver->name, wd->driver_name)) {
+				wd->driver = wrap_driver;
+				break;
+			} else
+				wrap_driver = NULL;
+		}
+		mutex_unlock(&loader_mutex);
+		if (wrap_driver)
+			TRACE1("driver %s is loaded", wrap_driver->name);
+		else
+			ERROR("couldn't load driver '%s'", wd->driver_name);
+	}
+	EXIT1(return wrap_driver);
+}
+
+/* load the driver files from userspace. */
+static int load_sys_files(struct wrap_driver *driver,
+			  struct load_driver *load_driver)
+{
+	int i, err;
+
+	TRACE1("num_pe_images = %d", load_driver->num_sys_files);
+	TRACE1("loading driver: %s", load_driver->name);
+	strncpy(driver->name, load_driver->name, sizeof(driver->name));
+	driver->name[sizeof(driver->name)-1] = 0;
+	TRACE1("driver: %s", driver->name);
+	err = 0;
+	driver->num_pe_images = 0;
+	for (i = 0; i < load_driver->num_sys_files; i++) {
+		struct pe_image *pe_image;
+		pe_image = &driver->pe_images[driver->num_pe_images];
+
+		strncpy(pe_image->name, load_driver->sys_files[i].name,
+			sizeof(pe_image->name));
+		pe_image->name[sizeof(pe_image->name)-1] = 0;
+		TRACE1("image size: %zu bytes", load_driver->sys_files[i].size);
+
+#ifdef CONFIG_X86_64
+#ifdef PAGE_KERNEL_EXECUTABLE
+		pe_image->image =
+			__vmalloc(load_driver->sys_files[i].size,
+				  GFP_KERNEL | __GFP_HIGHMEM
+				  );
+#elif defined PAGE_KERNEL_EXEC
+		pe_image->image =
+			__vmalloc(load_driver->sys_files[i].size,
+				  GFP_KERNEL | __GFP_HIGHMEM
+				  );
+#else
+#error x86_64 should have either PAGE_KERNEL_EXECUTABLE or PAGE_KERNEL_EXEC
+#endif
+#else
+		/* hate to play with kernel macros, but PAGE_KERNEL_EXEC is
+		 * not available to modules! */
+#ifdef cpu_has_nx
+		if (cpu_has_nx)
+			pe_image->image =
+				__vmalloc(load_driver->sys_files[i].size,
+					  GFP_KERNEL | __GFP_HIGHMEM
+					  );
+		else
+			pe_image->image =
+				vmalloc(load_driver->sys_files[i].size);
+#else
+			pe_image->image =
+				vmalloc(load_driver->sys_files[i].size);
+#endif
+#endif
+		if (!pe_image->image) {
+			ERROR("couldn't allocate memory");
+			err = -ENOMEM;
+			break;
+		}
+		TRACE1("image is at %p", pe_image->image);
+
+		if (copy_from_user(pe_image->image,
+				   load_driver->sys_files[i].data,
+				   load_driver->sys_files[i].size)) {
+			ERROR("couldn't load file %s",
+			      load_driver->sys_files[i].name);
+			err = -EFAULT;
+			break;
+		}
+		pe_image->size = load_driver->sys_files[i].size;
+		driver->num_pe_images++;
+	}
+
+	if (!err && link_pe_images(driver->pe_images, driver->num_pe_images)) {
+		ERROR("couldn't prepare driver '%s'", load_driver->name);
+		err = -EINVAL;
+	}
+
+	if (driver->num_pe_images < load_driver->num_sys_files || err) {
+		for (i = 0; i < driver->num_pe_images; i++)
+			if (driver->pe_images[i].image)
+				vfree(driver->pe_images[i].image);
+		driver->num_pe_images = 0;
+		EXIT1(return err);
+	} else
+		EXIT1(return 0);
+}
+
+struct wrap_bin_file *get_bin_file(char *bin_file_name)
+{
+	int i = 0;
+	struct wrap_driver *driver, *cur;
+
+	ENTER1("%s", bin_file_name);
+	mutex_lock(&loader_mutex);
+	driver = NULL;
+	nt_list_for_each_entry(cur, &wrap_drivers, list) {
+		for (i = 0; i < cur->num_bin_files; i++)
+			if (!stricmp(cur->bin_files[i].name, bin_file_name)) {
+				driver = cur;
+				break;
+			}
+		if (driver)
+			break;
+	}
+	mutex_unlock(&loader_mutex);
+	if (!driver) {
+		TRACE1("couldn't find bin file '%s'", bin_file_name);
+		return NULL;
+	}
+
+	if (!driver->bin_files[i].data) {
+		char *argv[] = {"loadndisdriver", WRAP_CMD_LOAD_BIN_FILE,
+#if DEBUG >= 1
+				"1",
+#else
+				"0",
+#endif
+				UTILS_VERSION, driver->name,
+				driver->bin_files[i].name, NULL};
+		char *env[] = {NULL};
+		int ret;
+
+		TRACE1("loading bin file %s/%s", driver->name,
+		       driver->bin_files[i].name);
+		mutex_lock(&loader_mutex);
+		reinit_completion(&loader_complete);
+		ret = call_usermodehelper("/sbin/loadndisdriver", argv, env,
+					  UMH_WAIT_PROC);
+		if (ret) {
+			mutex_unlock(&loader_mutex);
+			ERROR("couldn't load file %s/%s; check system log "
+			      "for messages from 'loadndisdriver' (%d)",
+			      driver->name, driver->bin_files[i].name, ret);
+			EXIT1(return NULL);
+		}
+		wait_for_completion(&loader_complete);
+		mutex_unlock(&loader_mutex);
+		if (!driver->bin_files[i].data) {
+			WARNING("couldn't load binary file %s",
+				driver->bin_files[i].name);
+			EXIT1(return NULL);
+		}
+	}
+	EXIT2(return &(driver->bin_files[i]));
+}
+
+/* called with loader_mutex down */
+static int add_bin_file(struct load_driver_file *driver_file)
+{
+	struct wrap_driver *driver, *cur;
+	struct wrap_bin_file *bin_file;
+	int i = 0;
+
+	driver = NULL;
+	nt_list_for_each_entry(cur, &wrap_drivers, list) {
+		for (i = 0; i < cur->num_bin_files; i++)
+			if (!stricmp(cur->bin_files[i].name,
+				     driver_file->name)) {
+				driver = cur;
+				break;
+			}
+		if (driver)
+			break;
+	}
+	if (!driver) {
+		ERROR("couldn't find %s", driver_file->name);
+		return -EINVAL;
+	}
+	bin_file = &driver->bin_files[i];
+	strncpy(bin_file->name, driver_file->name, sizeof(bin_file->name));
+	bin_file->name[sizeof(bin_file->name)-1] = 0;
+	bin_file->data = vmalloc(driver_file->size);
+	if (!bin_file->data) {
+		ERROR("couldn't allocate memory");
+		return -ENOMEM;
+	}
+	bin_file->size = driver_file->size;
+	if (copy_from_user(bin_file->data, driver_file->data, bin_file->size)) {
+		ERROR("couldn't copy data");
+		free_bin_file(bin_file);
+		return -EFAULT;
+	}
+	return 0;
+}
+
+void free_bin_file(struct wrap_bin_file *bin_file)
+{
+	TRACE2("unloading %s", bin_file->name);
+	if (bin_file->data)
+		vfree(bin_file->data);
+	bin_file->data = NULL;
+	bin_file->size = 0;
+	EXIT2(return);
+}
+
+/* load firmware files from userspace */
+static int load_bin_files_info(struct wrap_driver *driver,
+			       struct load_driver *load_driver)
+{
+	struct wrap_bin_file *bin_files;
+	int i;
+
+	ENTER1("%s, %d", load_driver->name, load_driver->num_bin_files);
+	driver->num_bin_files = 0;
+	driver->bin_files = NULL;
+	if (load_driver->num_bin_files == 0)
+		EXIT1(return 0);
+	bin_files = kzalloc(load_driver->num_bin_files * sizeof(*bin_files),
+			    GFP_KERNEL);
+	if (!bin_files) {
+		ERROR("couldn't allocate memory");
+		EXIT1(return -ENOMEM);
+	}
+
+	for (i = 0; i < load_driver->num_bin_files; i++) {
+		strncpy(bin_files[i].name, load_driver->bin_files[i].name,
+			sizeof(bin_files[i].name));
+		bin_files[i].name[sizeof(bin_files[i].name)-1] = 0;
+		TRACE2("loaded bin file %s", bin_files[i].name);
+	}
+	driver->num_bin_files = load_driver->num_bin_files;
+	driver->bin_files = bin_files;
+	EXIT1(return 0);
+}
+
+/* load settings for a device. called with loader_mutex down */
+static int load_settings(struct wrap_driver *wrap_driver,
+			 struct load_driver *load_driver)
+{
+	int i, num_settings;
+
+	ENTER1("%p, %p", wrap_driver, load_driver);
+
+	num_settings = 0;
+	for (i = 0; i < load_driver->num_settings; i++) {
+		struct load_device_setting *load_setting =
+			&load_driver->settings[i];
+		struct wrap_device_setting *setting;
+		ULONG data1;
+
+		setting = kzalloc(sizeof(*setting), GFP_KERNEL);
+		if (!setting) {
+			ERROR("couldn't allocate memory");
+			break;
+		}
+		strncpy(setting->name, load_setting->name,
+			sizeof(setting->name));
+		setting->name[sizeof(setting->name)-1] = 0;
+		strncpy(setting->value, load_setting->value,
+		       sizeof(setting->value));
+		setting->value[sizeof(setting->value)-1] = 0;
+		TRACE2("%p: %s=%s", setting, setting->name, setting->value);
+
+		if (strcmp(setting->name, "driver_version") == 0) {
+			strncpy(wrap_driver->version, setting->value,
+				sizeof(wrap_driver->version));
+			wrap_driver->version[sizeof(wrap_driver->version)-1] = 0;
+		} else if (strcmp(setting->name, "class_guid") == 0 &&
+			   sscanf(setting->value, "%x", &data1) == 1) {
+			wrap_driver->dev_type = wrap_device_type(data1);
+			if (wrap_driver->dev_type < 0) {
+				WARNING("unknown guid: %x", data1);
+				wrap_driver->dev_type = 0;
+			}
+		}
+		InsertTailList(&wrap_driver->settings, &setting->list);
+		num_settings++;
+	}
+	/* it is not a fatal error if some settings couldn't be loaded */
+	if (num_settings > 0)
+		EXIT1(return 0);
+	else
+		EXIT1(return -EINVAL);
+}
+
+void unload_wrap_device(struct wrap_device *wd)
+{
+	struct nt_list *cur;
+	ENTER1("unloading device %p (%04X:%04X:%04X:%04X), driver %s", wd,
+	       wd->vendor, wd->device, wd->subvendor, wd->subdevice,
+	       wd->driver_name);
+	mutex_lock(&loader_mutex);
+	while ((cur = RemoveHeadList(&wd->settings))) {
+		struct wrap_device_setting *setting;
+		setting = container_of(cur, struct wrap_device_setting, list);
+		kfree(setting);
+	}
+	RemoveEntryList(&wd->list);
+	mutex_unlock(&loader_mutex);
+	kfree(wd);
+	EXIT1(return);
+}
+
+/* should be called with loader_mutex down */
+void unload_wrap_driver(struct wrap_driver *driver)
+{
+	int i;
+	struct driver_object *drv_obj;
+	struct nt_list *cur, *next;
+
+	ENTER1("unloading driver: %s (%p)", driver->name, driver);
+	TRACE1("freeing %d images", driver->num_pe_images);
+	drv_obj = driver->drv_obj;
+	for (i = 0; i < driver->num_pe_images; i++)
+		if (driver->pe_images[i].image) {
+			TRACE1("freeing image at %p",
+			       driver->pe_images[i].image);
+			vfree(driver->pe_images[i].image);
+		}
+
+	TRACE1("freeing %d bin files", driver->num_bin_files);
+	for (i = 0; i < driver->num_bin_files; i++) {
+		TRACE1("freeing image at %p", driver->bin_files[i].data);
+		if (driver->bin_files[i].data)
+			vfree(driver->bin_files[i].data);
+	}
+	kfree(driver->bin_files);
+	RtlFreeUnicodeString(&drv_obj->name);
+	RemoveEntryList(&driver->list);
+	nt_list_for_each_safe(cur, next, &driver->settings) {
+		struct wrap_device_setting *setting;
+		struct ndis_configuration_parameter *param;
+
+		setting = container_of(cur, struct wrap_device_setting, list);
+		TRACE2("%p", setting);
+		param = setting->encoded;
+		if (param) {
+			TRACE2("%p", param);
+			if (param->type == NdisParameterString)
+				RtlFreeUnicodeString(&param->data.string);
+			ExFreePool(param);
+		}
+		kfree(setting);
+	}
+	/* this frees driver */
+	free_custom_extensions(drv_obj->drv_ext);
+	kfree(drv_obj->drv_ext);
+	TRACE1("drv_obj: %p", drv_obj);
+
+	EXIT1(return);
+}
+
+/* call the entry point of the driver */
+static int start_wrap_driver(struct wrap_driver *driver)
+{
+	int i;
+	NTSTATUS ret, res;
+	struct driver_object *drv_obj;
+	typeof(driver->pe_images[0].entry) entry;
+
+	ENTER1("%s", driver->name);
+	drv_obj = driver->drv_obj;
+	for (ret = res = 0, i = 0; i < driver->num_pe_images; i++)
+		/* dlls are already started by loader */
+		if (driver->pe_images[i].type == IMAGE_FILE_EXECUTABLE_IMAGE) {
+			entry = driver->pe_images[i].entry;
+			drv_obj->start = driver->pe_images[i].entry;
+			drv_obj->driver_size = driver->pe_images[i].size;
+			TRACE1("entry: %p, %p, drv_obj: %p",
+			       entry, *entry, drv_obj);
+			res = LIN2WIN2(entry, drv_obj, &drv_obj->name);
+			ret |= res;
+			TRACE1("entry returns %08X", res);
+			break;
+		}
+	if (ret) {
+		ERROR("driver initialization failed: %08X", ret);
+		RtlFreeUnicodeString(&drv_obj->name);
+		/* this frees ndis_driver */
+		free_custom_extensions(drv_obj->drv_ext);
+		kfree(drv_obj->drv_ext);
+		TRACE1("drv_obj: %p", drv_obj);
+		ObDereferenceObject(drv_obj);
+		EXIT1(return -EINVAL);
+	}
+	EXIT1(return 0);
+}
+
+/*
+ * add driver to list of loaded driver but make sure this driver is
+ * not loaded before. called with loader_mutex down
+ */
+static int add_wrap_driver(struct wrap_driver *driver)
+{
+	struct wrap_driver *tmp;
+
+	ENTER1("name: %s", driver->name);
+	nt_list_for_each_entry(tmp, &wrap_drivers, list) {
+		if (stricmp(tmp->name, driver->name) == 0) {
+			ERROR("cannot add duplicate driver");
+			EXIT1(return -EBUSY);
+		}
+	}
+	InsertHeadList(&wrap_drivers, &driver->list);
+	EXIT1(return 0);
+}
+
+/* load a driver from userspace and initialize it. called with
+ * loader_mutex down */
+static int load_user_space_driver(struct load_driver *load_driver)
+{
+	struct driver_object *drv_obj;
+	struct ansi_string ansi_reg;
+	struct wrap_driver *wrap_driver = NULL;
+
+	ENTER1("%p", load_driver);
+	drv_obj = allocate_object(sizeof(*drv_obj), OBJECT_TYPE_DRIVER, NULL);
+	if (!drv_obj) {
+		ERROR("couldn't allocate memory");
+		EXIT1(return -ENOMEM);
+	}
+	TRACE1("drv_obj: %p", drv_obj);
+	drv_obj->drv_ext = kzalloc(sizeof(*(drv_obj->drv_ext)), GFP_KERNEL);
+	if (!drv_obj->drv_ext) {
+		ERROR("couldn't allocate memory");
+		ObDereferenceObject(drv_obj);
+		EXIT1(return -ENOMEM);
+	}
+	InitializeListHead(&drv_obj->drv_ext->custom_ext);
+	if (IoAllocateDriverObjectExtension(drv_obj,
+					    (void *)WRAP_DRIVER_CLIENT_ID,
+					    sizeof(*wrap_driver),
+					    (void **)&wrap_driver) !=
+	    STATUS_SUCCESS)
+		EXIT1(return -ENOMEM);
+	TRACE1("driver: %p", wrap_driver);
+	memset(wrap_driver, 0, sizeof(*wrap_driver));
+	InitializeListHead(&wrap_driver->list);
+	InitializeListHead(&wrap_driver->settings);
+	wrap_driver->drv_obj = drv_obj;
+	RtlInitAnsiString(&ansi_reg, "/tmp");
+	if (RtlAnsiStringToUnicodeString(&drv_obj->name, &ansi_reg, TRUE) !=
+	    STATUS_SUCCESS) {
+		ERROR("couldn't initialize registry path");
+		free_custom_extensions(drv_obj->drv_ext);
+		kfree(drv_obj->drv_ext);
+		TRACE1("drv_obj: %p", drv_obj);
+		ObDereferenceObject(drv_obj);
+		EXIT1(return -EINVAL);
+	}
+	strncpy(wrap_driver->name, load_driver->name, sizeof(wrap_driver->name));
+	wrap_driver->name[sizeof(wrap_driver->name)-1] = 0;
+	if (load_sys_files(wrap_driver, load_driver) ||
+	    load_bin_files_info(wrap_driver, load_driver) ||
+	    load_settings(wrap_driver, load_driver) ||
+	    start_wrap_driver(wrap_driver) ||
+	    add_wrap_driver(wrap_driver)) {
+		unload_wrap_driver(wrap_driver);
+		ObDereferenceObject(drv_obj);
+		EXIT1(return -EINVAL);
+	} else {
+		printk(KERN_INFO "%s: driver %s (%s) loaded\n",
+		       DRIVER_NAME, wrap_driver->name, wrap_driver->version);
+		add_taint(TAINT_PROPRIETARY_MODULE, LOCKDEP_NOW_UNRELIABLE);
+		EXIT1(return 0);
+	}
+}
+
+static struct pci_device_id wrap_pci_id_table[] = {
+	{
+		.vendor = PCI_ANY_ID,
+		.device = PCI_ANY_ID,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_ANY_ID,
+		.class = 0,
+		.class_mask = 0,
+		.driver_data = 0
+	}
+};
+
+static struct pci_driver wrap_pci_driver = {
+	.name		= DRIVER_NAME,
+	.id_table	= wrap_pci_id_table,
+	.probe		= wrap_pnp_start_pci_device,
+	.remove		= wrap_pnp_remove_pci_device,
+	.suspend	= wrap_pnp_suspend_pci_device,
+	.resume		= wrap_pnp_resume_pci_device,
+};
+
+#ifdef ENABLE_USB
+static struct usb_device_id wrap_usb_id_table[] = {
+	{
+		.driver_info = 1
+	},
+};
+
+static struct usb_driver wrap_usb_driver = {
+	.name = DRIVER_NAME,
+	.id_table = wrap_usb_id_table,
+	.probe = wrap_pnp_start_usb_device,
+	.disconnect = wrap_pnp_remove_usb_device,
+	.suspend = wrap_pnp_suspend_usb_device,
+	.resume = wrap_pnp_resume_usb_device,
+};
+#endif
+
+/* register drivers for pci and usb */
+static void register_devices(void)
+{
+	int res;
+
+	res = pci_register_driver(&wrap_pci_driver);
+	if (res < 0) {
+		ERROR("couldn't register pci driver: %d", res);
+		wrap_pci_driver.name = NULL;
+	}
+
+#ifdef ENABLE_USB
+	res = usb_register(&wrap_usb_driver);
+	if (res < 0) {
+		ERROR("couldn't register usb driver: %d", res);
+		wrap_usb_driver.name = NULL;
+	}
+#endif
+	EXIT1(return);
+}
+
+static void unregister_devices(void)
+{
+	struct nt_list *cur, *next;
+
+	mutex_lock(&loader_mutex);
+	nt_list_for_each_safe(cur, next, &wrap_devices) {
+		struct wrap_device *wd;
+		wd = container_of(cur, struct wrap_device, list);
+		set_bit(HW_DISABLED, &wd->hw_status);
+	}
+	mutex_unlock(&loader_mutex);
+
+	if (wrap_pci_driver.name)
+		pci_unregister_driver(&wrap_pci_driver);
+#ifdef ENABLE_USB
+	if (wrap_usb_driver.name)
+		usb_deregister(&wrap_usb_driver);
+#endif
+}
+
+struct wrap_device *load_wrap_device(struct load_device *load_device)
+{
+	int ret;
+	struct nt_list *cur;
+	struct wrap_device *wd = NULL;
+	char vendor[5], device[5], subvendor[5], subdevice[5], bus[5];
+
+	ENTER1("%04x, %04x, %04x, %04x", load_device->vendor,
+	       load_device->device, load_device->subvendor,
+	       load_device->subdevice);
+	if (sprintf(vendor, "%04x", load_device->vendor) == 4 &&
+	    sprintf(device, "%04x", load_device->device) == 4 &&
+	    sprintf(subvendor, "%04x", load_device->subvendor) == 4 &&
+	    sprintf(subdevice, "%04x", load_device->subdevice) == 4 &&
+	    sprintf(bus, "%04x", load_device->bus) == 4) {
+		char *argv[] = {"loadndisdriver", WRAP_CMD_LOAD_DEVICE,
+#if DEBUG >= 1
+				"1",
+#else
+				"0",
+#endif
+				UTILS_VERSION, vendor, device,
+				subvendor, subdevice, bus, NULL};
+		char *env[] = {NULL};
+		TRACE2("%s, %s, %s, %s, %s", vendor, device,
+		       subvendor, subdevice, bus);
+		mutex_lock(&loader_mutex);
+		reinit_completion(&loader_complete);
+		ret = call_usermodehelper("/sbin/loadndisdriver", argv, env,
+					  UMH_WAIT_PROC);
+		if (ret) {
+			mutex_unlock(&loader_mutex);
+			TRACE1("couldn't load device %04x:%04x; check system "
+			       "log for messages from 'loadndisdriver'",
+			       load_device->vendor, load_device->device);
+			EXIT1(return NULL);
+		}
+		wait_for_completion(&loader_complete);
+		wd = NULL;
+		nt_list_for_each(cur, &wrap_devices) {
+			wd = container_of(cur, struct wrap_device, list);
+			TRACE2("%p, %04x, %04x, %04x, %04x", wd, wd->vendor,
+			       wd->device, wd->subvendor, wd->subdevice);
+			if (wd->vendor == load_device->vendor &&
+			    wd->device == load_device->device)
+				break;
+			else
+				wd = NULL;
+		}
+		mutex_unlock(&loader_mutex);
+	} else
+		wd = NULL;
+	EXIT1(return wd);
+}
+
+struct wrap_device *get_wrap_device(void *dev, int bus)
+{
+	struct nt_list *cur;
+	struct wrap_device *wd;
+
+	mutex_lock(&loader_mutex);
+	wd = NULL;
+	nt_list_for_each(cur, &wrap_devices) {
+		wd = container_of(cur, struct wrap_device, list);
+		if (bus == WRAP_PCI_BUS &&
+		    wrap_is_pci_bus(wd->dev_bus) && wd->pci.pdev == dev)
+			break;
+		else if (bus == WRAP_USB_BUS &&
+			 wrap_is_usb_bus(wd->dev_bus) && wd->usb.udev == dev)
+			break;
+		else
+			wd = NULL;
+	}
+	mutex_unlock(&loader_mutex);
+	return wd;
+}
+
+/* called with loader_mutex is down */
+static long wrapper_ioctl(struct file *file, unsigned int cmd,
+			  unsigned long arg)
+{
+	struct load_driver *load_driver;
+	struct load_device load_device;
+	struct load_driver_file load_bin_file;
+	int ret;
+	void __user *addr = (void __user *)arg;
+
+	ENTER1("cmd: 0x%x", cmd);
+
+	ret = 0;
+	switch (cmd) {
+	case WRAP_IOCTL_LOAD_DEVICE:
+		if (copy_from_user(&load_device, addr, sizeof(load_device))) {
+			ret = -EFAULT;
+			break;
+		}
+		TRACE2("%04x, %04x, %04x, %04x", load_device.vendor,
+		       load_device.device, load_device.subvendor,
+		       load_device.subdevice);
+		if (load_device.vendor) {
+			struct wrap_device *wd;
+			wd = kzalloc(sizeof(*wd), GFP_KERNEL);
+			if (!wd) {
+				ret = -ENOMEM;
+				break;
+			}
+			InitializeListHead(&wd->settings);
+			wd->dev_bus = WRAP_BUS(load_device.bus);
+			wd->vendor = load_device.vendor;
+			wd->device = load_device.device;
+			wd->subvendor = load_device.subvendor;
+			wd->subdevice = load_device.subdevice;
+			strncpy(wd->conf_file_name, load_device.conf_file_name,
+				sizeof(wd->conf_file_name));
+			wd->conf_file_name[sizeof(wd->conf_file_name)-1] = 0;
+			strncpy(wd->driver_name, load_device.driver_name,
+			       sizeof(wd->driver_name));
+			wd->driver_name[sizeof(wd->driver_name)-1] = 0;
+			InsertHeadList(&wrap_devices, &wd->list);
+			ret = 0;
+		} else
+			ret = -EINVAL;
+		break;
+	case WRAP_IOCTL_LOAD_DRIVER:
+		TRACE1("loading driver at %p", addr);
+		load_driver = vmalloc(sizeof(*load_driver));
+		if (!load_driver) {
+			ret = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(load_driver, addr, sizeof(*load_driver)))
+			ret = -EFAULT;
+		else
+			ret = load_user_space_driver(load_driver);
+		vfree(load_driver);
+		break;
+	case WRAP_IOCTL_LOAD_BIN_FILE:
+		if (copy_from_user(&load_bin_file, addr, sizeof(load_bin_file)))
+			ret = -EFAULT;
+		else
+			ret = add_bin_file(&load_bin_file);
+		break;
+	default:
+		ERROR("unknown ioctl 0x%x", cmd);
+		ret = -EINVAL;
+		break;
+	}
+	complete(&loader_complete);
+	EXIT1(return ret);
+}
+
+#ifdef CONFIG_COMPAT
+static int copy_load_driver_file32(struct load_driver_file *k,
+				   struct load_driver_file32 __user *u)
+{
+	u32 data;
+
+	if (copy_from_user(&k->driver_name, &u->driver_name,
+			   sizeof(u->driver_name) + sizeof(u->name)))
+		return -EFAULT;
+
+	if (get_user(k->size, &u->size))
+		return -EFAULT;
+	if (get_user(data, &u->data))
+		return -EFAULT;
+
+	k->data = (void __user *)(unsigned long)data;
+	return 0;
+}
+
+static int copy_load_driver32(struct load_driver *k,
+			      struct load_driver32 __user *u)
+{
+	int i;
+
+	if (copy_from_user(&k->name, &u->name,
+			   sizeof(u->name) + sizeof(u->conf_file_name)))
+		return -EFAULT;
+
+	if (get_user(k->num_sys_files, &u->num_sys_files))
+		return -EFAULT;
+
+	for (i = 0; i < k->num_sys_files; i++)
+		if (copy_load_driver_file32(&k->sys_files[i], &u->sys_files[i]))
+			return -EFAULT;
+
+	if (get_user(k->num_settings, &u->num_settings))
+		return -EFAULT;
+
+	if (copy_from_user(&k->settings, &u->settings,
+			   sizeof(u->settings[0]) * k->num_settings))
+		return -EFAULT;
+
+	if (get_user(k->num_bin_files, &u->num_bin_files))
+		return -EFAULT;
+
+	for (i = 0; i < k->num_bin_files; i++)
+		if (copy_load_driver_file32(&k->bin_files[i], &u->bin_files[i]))
+			return -EFAULT;
+
+	return 0;
+}
+
+static long wrapper_ioctl_compat(struct file *file, unsigned int cmd,
+				 unsigned long arg)
+{
+	int ret = 0;
+	void __user *addr = (void __user *)arg;
+	struct load_driver *kdriver;
+	struct load_driver32 __user *udriver = addr;
+	struct load_driver_file kfile;
+	struct load_driver_file32 __user *ufile = addr;
+
+	ENTER1("cmd: 0x%x", cmd);
+
+	switch (cmd) {
+	case WRAP_IOCTL_LOAD_DEVICE32:
+		return wrapper_ioctl(file, WRAP_IOCTL_LOAD_DEVICE, arg);
+	case WRAP_IOCTL_LOAD_DRIVER32:
+		TRACE1("loading driver at %p", addr);
+		kdriver = vmalloc(sizeof(*kdriver));
+		if (!kdriver) {
+			ret = -ENOMEM;
+			break;
+		}
+
+		ret = copy_load_driver32(kdriver, udriver);
+		if (!ret)
+			ret = load_user_space_driver(kdriver);
+
+		vfree(kdriver);
+		break;
+	case WRAP_IOCTL_LOAD_BIN_FILE32:
+		ret = copy_load_driver_file32(&kfile, ufile);
+		if (ret)
+			break;
+
+		ret = add_bin_file(&kfile);
+		break;
+	default:
+		ERROR("unknown ioctl 0x%x", cmd);
+		ret = -EINVAL;
+		break;
+	}
+	complete(&loader_complete);
+	EXIT1(return ret);
+}
+#endif
+
+static int wrapper_ioctl_release(struct inode *inode, struct file *file)
+{
+	ENTER1("");
+	complete(&loader_complete);
+	return 0;
+}
+
+static struct file_operations wrapper_fops = {
+	.owner		= THIS_MODULE,
+	.unlocked_ioctl	= wrapper_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= wrapper_ioctl_compat,
+#endif
+	.release	= wrapper_ioctl_release,
+};
+
+static struct miscdevice wrapper_misc = {
+	.name	= DRIVER_NAME,
+	.minor	= MISC_DYNAMIC_MINOR,
+	.fops	= &wrapper_fops
+};
+
+int loader_init(void)
+{
+	int err;
+
+	InitializeListHead(&wrap_drivers);
+	InitializeListHead(&wrap_devices);
+	mutex_init(&loader_mutex);
+	init_completion(&loader_complete);
+	if ((err = misc_register(&wrapper_misc)) < 0) {
+		ERROR("couldn't register module (%d)", err);
+		unregister_devices();
+		EXIT1(return err);
+	}
+	register_devices();
+	EXIT1(return 0);
+}
+
+void loader_exit(void)
+{
+	struct nt_list *cur, *next;
+
+	ENTER1("");
+	misc_deregister(&wrapper_misc);
+	unregister_devices();
+	mutex_lock(&loader_mutex);
+	nt_list_for_each_safe(cur, next, &wrap_drivers) {
+		struct wrap_driver *driver;
+		driver = container_of(cur, struct wrap_driver, list);
+		unload_wrap_driver(driver);
+	}
+	mutex_unlock(&loader_mutex);
+	EXIT1(return);
+}
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/loader.h linux-5.6.11-ndis/3rdparty/ndiswrapper/loader.h
--- linux-5.6.11/3rdparty/ndiswrapper/loader.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/loader.h	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,108 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LOADER_H_
+#define _LOADER_H_
+
+#include "ndiswrapper.h"
+
+#ifndef __KERNEL__
+#define __user
+#endif
+
+struct load_driver_file {
+	char driver_name[MAX_DRIVER_NAME_LEN];
+	char name[MAX_DRIVER_NAME_LEN];
+	size_t size;
+	void __user *data;
+};
+
+struct load_device_setting {
+	char name[MAX_SETTING_NAME_LEN];
+	char value[MAX_SETTING_VALUE_LEN];
+};
+
+struct load_device {
+	int bus;
+	int vendor;
+	int device;
+	int subvendor;
+	int subdevice;
+	char conf_file_name[MAX_DRIVER_NAME_LEN];
+	char driver_name[MAX_DRIVER_NAME_LEN];
+};
+
+struct load_driver {
+	char name[MAX_DRIVER_NAME_LEN];
+	char conf_file_name[MAX_DRIVER_NAME_LEN];
+	unsigned int num_sys_files;
+	struct load_driver_file sys_files[MAX_DRIVER_PE_IMAGES];
+	unsigned int num_settings;
+	struct load_device_setting settings[MAX_DEVICE_SETTINGS];
+	unsigned int num_bin_files;
+	struct load_driver_file bin_files[MAX_DRIVER_BIN_FILES];
+};
+
+#define WRAP_IOCTL_LOAD_DEVICE _IOW(('N' + 'd' + 'i' + 'S'), 0,	\
+				    struct load_device *)
+#define WRAP_IOCTL_LOAD_DRIVER _IOW(('N' + 'd' + 'i' + 'S'), 1,	\
+				    struct load_driver *)
+#define WRAP_IOCTL_LOAD_BIN_FILE _IOW(('N' + 'd' + 'i' + 'S'), 2,	\
+				      struct load_driver_file *)
+
+#ifdef CONFIG_COMPAT
+struct load_driver_file32 {
+	char driver_name[MAX_DRIVER_NAME_LEN];
+	char name[MAX_DRIVER_NAME_LEN];
+	u32 size;
+	u32 data;
+};
+
+struct load_driver32 {
+	char name[MAX_DRIVER_NAME_LEN];
+	char conf_file_name[MAX_DRIVER_NAME_LEN];
+	u32 num_sys_files;
+	struct load_driver_file32 sys_files[MAX_DRIVER_PE_IMAGES];
+	u32 num_settings;
+	struct load_device_setting settings[MAX_DEVICE_SETTINGS];
+	u32 num_bin_files;
+	struct load_driver_file32 bin_files[MAX_DRIVER_BIN_FILES];
+} __packed;
+
+#define WRAP_IOCTL_LOAD_DEVICE32 _IOW(('N' + 'd' + 'i' + 'S'), 0, u32)
+#define WRAP_IOCTL_LOAD_DRIVER32 _IOW(('N' + 'd' + 'i' + 'S'), 1, u32)
+#define WRAP_IOCTL_LOAD_BIN_FILE32 _IOW(('N' + 'd' + 'i' + 'S'), 2, u32)
+#endif
+
+#define WRAP_CMD_LOAD_DEVICE "load_device"
+#define WRAP_CMD_LOAD_DRIVER "load_driver"
+#define WRAP_CMD_LOAD_BIN_FILE "load_bin_file"
+
+int loader_init(void);
+void loader_exit(void);
+
+#ifdef __KERNEL__
+struct wrap_device *load_wrap_device(struct load_device *load_device);
+struct wrap_driver *load_wrap_driver(struct wrap_device *device);
+struct wrap_bin_file *get_bin_file(char *bin_file_name);
+void free_bin_file(struct wrap_bin_file *bin_file);
+void unload_wrap_driver(struct wrap_driver *driver);
+void unload_wrap_device(struct wrap_device *wd);
+struct wrap_device *get_wrap_device(void *dev, int bus_type);
+
+extern struct mutex loader_mutex;
+#endif
+
+#endif /* LOADER_H */
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/longlong.h linux-5.6.11-ndis/3rdparty/ndiswrapper/longlong.h
--- linux-5.6.11/3rdparty/ndiswrapper/longlong.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/longlong.h	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,1333 @@
+/* longlong.h -- definitions for mixed size 32/64 bit arithmetic.
+   Copyright (C) 1991, 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2000
+   Free Software Foundation, Inc.
+
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* You have to define the following before including this file:
+
+   UWtype -- An unsigned type, default type for operations (typically a "word")
+   UHWtype -- An unsigned type, at least half the size of UWtype.
+   UDWtype -- An unsigned type, at least twice as large a UWtype
+   W_TYPE_SIZE -- size in bits of UWtype
+
+   UQItype -- Unsigned 8 bit type.
+   SItype, USItype -- Signed and unsigned 32 bit types.
+   DItype, UDItype -- Signed and unsigned 64 bit types.
+
+   On a 32 bit machine UWtype should typically be USItype;
+   on a 64 bit machine, UWtype should typically be UDItype.
+*/
+
+#define __BITS4 (W_TYPE_SIZE / 4)
+#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2))
+#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1))
+#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2))
+
+#ifndef W_TYPE_SIZE
+#define W_TYPE_SIZE	32
+#define UWtype		USItype
+#define UHWtype		USItype
+#define UDWtype		UDItype
+#endif
+
+/* Define auxiliary asm macros.
+
+   1) umul_ppmm(high_prod, low_prod, multipler, multiplicand) multiplies two
+   UWtype integers MULTIPLER and MULTIPLICAND, and generates a two UWtype
+   word product in HIGH_PROD and LOW_PROD.
+
+   2) __umulsidi3(a,b) multiplies two UWtype integers A and B, and returns a
+   UDWtype product.  This is just a variant of umul_ppmm.
+
+   3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
+   denominator) divides a UDWtype, composed by the UWtype integers
+   HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient
+   in QUOTIENT and the remainder in REMAINDER.  HIGH_NUMERATOR must be less
+   than DENOMINATOR for correct operation.  If, in addition, the most
+   significant bit of DENOMINATOR must be 1, then the pre-processor symbol
+   UDIV_NEEDS_NORMALIZATION is defined to 1.
+
+   4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
+   denominator).  Like udiv_qrnnd but the numbers are signed.  The quotient
+   is rounded towards 0.
+
+   5) count_leading_zeros(count, x) counts the number of zero-bits from the
+   msb to the first nonzero bit in the UWtype X.  This is the number of
+   steps X needs to be shifted left to set the msb.  Undefined for X == 0,
+   unless the symbol COUNT_LEADING_ZEROS_0 is defined to some value.
+
+   6) count_trailing_zeros(count, x) like count_leading_zeros, but counts
+   from the least significant end.
+
+   7) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1,
+   high_addend_2, low_addend_2) adds two UWtype integers, composed by
+   HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2
+   respectively.  The result is placed in HIGH_SUM and LOW_SUM.  Overflow
+   (i.e. carry out) is not stored anywhere, and is lost.
+
+   8) sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend,
+   high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers,
+   composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and
+   LOW_SUBTRAHEND_2 respectively.  The result is placed in HIGH_DIFFERENCE
+   and LOW_DIFFERENCE.  Overflow (i.e. carry out) is not stored anywhere,
+   and is lost.
+
+   If any of these macros are left undefined for a particular CPU,
+   C macros are used.  */
+
+/* The CPUs come in alphabetical order below.
+
+   Please add support for more CPUs here, or improve the current support
+   for the CPUs below!
+   (E.g. WE32100, IBM360.)  */
+
+#if defined (__GNUC__) && !defined (NO_ASM)
+
+/* We sometimes need to clobber "cc" with gcc2, but that would not be
+   understood by gcc1.  Use cpp to avoid major code duplication.  */
+#if __GNUC__ < 2
+#define __CLOBBER_CC
+#define __AND_CLOBBER_CC
+#else /* __GNUC__ >= 2 */
+#define __CLOBBER_CC : "cc"
+#define __AND_CLOBBER_CC , "cc"
+#endif /* __GNUC__ < 2 */
+
+#if defined (__alpha) && W_TYPE_SIZE == 64
+#define umul_ppmm(ph, pl, m0, m1) \
+  do {									\
+    UDItype __m0 = (m0), __m1 = (m1);					\
+    __asm__ ("umulh %r1,%2,%0"						\
+	     : "=r" ((UDItype) ph)					\
+	     : "%rJ" (__m0),						\
+	       "rI" (__m1));						\
+    (pl) = __m0 * __m1;							\
+  } while (0)
+#define UMUL_TIME 46
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  do { UDItype __r;							\
+    (q) = __udiv_qrnnd (&__r, (n1), (n0), (d));				\
+    (r) = __r;								\
+  } while (0)
+extern UDItype __udiv_qrnnd (UDItype *, UDItype, UDItype, UDItype);
+#define UDIV_TIME 220
+#endif /* LONGLONG_STANDALONE */
+#ifdef __alpha_cix__
+#define count_leading_zeros(COUNT,X) \
+  __asm__("ctlz %1,%0" : "=r"(COUNT) : "r"(X))
+#define count_trailing_zeros(COUNT,X) \
+  __asm__("cttz %1,%0" : "=r"(COUNT) : "r"(X))
+#define COUNT_LEADING_ZEROS_0 64
+#else
+extern const UQItype __clz_tab[];
+#define count_leading_zeros(COUNT,X) \
+  do {									\
+    UDItype __xr = (X), __t, __a;					\
+    __asm__("cmpbge $31,%1,%0" : "=r"(__t) : "r"(__xr));		\
+    __a = __clz_tab[__t ^ 0xff] - 1;					\
+    __asm__("extbl %1,%2,%0" : "=r"(__t) : "r"(__xr), "r"(__a));	\
+    (COUNT) = 64 - (__clz_tab[__t] + __a*8);				\
+  } while (0)
+#define count_trailing_zeros(COUNT,X) \
+  do {									\
+    UDItype __xr = (X), __t, __a;					\
+    __asm__("cmpbge $31,%1,%0" : "=r"(__t) : "r"(__xr));		\
+    __t = ~__t & -~__t;							\
+    __a = ((__t & 0xCC) != 0) * 2;					\
+    __a += ((__t & 0xF0) != 0) * 4;					\
+    __a += ((__t & 0xAA) != 0);						\
+    __asm__("extbl %1,%2,%0" : "=r"(__t) : "r"(__xr), "r"(__a));	\
+    __a <<= 3;								\
+    __t &= -__t;							\
+    __a += ((__t & 0xCC) != 0) * 2;					\
+    __a += ((__t & 0xF0) != 0) * 4;					\
+    __a += ((__t & 0xAA) != 0);						\
+    (COUNT) = __a;							\
+  } while (0)
+#endif /* __alpha_cix__ */
+#endif /* __alpha */
+
+#if defined (__arc__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("add.f	%1, %4, %5\n\tadc	%0, %2, %3"		\
+	   : "=r" ((USItype) (sh)),					\
+	     "=&r" ((USItype) (sl))					\
+	   : "%r" ((USItype) (ah)),					\
+	     "rIJ" ((USItype) (bh)),					\
+	     "%r" ((USItype) (al)),					\
+	     "rIJ" ((USItype) (bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("sub.f	%1, %4, %5\n\tsbc	%0, %2, %3"		\
+	   : "=r" ((USItype) (sh)),					\
+	     "=&r" ((USItype) (sl))					\
+	   : "r" ((USItype) (ah)),					\
+	     "rIJ" ((USItype) (bh)),					\
+	     "r" ((USItype) (al)),					\
+	     "rIJ" ((USItype) (bl)))
+/* Call libgcc routine.  */
+#define umul_ppmm(w1, w0, u, v) \
+do {									\
+  DWunion __w;								\
+  __w.ll = __umulsidi3 (u, v);						\
+  w1 = __w.s.high;							\
+  w0 = __w.s.low;							\
+} while (0)
+#define __umulsidi3 __umulsidi3
+UDItype __umulsidi3 (USItype, USItype);
+#endif
+
+#if defined (__arm__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("adds	%1, %4, %5\n\tadc	%0, %2, %3"		\
+	   : "=r" ((USItype) (sh)),					\
+	     "=&r" ((USItype) (sl))					\
+	   : "%r" ((USItype) (ah)),					\
+	     "rI" ((USItype) (bh)),					\
+	     "%r" ((USItype) (al)),					\
+	     "rI" ((USItype) (bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("subs	%1, %4, %5\n\tsbc	%0, %2, %3"		\
+	   : "=r" ((USItype) (sh)),					\
+	     "=&r" ((USItype) (sl))					\
+	   : "r" ((USItype) (ah)),					\
+	     "rI" ((USItype) (bh)),					\
+	     "r" ((USItype) (al)),					\
+	     "rI" ((USItype) (bl)))
+#define umul_ppmm(xh, xl, a, b) \
+{register USItype __t0, __t1, __t2;					\
+  __asm__ ("%@ Inlined umul_ppmm\n"					\
+	   "	mov	%2, %5, lsr #16\n"				\
+	   "	mov	%0, %6, lsr #16\n"				\
+	   "	bic	%3, %5, %2, lsl #16\n"				\
+	   "	bic	%4, %6, %0, lsl #16\n"				\
+	   "	mul	%1, %3, %4\n"					\
+	   "	mul	%4, %2, %4\n"					\
+	   "	mul	%3, %0, %3\n"					\
+	   "	mul	%0, %2, %0\n"					\
+	   "	adds	%3, %4, %3\n"					\
+	   "	addcs	%0, %0, #65536\n"				\
+	   "	adds	%1, %1, %3, lsl #16\n"				\
+	   "	adc	%0, %0, %3, lsr #16"				\
+	   : "=&r" ((USItype) (xh)),					\
+	     "=r" ((USItype) (xl)),					\
+	     "=&r" (__t0), "=&r" (__t1), "=r" (__t2)			\
+	   : "r" ((USItype) (a)),					\
+	     "r" ((USItype) (b)));}
+#define UMUL_TIME 20
+#define UDIV_TIME 100
+#endif /* __arm__ */
+
+#if defined (__hppa) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("add %4,%5,%1\n\taddc %2,%3,%0"				\
+	   : "=r" ((USItype) (sh)),					\
+	     "=&r" ((USItype) (sl))					\
+	   : "%rM" ((USItype) (ah)),					\
+	     "rM" ((USItype) (bh)),					\
+	     "%rM" ((USItype) (al)),					\
+	     "rM" ((USItype) (bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("sub %4,%5,%1\n\tsubb %2,%3,%0"				\
+	   : "=r" ((USItype) (sh)),					\
+	     "=&r" ((USItype) (sl))					\
+	   : "rM" ((USItype) (ah)),					\
+	     "rM" ((USItype) (bh)),					\
+	     "rM" ((USItype) (al)),					\
+	     "rM" ((USItype) (bl)))
+#if defined (_PA_RISC1_1)
+#define umul_ppmm(w1, w0, u, v) \
+  do {									\
+    union								\
+      {									\
+	UDItype __f;							\
+	struct {USItype __w1, __w0;} __w1w0;				\
+      } __t;								\
+    __asm__ ("xmpyu %1,%2,%0"						\
+	     : "=x" (__t.__f)						\
+	     : "x" ((USItype) (u)),					\
+	       "x" ((USItype) (v)));					\
+    (w1) = __t.__w1w0.__w1;						\
+    (w0) = __t.__w1w0.__w0;						\
+     } while (0)
+#define UMUL_TIME 8
+#else
+#define UMUL_TIME 30
+#endif
+#define UDIV_TIME 40
+#define count_leading_zeros(count, x) \
+  do {									\
+    USItype __tmp;							\
+    __asm__ (								\
+       "ldi		1,%0\n"						\
+"	extru,=		%1,15,16,%%r0		; Bits 31..16 zero?\n"	\
+"	extru,tr	%1,15,16,%1		; No.  Shift down, skip add.\n"\
+"	ldo		16(%0),%0		; Yes.  Perform add.\n"	\
+"	extru,=		%1,23,8,%%r0		; Bits 15..8 zero?\n"	\
+"	extru,tr	%1,23,8,%1		; No.  Shift down, skip add.\n"\
+"	ldo		8(%0),%0		; Yes.  Perform add.\n"	\
+"	extru,=		%1,27,4,%%r0		; Bits 7..4 zero?\n"	\
+"	extru,tr	%1,27,4,%1		; No.  Shift down, skip add.\n"\
+"	ldo		4(%0),%0		; Yes.  Perform add.\n"	\
+"	extru,=		%1,29,2,%%r0		; Bits 3..2 zero?\n"	\
+"	extru,tr	%1,29,2,%1		; No.  Shift down, skip add.\n"\
+"	ldo		2(%0),%0		; Yes.  Perform add.\n"	\
+"	extru		%1,30,1,%1		; Extract bit 1.\n"	\
+"	sub		%0,%1,%0		; Subtract it.\n"	\
+	: "=r" (count), "=r" (__tmp) : "1" (x));			\
+  } while (0)
+#endif
+
+#if (defined (__i370__) || defined (__mvs__)) && W_TYPE_SIZE == 32
+#define umul_ppmm(xh, xl, m0, m1) \
+  do {									\
+    union {UDItype __ll;						\
+	   struct {USItype __h, __l;} __i;				\
+	  } __xx;							\
+    USItype __m0 = (m0), __m1 = (m1);					\
+    __asm__ ("mr %0,%3"							\
+	     : "=r" (__xx.__i.__h),					\
+	       "=r" (__xx.__i.__l)					\
+	     : "%1" (__m0),						\
+	       "r" (__m1));						\
+    (xh) = __xx.__i.__h; (xl) = __xx.__i.__l;				\
+    (xh) += ((((SItype) __m0 >> 31) & __m1)				\
+	     + (((SItype) __m1 >> 31) & __m0));				\
+  } while (0)
+#define smul_ppmm(xh, xl, m0, m1) \
+  do {									\
+    union {DItype __ll;							\
+	   struct {USItype __h, __l;} __i;				\
+	  } __xx;							\
+    __asm__ ("mr %0,%3"							\
+	     : "=r" (__xx.__i.__h),					\
+	       "=r" (__xx.__i.__l)					\
+	     : "%1" (m0),						\
+	       "r" (m1));						\
+    (xh) = __xx.__i.__h; (xl) = __xx.__i.__l;				\
+  } while (0)
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+  do {									\
+    union {DItype __ll;							\
+	   struct {USItype __h, __l;} __i;				\
+	  } __xx;							\
+    __xx.__i.__h = n1; __xx.__i.__l = n0;				\
+    __asm__ ("dr %0,%2"							\
+	     : "=r" (__xx.__ll)						\
+	     : "0" (__xx.__ll), "r" (d));				\
+    (q) = __xx.__i.__l; (r) = __xx.__i.__h;				\
+  } while (0)
+#endif
+
+#if (defined (__i386__) || defined (__i486__)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("addl %5,%1\n\tadcl %3,%0"					\
+	   : "=r" (sh),							\
+	     "=&r" (sl)							\
+	   : "%0" ((USItype) (ah)),					\
+	     "g" ((USItype) (bh)),					\
+	     "%1" ((USItype) (al)),					\
+	     "g" ((USItype) (bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("subl %5,%1\n\tsbbl %3,%0"					\
+	   : "=r" (sh),							\
+	     "=&r" (sl)							\
+	   : "0" ((USItype) (ah)),					\
+	     "g" ((USItype) (bh)),					\
+	     "1" ((USItype) (al)),					\
+	     "g" ((USItype) (bl)))
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("mull %3"							\
+	   : "=a" (w0),							\
+	     "=d" (w1)							\
+	   : "%0" ((USItype) (u)),					\
+	     "rm" ((USItype) (v)))
+#define udiv_qrnnd(q, r, n1, n0, dv) \
+  __asm__ ("divl %4"							\
+	   : "=a" (q),							\
+	     "=d" (r)							\
+	   : "0" ((USItype) (n0)),					\
+	     "1" ((USItype) (n1)),					\
+	     "rm" ((USItype) (dv)))
+#define count_leading_zeros(count, x) \
+  do {									\
+    USItype __cbtmp;							\
+    __asm__ ("bsrl %1,%0"						\
+	     : "=r" (__cbtmp) : "rm" ((USItype) (x)));			\
+    (count) = __cbtmp ^ 31;						\
+  } while (0)
+#define count_trailing_zeros(count, x) \
+  __asm__ ("bsfl %1,%0" : "=r" (count) : "rm" ((USItype)(x)))
+#define UMUL_TIME 40
+#define UDIV_TIME 40
+#endif /* 80x86 */
+
+#if defined (__i960__) && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+  ({union {UDItype __ll;						\
+	   struct {USItype __l, __h;} __i;				\
+	  } __xx;							\
+  __asm__ ("emul	%2,%1,%0"					\
+	   : "=d" (__xx.__ll)						\
+	   : "%dI" ((USItype) (u)),					\
+	     "dI" ((USItype) (v)));					\
+  (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define __umulsidi3(u, v) \
+  ({UDItype __w;							\
+    __asm__ ("emul	%2,%1,%0"					\
+	     : "=d" (__w)						\
+	     : "%dI" ((USItype) (u)),					\
+	       "dI" ((USItype) (v)));					\
+    __w; })
+#endif /* __i960__ */
+
+#if defined (__M32R__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  /* The cmp clears the condition bit.  */ \
+  __asm__ ("cmp %0,%0\n\taddx %%5,%1\n\taddx %%3,%0"			\
+	   : "=r" ((USItype) (sh)),					\
+	     "=&r" ((USItype) (sl))					\
+	   : "%0" ((USItype) (ah)),					\
+	     "r" ((USItype) (bh)),					\
+	     "%1" ((USItype) (al)),					\
+	     "r" ((USItype) (bl))					\
+	   : "cbit")
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  /* The cmp clears the condition bit.  */ \
+  __asm__ ("cmp %0,%0\n\tsubx %5,%1\n\tsubx %3,%0"			\
+	   : "=r" ((USItype) (sh)),					\
+	     "=&r" ((USItype) (sl))					\
+	   : "0" ((USItype) (ah)),					\
+	     "r" ((USItype) (bh)),					\
+	     "1" ((USItype) (al)),					\
+	     "r" ((USItype) (bl))					\
+	   : "cbit")
+#endif /* __M32R__ */
+
+#if defined (__mc68000__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("add%.l %5,%1\n\taddx%.l %3,%0"				\
+	   : "=d" ((USItype) (sh)),					\
+	     "=&d" ((USItype) (sl))					\
+	   : "%0" ((USItype) (ah)),					\
+	     "d" ((USItype) (bh)),					\
+	     "%1" ((USItype) (al)),					\
+	     "g" ((USItype) (bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("sub%.l %5,%1\n\tsubx%.l %3,%0"				\
+	   : "=d" ((USItype) (sh)),					\
+	     "=&d" ((USItype) (sl))					\
+	   : "0" ((USItype) (ah)),					\
+	     "d" ((USItype) (bh)),					\
+	     "1" ((USItype) (al)),					\
+	     "g" ((USItype) (bl)))
+
+/* The '020, '030, '040 and CPU32 have 32x32->64 and 64/32->32q-32r.  */
+#if defined (__mc68020__) || defined(mc68020) \
+	|| defined(__mc68030__) || defined(mc68030) \
+	|| defined(__mc68040__) || defined(mc68040) \
+	|| defined(__mcpu32__) || defined(mcpu32)
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("mulu%.l %3,%1:%0"						\
+	   : "=d" ((USItype) (w0)),					\
+	     "=d" ((USItype) (w1))					\
+	   : "%0" ((USItype) (u)),					\
+	     "dmi" ((USItype) (v)))
+#define UMUL_TIME 45
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  __asm__ ("divu%.l %4,%1:%0"						\
+	   : "=d" ((USItype) (q)),					\
+	     "=d" ((USItype) (r))					\
+	   : "0" ((USItype) (n0)),					\
+	     "1" ((USItype) (n1)),					\
+	     "dmi" ((USItype) (d)))
+#define UDIV_TIME 90
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+  __asm__ ("divs%.l %4,%1:%0"						\
+	   : "=d" ((USItype) (q)),					\
+	     "=d" ((USItype) (r))					\
+	   : "0" ((USItype) (n0)),					\
+	     "1" ((USItype) (n1)),					\
+	     "dmi" ((USItype) (d)))
+
+#else /* not mc68020 */
+#if !defined(__mcf5200__)
+/* %/ inserts REGISTER_PREFIX, %# inserts IMMEDIATE_PREFIX.  */
+#define umul_ppmm(xh, xl, a, b) \
+  __asm__ ("| Inlined umul_ppmm\n"					\
+	   "	move%.l	%2,%/d0\n"					\
+	   "	move%.l	%3,%/d1\n"					\
+	   "	move%.l	%/d0,%/d2\n"					\
+	   "	swap	%/d0\n"						\
+	   "	move%.l	%/d1,%/d3\n"					\
+	   "	swap	%/d1\n"						\
+	   "	move%.w	%/d2,%/d4\n"					\
+	   "	mulu	%/d3,%/d4\n"					\
+	   "	mulu	%/d1,%/d2\n"					\
+	   "	mulu	%/d0,%/d3\n"					\
+	   "	mulu	%/d0,%/d1\n"					\
+	   "	move%.l	%/d4,%/d0\n"					\
+	   "	eor%.w	%/d0,%/d0\n"					\
+	   "	swap	%/d0\n"						\
+	   "	add%.l	%/d0,%/d2\n"					\
+	   "	add%.l	%/d3,%/d2\n"					\
+	   "	jcc	1f\n"						\
+	   "	add%.l	%#65536,%/d1\n"					\
+	   "1:	swap	%/d2\n"						\
+	   "	moveq	%#0,%/d0\n"					\
+	   "	move%.w	%/d2,%/d0\n"					\
+	   "	move%.w	%/d4,%/d2\n"					\
+	   "	move%.l	%/d2,%1\n"					\
+	   "	add%.l	%/d1,%/d0\n"					\
+	   "	move%.l	%/d0,%0"					\
+	   : "=g" ((USItype) (xh)),					\
+	     "=g" ((USItype) (xl))					\
+	   : "g" ((USItype) (a)),					\
+	     "g" ((USItype) (b))					\
+	   : "d0", "d1", "d2", "d3", "d4")
+#define UMUL_TIME 100
+#define UDIV_TIME 400
+#endif /* not mcf5200 */
+#endif /* not mc68020 */
+
+/* The '020, '030, '040 and '060 have bitfield insns.  */
+#if defined (__mc68020__) || defined(mc68020) \
+	|| defined(__mc68030__) || defined(mc68030) \
+	|| defined(__mc68040__) || defined(mc68040) \
+	|| defined(__mc68060__) || defined(mc68060)
+#define count_leading_zeros(count, x) \
+  __asm__ ("bfffo %1{%b2:%b2},%0"					\
+	   : "=d" ((USItype) (count))					\
+	   : "od" ((USItype) (x)), "n" (0))
+#endif
+#endif /* mc68000 */
+
+#if defined (__m88000__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("addu.co %1,%r4,%r5\n\taddu.ci %0,%r2,%r3"			\
+	   : "=r" ((USItype) (sh)),					\
+	     "=&r" ((USItype) (sl))					\
+	   : "%rJ" ((USItype) (ah)),					\
+	     "rJ" ((USItype) (bh)),					\
+	     "%rJ" ((USItype) (al)),					\
+	     "rJ" ((USItype) (bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("subu.co %1,%r4,%r5\n\tsubu.ci %0,%r2,%r3"			\
+	   : "=r" ((USItype) (sh)),					\
+	     "=&r" ((USItype) (sl))					\
+	   : "rJ" ((USItype) (ah)),					\
+	     "rJ" ((USItype) (bh)),					\
+	     "rJ" ((USItype) (al)),					\
+	     "rJ" ((USItype) (bl)))
+#define count_leading_zeros(count, x) \
+  do {									\
+    USItype __cbtmp;							\
+    __asm__ ("ff1 %0,%1"						\
+	     : "=r" (__cbtmp)						\
+	     : "r" ((USItype) (x)));					\
+    (count) = __cbtmp ^ 31;						\
+  } while (0)
+#define COUNT_LEADING_ZEROS_0 63 /* sic */
+#if defined (__mc88110__)
+#define umul_ppmm(wh, wl, u, v) \
+  do {									\
+    union {UDItype __ll;						\
+	   struct {USItype __h, __l;} __i;				\
+	  } __xx;							\
+    __asm__ ("mulu.d	%0,%1,%2"					\
+	     : "=r" (__xx.__ll)						\
+	     : "r" ((USItype) (u)),					\
+	       "r" ((USItype) (v)));					\
+    (wh) = __xx.__i.__h;						\
+    (wl) = __xx.__i.__l;						\
+  } while (0)
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  ({union {UDItype __ll;						\
+	   struct {USItype __h, __l;} __i;				\
+	  } __xx;							\
+  USItype __q;								\
+  __xx.__i.__h = (n1); __xx.__i.__l = (n0);				\
+  __asm__ ("divu.d %0,%1,%2"						\
+	   : "=r" (__q)							\
+	   : "r" (__xx.__ll),						\
+	     "r" ((USItype) (d)));					\
+  (r) = (n0) - __q * (d); (q) = __q; })
+#define UMUL_TIME 5
+#define UDIV_TIME 25
+#else
+#define UMUL_TIME 17
+#define UDIV_TIME 150
+#endif /* __mc88110__ */
+#endif /* __m88000__ */
+
+#if defined (__mips__) && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("multu %2,%3"						\
+	   : "=l" ((USItype) (w0)),					\
+	     "=h" ((USItype) (w1))					\
+	   : "d" ((USItype) (u)),					\
+	     "d" ((USItype) (v)))
+#define UMUL_TIME 10
+#define UDIV_TIME 100
+#endif /* __mips__ */
+
+#if defined (__ns32000__) && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+  ({union {UDItype __ll;						\
+	   struct {USItype __l, __h;} __i;				\
+	  } __xx;							\
+  __asm__ ("meid %2,%0"							\
+	   : "=g" (__xx.__ll)						\
+	   : "%0" ((USItype) (u)),					\
+	     "g" ((USItype) (v)));					\
+  (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define __umulsidi3(u, v) \
+  ({UDItype __w;							\
+    __asm__ ("meid %2,%0"						\
+	     : "=g" (__w)						\
+	     : "%0" ((USItype) (u)),					\
+	       "g" ((USItype) (v)));					\
+    __w; })
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  ({union {UDItype __ll;						\
+	   struct {USItype __l, __h;} __i;				\
+	  } __xx;							\
+  __xx.__i.__h = (n1); __xx.__i.__l = (n0);				\
+  __asm__ ("deid %2,%0"							\
+	   : "=g" (__xx.__ll)						\
+	   : "0" (__xx.__ll),						\
+	     "g" ((USItype) (d)));					\
+  (r) = __xx.__i.__l; (q) = __xx.__i.__h; })
+#define count_trailing_zeros(count,x) \
+  do {									\
+    __asm__ ("ffsd     %2,%0"						\
+            : "=r" ((USItype) (count))					\
+            : "0" ((USItype) 0),					\
+              "r" ((USItype) (x)));					\
+  } while (0)
+#endif /* __ns32000__ */
+
+/* FIXME: We should test _IBMR2 here when we add assembly support for the
+   system vendor compilers.
+   FIXME: What's needed for gcc PowerPC VxWorks?  __vxworks__ is not good
+   enough, since that hits ARM and m68k too.  */
+#if (defined (_ARCH_PPC)	/* AIX */				\
+     || defined (_ARCH_PWR)	/* AIX */				\
+     || defined (_ARCH_COM)	/* AIX */				\
+     || defined (__powerpc__)	/* gcc */				\
+     || defined (__POWERPC__)	/* BEOS */				\
+     || defined (__ppc__)	/* Darwin */				\
+     || defined (PPC)		/* GNU/Linux, SysV */			\
+     ) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  do {									\
+    if (__builtin_constant_p (bh) && (bh) == 0)				\
+      __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2"		\
+	     : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\
+    else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0)		\
+      __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2"		\
+	     : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\
+    else								\
+      __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3"		\
+	     : "=r" (sh), "=&r" (sl)					\
+	     : "%r" (ah), "r" (bh), "%r" (al), "rI" (bl));		\
+  } while (0)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  do {									\
+    if (__builtin_constant_p (ah) && (ah) == 0)				\
+      __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2"	\
+	       : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\
+    else if (__builtin_constant_p (ah) && (ah) == ~(USItype) 0)		\
+      __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2"	\
+	       : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\
+    else if (__builtin_constant_p (bh) && (bh) == 0)			\
+      __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2"		\
+	       : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\
+    else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0)		\
+      __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2"		\
+	       : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\
+    else								\
+      __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2"	\
+	       : "=r" (sh), "=&r" (sl)					\
+	       : "r" (ah), "r" (bh), "rI" (al), "r" (bl));		\
+  } while (0)
+#define count_leading_zeros(count, x) \
+  __asm__ ("{cntlz|cntlzw} %0,%1" : "=r" (count) : "r" (x))
+#define COUNT_LEADING_ZEROS_0 32
+#if defined (_ARCH_PPC) || defined (__powerpc__) || defined (__POWERPC__) \
+  || defined (__ppc__) || defined (PPC) || defined (__vxworks__)
+#define umul_ppmm(ph, pl, m0, m1) \
+  do {									\
+    USItype __m0 = (m0), __m1 = (m1);					\
+    __asm__ ("mulhwu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1));	\
+    (pl) = __m0 * __m1;							\
+  } while (0)
+#define UMUL_TIME 15
+#define smul_ppmm(ph, pl, m0, m1) \
+  do {									\
+    SItype __m0 = (m0), __m1 = (m1);					\
+    __asm__ ("mulhw %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1));	\
+    (pl) = __m0 * __m1;							\
+  } while (0)
+#define SMUL_TIME 14
+#define UDIV_TIME 120
+#elif defined (_ARCH_PWR)
+#define UMUL_TIME 8
+#define smul_ppmm(xh, xl, m0, m1) \
+  __asm__ ("mul %0,%2,%3" : "=r" (xh), "=q" (xl) : "r" (m0), "r" (m1))
+#define SMUL_TIME 4
+#define sdiv_qrnnd(q, r, nh, nl, d) \
+  __asm__ ("div %0,%2,%4" : "=r" (q), "=q" (r) : "r" (nh), "1" (nl), "r" (d))
+#define UDIV_TIME 100
+#endif
+#endif /* 32-bit POWER architecture variants.  */
+
+/* We should test _IBMR2 here when we add assembly support for the system
+   vendor compilers.  */
+#if (defined (_ARCH_PPC64) || defined (__powerpc64__)) && W_TYPE_SIZE == 64
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  do {									\
+    if (__builtin_constant_p (bh) && (bh) == 0)				\
+      __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2"		\
+	     : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\
+    else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0)		\
+      __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2"		\
+	     : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl));\
+    else								\
+      __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3"		\
+	     : "=r" (sh), "=&r" (sl)					\
+	     : "%r" (ah), "r" (bh), "%r" (al), "rI" (bl));		\
+  } while (0)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  do {									\
+    if (__builtin_constant_p (ah) && (ah) == 0)				\
+      __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2"	\
+	       : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\
+    else if (__builtin_constant_p (ah) && (ah) == ~(UDItype) 0)		\
+      __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2"	\
+	       : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl));\
+    else if (__builtin_constant_p (bh) && (bh) == 0)			\
+      __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2"		\
+	       : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\
+    else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0)		\
+      __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2"		\
+	       : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl));\
+    else								\
+      __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2"	\
+	       : "=r" (sh), "=&r" (sl)					\
+	       : "r" (ah), "r" (bh), "rI" (al), "r" (bl));		\
+  } while (0)
+#define count_leading_zeros(count, x) \
+  __asm__ ("cntlzd %0,%1" : "=r" (count) : "r" (x))
+#define COUNT_LEADING_ZEROS_0 64
+#define umul_ppmm(ph, pl, m0, m1) \
+  do {									\
+    UDItype __m0 = (m0), __m1 = (m1);					\
+    __asm__ ("mulhdu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1));	\
+    (pl) = __m0 * __m1;							\
+  } while (0)
+#define UMUL_TIME 15
+#define smul_ppmm(ph, pl, m0, m1) \
+  do {									\
+    DItype __m0 = (m0), __m1 = (m1);					\
+    __asm__ ("mulhd %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1));	\
+    (pl) = __m0 * __m1;							\
+  } while (0)
+#define SMUL_TIME 14  /* ??? */
+#define UDIV_TIME 120 /* ??? */
+#endif /* 64-bit PowerPC.  */
+
+#if defined (__ibm032__) /* RT/ROMP */ && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("a %1,%5\n\tae %0,%3"					\
+	   : "=r" ((USItype) (sh)),					\
+	     "=&r" ((USItype) (sl))					\
+	   : "%0" ((USItype) (ah)),					\
+	     "r" ((USItype) (bh)),					\
+	     "%1" ((USItype) (al)),					\
+	     "r" ((USItype) (bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("s %1,%5\n\tse %0,%3"					\
+	   : "=r" ((USItype) (sh)),					\
+	     "=&r" ((USItype) (sl))					\
+	   : "0" ((USItype) (ah)),					\
+	     "r" ((USItype) (bh)),					\
+	     "1" ((USItype) (al)),					\
+	     "r" ((USItype) (bl)))
+#define umul_ppmm(ph, pl, m0, m1) \
+  do {									\
+    USItype __m0 = (m0), __m1 = (m1);					\
+    __asm__ (								\
+       "s	r2,r2\n"						\
+"	mts	r10,%2\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	m	r2,%3\n"						\
+"	cas	%0,r2,r0\n"						\
+"	mfs	r10,%1"							\
+	     : "=r" ((USItype) (ph)),					\
+	       "=r" ((USItype) (pl))					\
+	     : "%r" (__m0),						\
+		"r" (__m1)						\
+	     : "r2");							\
+    (ph) += ((((SItype) __m0 >> 31) & __m1)				\
+	     + (((SItype) __m1 >> 31) & __m0));				\
+  } while (0)
+#define UMUL_TIME 20
+#define UDIV_TIME 200
+#define count_leading_zeros(count, x) \
+  do {									\
+    if ((x) >= 0x10000)							\
+      __asm__ ("clz	%0,%1"						\
+	       : "=r" ((USItype) (count))				\
+	       : "r" ((USItype) (x) >> 16));				\
+    else								\
+      {									\
+	__asm__ ("clz	%0,%1"						\
+		 : "=r" ((USItype) (count))				\
+		 : "r" ((USItype) (x)));					\
+	(count) += 16;							\
+      }									\
+  } while (0)
+#endif
+
+#if defined (__sh2__) && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ (								\
+       "dmulu.l	%2,%3\n\tsts	macl,%1\n\tsts	mach,%0"		\
+	   : "=r" ((USItype)(w1)),					\
+	     "=r" ((USItype)(w0))					\
+	   : "r" ((USItype)(u)),					\
+	     "r" ((USItype)(v))						\
+	   : "macl", "mach")
+#define UMUL_TIME 5
+#endif
+
+#if defined (__SH5__) && __SHMEDIA__ && W_TYPE_SIZE == 32
+#define __umulsidi3(u,v) ((UDItype)(USItype)u*(USItype)v)
+#define count_leading_zeros(count, x) \
+  do									\
+    {									\
+      UDItype x_ = (USItype)(x);					\
+      SItype c_;							\
+									\
+      __asm__ ("nsb %1, %0" : "=r" (c_) : "r" (x_));			\
+      (count) = c_ - 31;						\
+    }									\
+  while (0)
+#define COUNT_LEADING_ZEROS_0 32
+#endif
+
+#if defined (__sparc__) && !defined (__arch64__) && !defined (__sparcv9) \
+    && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("addcc %r4,%5,%1\n\taddx %r2,%3,%0"				\
+	   : "=r" ((USItype) (sh)),					\
+	     "=&r" ((USItype) (sl))					\
+	   : "%rJ" ((USItype) (ah)),					\
+	     "rI" ((USItype) (bh)),					\
+	     "%rJ" ((USItype) (al)),					\
+	     "rI" ((USItype) (bl))					\
+	   __CLOBBER_CC)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("subcc %r4,%5,%1\n\tsubx %r2,%3,%0"				\
+	   : "=r" ((USItype) (sh)),					\
+	     "=&r" ((USItype) (sl))					\
+	   : "rJ" ((USItype) (ah)),					\
+	     "rI" ((USItype) (bh)),					\
+	     "rJ" ((USItype) (al)),					\
+	     "rI" ((USItype) (bl))					\
+	   __CLOBBER_CC)
+#if defined (__sparc_v8__)
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("umul %2,%3,%1;rd %%y,%0"					\
+	   : "=r" ((USItype) (w1)),					\
+	     "=r" ((USItype) (w0))					\
+	   : "r" ((USItype) (u)),					\
+	     "r" ((USItype) (v)))
+#define udiv_qrnnd(__q, __r, __n1, __n0, __d) \
+  __asm__ ("mov %2,%%y;nop;nop;nop;udiv %3,%4,%0;umul %0,%4,%1;sub %3,%1,%1"\
+	   : "=&r" ((USItype) (__q)),					\
+	     "=&r" ((USItype) (__r))					\
+	   : "r" ((USItype) (__n1)),					\
+	     "r" ((USItype) (__n0)),					\
+	     "r" ((USItype) (__d)))
+#else
+#if defined (__sparclite__)
+/* This has hardware multiply but not divide.  It also has two additional
+   instructions scan (ffs from high bit) and divscc.  */
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("umul %2,%3,%1;rd %%y,%0"					\
+	   : "=r" ((USItype) (w1)),					\
+	     "=r" ((USItype) (w0))					\
+	   : "r" ((USItype) (u)),					\
+	     "r" ((USItype) (v)))
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  __asm__ ("! Inlined udiv_qrnnd\n"					\
+"	wr	%%g0,%2,%%y	! Not a delayed write for sparclite\n"	\
+"	tst	%%g0\n"							\
+"	divscc	%3,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%%g1\n"						\
+"	divscc	%%g1,%4,%0\n"						\
+"	rd	%%y,%1\n"						\
+"	bl,a 1f\n"							\
+"	add	%1,%4,%1\n"						\
+"1:	! End of inline udiv_qrnnd"					\
+	   : "=r" ((USItype) (q)),					\
+	     "=r" ((USItype) (r))					\
+	   : "r" ((USItype) (n1)),					\
+	     "r" ((USItype) (n0)),					\
+	     "rI" ((USItype) (d))					\
+	   : "g1" __AND_CLOBBER_CC)
+#define UDIV_TIME 37
+#define count_leading_zeros(count, x) \
+  do {                                                                  \
+  __asm__ ("scan %1,1,%0"                                               \
+           : "=r" ((USItype) (count))                                   \
+           : "r" ((USItype) (x)));					\
+  } while (0)
+/* Early sparclites return 63 for an argument of 0, but they warn that future
+   implementations might change this.  Therefore, leave COUNT_LEADING_ZEROS_0
+   undefined.  */
+#else
+/* SPARC without integer multiplication and divide instructions.
+   (i.e. at least Sun4/20,40,60,65,75,110,260,280,330,360,380,470,490) */
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("! Inlined umul_ppmm\n"					\
+"	wr	%%g0,%2,%%y	! SPARC has 0-3 delay insn after a wr\n"\
+"	sra	%3,31,%%o5	! Don't move this insn\n"		\
+"	and	%2,%%o5,%%o5	! Don't move this insn\n"		\
+"	andcc	%%g0,0,%%g1	! Don't move this insn\n"		\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,%3,%%g1\n"						\
+"	mulscc	%%g1,0,%%g1\n"						\
+"	add	%%g1,%%o5,%0\n"						\
+"	rd	%%y,%1"							\
+	   : "=r" ((USItype) (w1)),					\
+	     "=r" ((USItype) (w0))					\
+	   : "%rI" ((USItype) (u)),					\
+	     "r" ((USItype) (v))						\
+	   : "g1", "o5" __AND_CLOBBER_CC)
+#define UMUL_TIME 39		/* 39 instructions */
+/* It's quite necessary to add this much assembler for the sparc.
+   The default udiv_qrnnd (in C) is more than 10 times slower!  */
+#define udiv_qrnnd(__q, __r, __n1, __n0, __d) \
+  __asm__ ("! Inlined udiv_qrnnd\n"					\
+"	mov	32,%%g1\n"						\
+"	subcc	%1,%2,%%g0\n"						\
+"1:	bcs	5f\n"							\
+"	 addxcc %0,%0,%0	! shift n1n0 and a q-bit in lsb\n"	\
+"	sub	%1,%2,%1	! this kills msb of n\n"		\
+"	addx	%1,%1,%1	! so this can't give carry\n"		\
+"	subcc	%%g1,1,%%g1\n"						\
+"2:	bne	1b\n"							\
+"	 subcc	%1,%2,%%g0\n"						\
+"	bcs	3f\n"							\
+"	 addxcc %0,%0,%0	! shift n1n0 and a q-bit in lsb\n"	\
+"	b	3f\n"							\
+"	 sub	%1,%2,%1	! this kills msb of n\n"		\
+"4:	sub	%1,%2,%1\n"						\
+"5:	addxcc	%1,%1,%1\n"						\
+"	bcc	2b\n"							\
+"	 subcc	%%g1,1,%%g1\n"						\
+"! Got carry from n.  Subtract next step to cancel this carry.\n"	\
+"	bne	4b\n"							\
+"	 addcc	%0,%0,%0	! shift n1n0 and a 0-bit in lsb\n"	\
+"	sub	%1,%2,%1\n"						\
+"3:	xnor	%0,0,%0\n"						\
+"	! End of inline udiv_qrnnd"					\
+	   : "=&r" ((USItype) (__q)),					\
+	     "=&r" ((USItype) (__r))					\
+	   : "r" ((USItype) (__d)),					\
+	     "1" ((USItype) (__n1)),					\
+	     "0" ((USItype) (__n0)) : "g1" __AND_CLOBBER_CC)
+#define UDIV_TIME (3+7*32)	/* 7 instructions/iteration. 32 iterations.  */
+#endif /* __sparclite__ */
+#endif /* __sparc_v8__ */
+#endif /* sparc32 */
+
+#if ((defined (__sparc__) && defined (__arch64__)) || defined (__sparcv9)) \
+    && W_TYPE_SIZE == 64
+#define add_ssaaaa(sh, sl, ah, al, bh, bl)				\
+  __asm__ ("addcc %r4,%5,%1\n\t"					\
+	   "add %r2,%3,%0\n\t"						\
+	   "bcs,a,pn %%xcc, 1f\n\t"					\
+	   "add %0, 1, %0\n"						\
+	   "1:"								\
+	   : "=r" ((UDItype)(sh)),					\
+	     "=&r" ((UDItype)(sl))					\
+	   : "%rJ" ((UDItype)(ah)),					\
+	     "rI" ((UDItype)(bh)),					\
+	     "%rJ" ((UDItype)(al)),					\
+	     "rI" ((UDItype)(bl))					\
+	   __CLOBBER_CC)
+
+#define sub_ddmmss(sh, sl, ah, al, bh, bl)				\
+  __asm__ ("subcc %r4,%5,%1\n\t"					\
+	   "sub %r2,%3,%0\n\t"						\
+	   "bcs,a,pn %%xcc, 1f\n\t"					\
+	   "sub %0, 1, %0\n\t"						\
+	   "1:"								\
+	   : "=r" ((UDItype)(sh)),					\
+	     "=&r" ((UDItype)(sl))					\
+	   : "rJ" ((UDItype)(ah)),					\
+	     "rI" ((UDItype)(bh)),					\
+	     "rJ" ((UDItype)(al)),					\
+	     "rI" ((UDItype)(bl))					\
+	   __CLOBBER_CC)
+
+#define umul_ppmm(wh, wl, u, v)						\
+  do {									\
+	  UDItype tmp1, tmp2, tmp3, tmp4;				\
+	  __asm__ __volatile__ (					\
+		   "srl %7,0,%3\n\t"					\
+		   "mulx %3,%6,%1\n\t"					\
+		   "srlx %6,32,%2\n\t"					\
+		   "mulx %2,%3,%4\n\t"					\
+		   "sllx %4,32,%5\n\t"					\
+		   "srl %6,0,%3\n\t"					\
+		   "sub %1,%5,%5\n\t"					\
+		   "srlx %5,32,%5\n\t"					\
+		   "addcc %4,%5,%4\n\t"					\
+		   "srlx %7,32,%5\n\t"					\
+		   "mulx %3,%5,%3\n\t"					\
+		   "mulx %2,%5,%5\n\t"					\
+		   "sethi %%hi(0x80000000),%2\n\t"			\
+		   "addcc %4,%3,%4\n\t"					\
+		   "srlx %4,32,%4\n\t"					\
+		   "add %2,%2,%2\n\t"					\
+		   "movcc %%xcc,%%g0,%2\n\t"				\
+		   "addcc %5,%4,%5\n\t"					\
+		   "sllx %3,32,%3\n\t"					\
+		   "add %1,%3,%1\n\t"					\
+		   "add %5,%2,%0"					\
+	   : "=r" ((UDItype)(wh)),					\
+	     "=&r" ((UDItype)(wl)),					\
+	     "=&r" (tmp1), "=&r" (tmp2), "=&r" (tmp3), "=&r" (tmp4)	\
+	   : "r" ((UDItype)(u)),					\
+	     "r" ((UDItype)(v))						\
+	   __CLOBBER_CC);						\
+  } while (0)
+#define UMUL_TIME 96
+#define UDIV_TIME 230
+#endif /* sparc64 */
+
+#if defined (__vax__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("addl2 %5,%1\n\tadwc %3,%0"					\
+	   : "=g" ((USItype) (sh)),					\
+	     "=&g" ((USItype) (sl))					\
+	   : "%0" ((USItype) (ah)),					\
+	     "g" ((USItype) (bh)),					\
+	     "%1" ((USItype) (al)),					\
+	     "g" ((USItype) (bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("subl2 %5,%1\n\tsbwc %3,%0"					\
+	   : "=g" ((USItype) (sh)),					\
+	     "=&g" ((USItype) (sl))					\
+	   : "0" ((USItype) (ah)),					\
+	     "g" ((USItype) (bh)),					\
+	     "1" ((USItype) (al)),					\
+	     "g" ((USItype) (bl)))
+#define umul_ppmm(xh, xl, m0, m1) \
+  do {									\
+    union {								\
+	UDItype __ll;							\
+	struct {USItype __l, __h;} __i;					\
+      } __xx;								\
+    USItype __m0 = (m0), __m1 = (m1);					\
+    __asm__ ("emul %1,%2,$0,%0"						\
+	     : "=r" (__xx.__ll)						\
+	     : "g" (__m0),						\
+	       "g" (__m1));						\
+    (xh) = __xx.__i.__h;						\
+    (xl) = __xx.__i.__l;						\
+    (xh) += ((((SItype) __m0 >> 31) & __m1)				\
+	     + (((SItype) __m1 >> 31) & __m0));				\
+  } while (0)
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+  do {									\
+    union {DItype __ll;							\
+	   struct {SItype __l, __h;} __i;				\
+	  } __xx;							\
+    __xx.__i.__h = n1; __xx.__i.__l = n0;				\
+    __asm__ ("ediv %3,%2,%0,%1"						\
+	     : "=g" (q), "=g" (r)					\
+	     : "g" (__xx.__ll), "g" (d));				\
+  } while (0)
+#endif /* __vax__ */
+
+#if defined (__z8000__) && W_TYPE_SIZE == 16
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("add	%H1,%H5\n\tadc	%H0,%H3"				\
+	   : "=r" ((unsigned int)(sh)),					\
+	     "=&r" ((unsigned int)(sl))					\
+	   : "%0" ((unsigned int)(ah)),					\
+	     "r" ((unsigned int)(bh)),					\
+	     "%1" ((unsigned int)(al)),					\
+	     "rQR" ((unsigned int)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("sub	%H1,%H5\n\tsbc	%H0,%H3"				\
+	   : "=r" ((unsigned int)(sh)),					\
+	     "=&r" ((unsigned int)(sl))					\
+	   : "0" ((unsigned int)(ah)),					\
+	     "r" ((unsigned int)(bh)),					\
+	     "1" ((unsigned int)(al)),					\
+	     "rQR" ((unsigned int)(bl)))
+#define umul_ppmm(xh, xl, m0, m1) \
+  do {									\
+    union {long int __ll;						\
+	   struct {unsigned int __h, __l;} __i;				\
+	  } __xx;							\
+    unsigned int __m0 = (m0), __m1 = (m1);				\
+    __asm__ ("mult	%S0,%H3"					\
+	     : "=r" (__xx.__i.__h),					\
+	       "=r" (__xx.__i.__l)					\
+	     : "%1" (__m0),						\
+	       "rQR" (__m1));						\
+    (xh) = __xx.__i.__h; (xl) = __xx.__i.__l;				\
+    (xh) += ((((signed int) __m0 >> 15) & __m1)				\
+	     + (((signed int) __m1 >> 15) & __m0));			\
+  } while (0)
+#endif /* __z8000__ */
+
+#endif /* __GNUC__ */
+
+/* If this machine has no inline assembler, use C macros.  */
+
+#if !defined (add_ssaaaa)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  do {									\
+    UWtype __x;								\
+    __x = (al) + (bl);							\
+    (sh) = (ah) + (bh) + (__x < (al));					\
+    (sl) = __x;								\
+  } while (0)
+#endif
+
+#if !defined (sub_ddmmss)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  do {									\
+    UWtype __x;								\
+    __x = (al) - (bl);							\
+    (sh) = (ah) - (bh) - (__x > (al));					\
+    (sl) = __x;								\
+  } while (0)
+#endif
+
+#if !defined (umul_ppmm)
+#define umul_ppmm(w1, w0, u, v)						\
+  do {									\
+    UWtype __x0, __x1, __x2, __x3;					\
+    UHWtype __ul, __vl, __uh, __vh;					\
+									\
+    __ul = __ll_lowpart (u);						\
+    __uh = __ll_highpart (u);						\
+    __vl = __ll_lowpart (v);						\
+    __vh = __ll_highpart (v);						\
+									\
+    __x0 = (UWtype) __ul * __vl;					\
+    __x1 = (UWtype) __ul * __vh;					\
+    __x2 = (UWtype) __uh * __vl;					\
+    __x3 = (UWtype) __uh * __vh;					\
+									\
+    __x1 += __ll_highpart (__x0);/* this can't give carry */		\
+    __x1 += __x2;		/* but this indeed can */		\
+    if (__x1 < __x2)		/* did we get it? */			\
+      __x3 += __ll_B;		/* yes, add it in the proper pos.  */	\
+									\
+    (w1) = __x3 + __ll_highpart (__x1);					\
+    (w0) = __ll_lowpart (__x1) * __ll_B + __ll_lowpart (__x0);		\
+  } while (0)
+#endif
+
+#if !defined (__umulsidi3)
+#define __umulsidi3(u, v) \
+  ({DWunion __w;							\
+    umul_ppmm (__w.s.high, __w.s.low, u, v);				\
+    __w.ll; })
+#endif
+
+/* Define this unconditionally, so it can be used for debugging.  */
+#define __udiv_qrnnd_c(q, r, n1, n0, d) \
+  do {									\
+    UWtype __d1, __d0, __q1, __q0;					\
+    UWtype __r1, __r0, __m;						\
+    __d1 = __ll_highpart (d);						\
+    __d0 = __ll_lowpart (d);						\
+									\
+    __r1 = (n1) % __d1;							\
+    __q1 = (n1) / __d1;							\
+    __m = (UWtype) __q1 * __d0;						\
+    __r1 = __r1 * __ll_B | __ll_highpart (n0);				\
+    if (__r1 < __m)							\
+      {									\
+	__q1--, __r1 += (d);						\
+	if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\
+	  if (__r1 < __m)						\
+	    __q1--, __r1 += (d);					\
+      }									\
+    __r1 -= __m;							\
+									\
+    __r0 = __r1 % __d1;							\
+    __q0 = __r1 / __d1;							\
+    __m = (UWtype) __q0 * __d0;						\
+    __r0 = __r0 * __ll_B | __ll_lowpart (n0);				\
+    if (__r0 < __m)							\
+      {									\
+	__q0--, __r0 += (d);						\
+	if (__r0 >= (d))						\
+	  if (__r0 < __m)						\
+	    __q0--, __r0 += (d);					\
+      }									\
+    __r0 -= __m;							\
+									\
+    (q) = (UWtype) __q1 * __ll_B | __q0;				\
+    (r) = __r0;								\
+  } while (0)
+
+/* If the processor has no udiv_qrnnd but sdiv_qrnnd, go through
+   __udiv_w_sdiv (defined in libgcc or elsewhere).  */
+#if !defined (udiv_qrnnd) && defined (sdiv_qrnnd)
+#define udiv_qrnnd(q, r, nh, nl, d) \
+  do {									\
+    USItype __r;							\
+    (q) = __udiv_w_sdiv (&__r, nh, nl, d);				\
+    (r) = __r;								\
+  } while (0)
+#endif
+
+/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c.  */
+#if !defined (udiv_qrnnd)
+#define UDIV_NEEDS_NORMALIZATION 1
+#define udiv_qrnnd __udiv_qrnnd_c
+#endif
+
+#if !defined (count_leading_zeros)
+extern const UQItype __clz_tab[];
+#define count_leading_zeros(count, x) \
+  do {									\
+    UWtype __xr = (x);							\
+    UWtype __a;								\
+									\
+    if (W_TYPE_SIZE <= 32)						\
+      {									\
+	__a = __xr < ((UWtype)1<<2*__BITS4)				\
+	  ? (__xr < ((UWtype)1<<__BITS4) ? 0 : __BITS4)			\
+	  : (__xr < ((UWtype)1<<3*__BITS4) ?  2*__BITS4 : 3*__BITS4);	\
+      }									\
+    else								\
+      {									\
+	for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8)			\
+	  if (((__xr >> __a) & 0xff) != 0)				\
+	    break;							\
+      }									\
+									\
+    (count) = W_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a);		\
+  } while (0)
+#define COUNT_LEADING_ZEROS_0 W_TYPE_SIZE
+#endif
+
+#if !defined (count_trailing_zeros)
+/* Define count_trailing_zeros using count_leading_zeros.  The latter might be
+   defined in asm, but if it is not, the C version above is good enough.  */
+#define count_trailing_zeros(count, x) \
+  do {									\
+    UWtype __ctz_x = (x);						\
+    UWtype __ctz_c;							\
+    count_leading_zeros (__ctz_c, __ctz_x & -__ctz_x);			\
+    (count) = W_TYPE_SIZE - 1 - __ctz_c;				\
+  } while (0)
+#endif
+
+#ifndef UDIV_NEEDS_NORMALIZATION
+#define UDIV_NEEDS_NORMALIZATION 0
+#endif
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/Makefile linux-5.6.11-ndis/3rdparty/ndiswrapper/Makefile
--- linux-5.6.11/3rdparty/ndiswrapper/Makefile	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/Makefile	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,213 @@
+# Name of the module
+MODNAME = ndiswrapper
+
+DISTFILES = \
+	Makefile crt.c divdi3.c hal.c iw_ndis.c iw_ndis.h lin2win.S lin2win.h \
+	loader.c loader.h longlong.h mkexport.sh mkstubs.sh ndis.c ndis.h \
+	ndiswrapper.h ntoskernel.c ntoskernel.h ntoskernel_io.c pe_linker.c \
+	pe_linker.h pnp.c pnp.h proc.c rtl.c usb.c usb.h win2lin_stubs.S \
+	winnt_types.h workqueue.c wrapmem.c wrapmem.h wrapndis.c wrapndis.h \
+	wrapper.c wrapper.h
+
+# By default, we try to compile the modules for the currently running
+# kernel.  But it's the first approximation, as we will re-read the
+# version from the kernel sources.
+KVERS_UNAME ?= $(shell uname -r)
+
+# KBUILD is the path to the Linux kernel build tree.  It is usually the
+# same as the kernel source tree, except when the kernel was compiled in
+# a separate directory.
+KBUILD ?= $(shell readlink -f /lib/modules/$(KVERS_UNAME)/build)
+
+ifeq (,$(KBUILD))
+$(error Kernel build tree not found - please set KBUILD to configured kernel)
+endif
+
+KCONFIG := $(KBUILD)/.config
+ifeq (,$(wildcard $(KCONFIG)))
+$(error No .config found in $(KBUILD), please set KBUILD to configured kernel)
+endif
+
+ifneq (,$(wildcard $(KBUILD)/include/linux/version.h))
+ifneq (,$(wildcard $(KBUILD)/include/generated/uapi/linux/version.h))
+$(error Multiple copies of version.h found, please clean your build tree)
+endif
+endif
+
+# Kernel Makefile doesn't always know the exact kernel version, so we
+# get it from the kernel headers instead and pass it to make.
+VERSION_H := $(KBUILD)/include/generated/utsrelease.h
+ifeq (,$(wildcard $(VERSION_H)))
+VERSION_H := $(KBUILD)/include/linux/utsrelease.h
+endif
+ifeq (,$(wildcard $(VERSION_H)))
+VERSION_H := $(KBUILD)/include/linux/version.h
+endif
+ifeq (,$(wildcard $(VERSION_H)))
+$(error Please run 'make modules_prepare' in $(KBUILD))
+endif
+
+KVERS := $(shell sed -ne 's/"//g;s/^\#define UTS_RELEASE //p' $(VERSION_H))
+
+ifeq (,$(KVERS))
+$(error Cannot find UTS_RELEASE in $(VERSION_H), please report)
+endif
+
+INST_DIR = /lib/modules/$(KVERS)/misc
+
+SRC_DIR=$(shell pwd)
+
+include $(KCONFIG)
+
+# returns of structs and unions in registers when possible, like Windows
+EXTRA_CFLAGS += -freg-struct-return
+
+# to produce debug trace, add option "DEBUG=<n>" where <n> is 1 to 6
+ifdef DEBUG
+EXTRA_CFLAGS += -DDEBUG=$(DEBUG) -g
+endif
+
+# to debug timers, add option "TIMER_DEBUG=1"
+ifdef TIMER_DEBUG
+EXTRA_CFLAGS += -DTIMER_DEBUG
+endif
+
+# to debug event layer, add option "EVENT_DEBUG=1"
+ifdef EVENT_DEBUG
+EXTRA_CFLAGS += -DEVENT_DEBUG
+endif
+
+# to debug USB layer, add option "USB_DEBUG=1"
+ifdef USB_DEBUG
+EXTRA_CFLAGS += -DUSB_DEBUG
+endif
+
+# to debug I/O layer, add option "IO_DEBUG=1"
+ifdef IO_DEBUG
+EXTRA_CFLAGS += -DIO_DEBUG
+endif
+
+# to debug worker threads, add option "WORK_DEBUG=1"
+ifdef WORK_DEBUG
+EXTRA_CFLAGS += -DWORK_DEBUG
+endif
+
+# to debug memory allocation, add option "ALLOC_DEBUG=<n>" where <n> is 1 or 2
+ifdef ALLOC_DEBUG
+EXTRA_CFLAGS += -DALLOC_DEBUG=$(ALLOC_DEBUG)
+endif
+
+OBJS = crt.o hal.o iw_ndis.o loader.o ndis.o ntoskernel.o ntoskernel_io.o \
+	pe_linker.o pnp.o proc.o rtl.o wrapmem.o wrapndis.o wrapper.o
+
+EXPORT_SRCS = crt.c hal.c ndis.c ntoskernel.c ntoskernel_io.c rtl.c
+
+STUB_SRCS = crt.c hal.c ndis.c ntoskernel.c ntoskernel_io.c \
+	pnp.c rtl.c wrapndis.c
+
+
+# By default, USB layer is compiled in if USB support is in kernel;
+# to disable USB support in ndiswrapper even if USB support is in kernel,
+# add option "DISABLE_USB=1"
+ifndef DISABLE_USB
+ifeq ($(CONFIG_USB),y)
+ENABLE_USB = 1
+endif
+ifeq ($(CONFIG_USB),m)
+ENABLE_USB = 1
+endif
+endif
+
+ifdef ENABLE_USB
+EXPORT_SRCS += usb.c
+STUB_SRCS += usb.c
+OBJS += usb.o
+EXTRA_CFLAGS += -DENABLE_USB
+endif
+
+ifdef WRAP_WQ
+EXTRA_CFLAGS += -DWRAP_WQ
+OBJS += workqueue.o
+endif
+
+
+all: config_check modules
+
+# generate exports symbol table from C files
+quiet_cmd_mkexport = MKEXPORT $@
+cmd_mkexport = $(SHELL) $(obj)/mkexport.sh $< $@
+
+extra-y += $(EXPORT_SRCS:.c=_exports.h)
+%_exports.h: %.c $(obj)/mkexport.sh FORCE
+	$(call if_changed,mkexport)
+
+$(addprefix $(obj)/,$(EXPORT_SRCS:.c=.o)): %.o: %_exports.h
+
+ifeq ($(CONFIG_X86_64),y)
+quiet_cmd_mkstubs = MKSTUBS $@
+cmd_mkstubs = $(SHELL) $(obj)/mkstubs.sh $(addprefix $(src)/,$(STUB_SRCS)) >$@
+
+extra-y += win2lin_stubs.h
+$(obj)/win2lin_stubs.h: $(addprefix $(src)/,$(STUB_SRCS)) FORCE
+	$(call if_changed,mkstubs)
+
+$(obj)/win2lin_stubs.o: $(obj)/win2lin_stubs.h
+OBJS += win2lin_stubs.o lin2win.o
+else
+OBJS += divdi3.o
+endif
+
+MODULE := $(MODNAME).ko
+obj-m := $(MODNAME).o
+
+$(MODNAME)-objs := $(OBJS)
+
+
+config_check:
+	@if [ -z "$(CONFIG_WIRELESS_EXT)$(CONFIG_NET_RADIO)" ]; then \
+		echo; echo; \
+		echo "*** WARNING: This kernel lacks wireless extensions."; \
+		echo "Wireless drivers will not work properly."; \
+		echo; echo; \
+	fi
+	@if [ -z "$(CONFIG_X86_64)" ] && [ -n "$(CONFIG_4KSTACKS)" ]; then \
+		echo; echo; \
+		echo "*** WARNING: This kernel uses 4K stack size option"; \
+		echo "(CONFIG_4KSTACKS); many Windows drivers will not work"; \
+		echo "with this option enabled. Disable CONFIG_4KSTACKS"; \
+		echo "in kernel's .config file, recompile and install kernel"; \
+		echo; echo; \
+	fi
+
+modules:
+	$(MAKE) -C $(KBUILD) M=$(SRC_DIR)
+
+$(MODULE):
+	$(MAKE) modules
+
+clean:
+	rm -f *.o *.ko .*.cmd *.mod.c *.symvers modules.order *~ .\#*
+	rm -f *_exports.h win2lin_stubs.h
+	rm -rf .tmp_versions
+
+install: config_check $(MODULE)
+	@/sbin/modinfo $(MODULE) | grep -q "^vermagic: *$(KVERS) " || \
+		{ echo "$(MODULE)" is not for Linux $(KVERS); exit 1; }
+	mkdir -p -m 755 $(DESTDIR)$(INST_DIR)
+	install -m 0644 $(MODULE) $(DESTDIR)$(INST_DIR)
+ifndef DESTDIR
+	-/sbin/depmod -a $(KVERS)
+endif
+
+uninstall:
+	rm -f $(DESTDIR)$(INST_DIR)/$(MODULE)
+ifndef DESTDIR
+	-/sbin/depmod -a $(KVERS)
+endif
+
+dist:
+	@for file in $(DISTFILES); do \
+		cp $$file $(distdir)/$$file || exit 1; \
+	done
+
+.PHONY: all modules clean install config_check dist
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/mkexport.sh linux-5.6.11-ndis/3rdparty/ndiswrapper/mkexport.sh
--- linux-5.6.11/3rdparty/ndiswrapper/mkexport.sh	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/mkexport.sh	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,42 @@
+#! /bin/sh
+
+# Generate exports symbol table from C files
+
+input="$1"
+output="$2"
+exports=$(basename "$output" .h)
+exec >"$output"
+
+echo "/* automatically generated from src */";
+
+sed -n -e '/^\(wstdcall\|wfastcall\|noregparm\|regparm3\|__attribute__\)/{
+:more
+N
+s/\([^{]\)$/\1/
+t more
+s/\n{$/;/
+p
+}' $input
+
+echo "#ifdef CONFIG_X86_64";
+
+sed -n \
+	-e 's/.*WIN_FUNC(\([^\,]\+\) *\, *\([0-9]\+\)).*/'\
+'WIN_FUNC_DECL(\1, \2)/p' \
+	-e 's/.*WIN_FUNC_PTR(\([^\,]\+\) *\, *\([0-9]\+\)).*/'\
+'WIN_FUNC_DECL(\1, \2)/p' $input | sort -u
+
+echo "#endif"
+echo "extern struct wrap_export $exports[];"
+echo "struct wrap_export $exports[] = {"
+
+sed -n \
+	-e 's/.*WIN_FUNC(_win_\([^\,]\+\) *\, *\([0-9]\+\)).*/'\
+'	WIN_WIN_SYMBOL(\1, \2),/p' \
+	-e 's/.*WIN_FUNC(\([^\,]\+\) *\, *\([0-9]\+\)).*/'\
+'	WIN_SYMBOL(\1, \2),/p' \
+	-e 's/.*WIN_SYMBOL_MAP(\("[^"]\+"\)[ ,\n]\+\([^)]\+\)).*/'\
+'	{\1, (generic_func)\2},/p' $input | sort -u
+
+echo "	{NULL, NULL}"
+echo "};"
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/mkstubs.sh linux-5.6.11-ndis/3rdparty/ndiswrapper/mkstubs.sh
--- linux-5.6.11/3rdparty/ndiswrapper/mkstubs.sh	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/mkstubs.sh	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,12 @@
+#! /bin/sh
+
+for file in "$@"; do
+	echo
+	echo "# automatically generated from $file"
+	sed -n \
+		-e 's/.*WIN_FUNC(\([^\,]\+\) *\, *\([0-9]\+\)).*/\
+		   win2lin(\1, \2)/p'   \
+		-e 's/.*WIN_FUNC_PTR(\([^\,]\+\) *\, *\([0-9]\+\)).*/\
+		   win2lin(\1, \2)/p'   \
+	   $file | sed -e 's/[ \t	]\+//' | sort -u; \
+done
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/ndis.c linux-5.6.11-ndis/3rdparty/ndiswrapper/ndis.c
--- linux-5.6.11/3rdparty/ndiswrapper/ndis.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/ndis.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,3031 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include "ndis.h"
+#include "iw_ndis.h"
+#include "wrapndis.h"
+#include "pnp.h"
+#include "loader.h"
+#include <linux/kernel_stat.h>
+#include <asm/dma.h>
+#include "ndis_exports.h"
+
+#define MAX_ALLOCATED_NDIS_PACKETS TX_RING_SIZE
+#define MAX_ALLOCATED_NDIS_BUFFERS TX_RING_SIZE
+
+static struct work_struct ndis_work;
+static struct nt_list ndis_work_list;
+static spinlock_t ndis_work_list_lock;
+
+struct workqueue_struct *ndis_wq;
+
+static void *ndis_get_routine_address(char *name);
+
+wstdcall void WIN_FUNC(NdisInitializeWrapper,4)
+	(void **driver_handle, struct driver_object *driver,
+	 struct unicode_string *reg_path, void *unused)
+{
+	ENTER1("handle: %p, driver: %p", driver_handle, driver);
+	*driver_handle = driver;
+	EXIT1(return);
+}
+
+wstdcall void WIN_FUNC(NdisTerminateWrapper,2)
+	(struct device_object *dev_obj, void *system_specific)
+{
+	EXIT1(return);
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMRegisterMiniport,3)
+	(struct driver_object *drv_obj, struct miniport *mp, UINT length)
+{
+	int min_length;
+	struct wrap_driver *wrap_driver;
+	struct ndis_driver *ndis_driver;
+
+	min_length = ((char *)&mp->co_create_vc) - ((char *)mp);
+
+	ENTER1("%p %p %d", drv_obj, mp, length);
+
+	if (mp->major_version < 4) {
+		ERROR("Driver is using ndis version %d which is too old.",
+		      mp->major_version);
+		EXIT1(return NDIS_STATUS_BAD_VERSION);
+	}
+
+	if (length < min_length) {
+		ERROR("Characteristics length %d is too small", length);
+		EXIT1(return NDIS_STATUS_BAD_CHARACTERISTICS);
+	}
+
+	TRACE1("%d.%d, %d, %u", mp->major_version, mp->minor_version, length,
+	       (u32)sizeof(struct miniport));
+	wrap_driver = IoGetDriverObjectExtension(drv_obj,
+						 (void *)WRAP_DRIVER_CLIENT_ID);
+	if (!wrap_driver) {
+		ERROR("couldn't get wrap_driver");
+		EXIT1(return NDIS_STATUS_RESOURCES);
+	}
+	if (IoAllocateDriverObjectExtension(
+		    drv_obj, (void *)NDIS_DRIVER_CLIENT_ID,
+		    sizeof(*ndis_driver), (void **)&ndis_driver) !=
+	    STATUS_SUCCESS)
+		EXIT1(return NDIS_STATUS_RESOURCES);
+	wrap_driver->ndis_driver = ndis_driver;
+	TRACE1("driver: %p", ndis_driver);
+	memcpy(&ndis_driver->mp, mp, min_t(int, sizeof(*mp), length));
+
+	DBG_BLOCK(2) {
+		int i;
+		void **func;
+		char *mp_funcs[] = {
+			"queryinfo", "reconfig", "reset", "send", "setinfo",
+			"tx_data", "return_packet", "send_packets",
+			"alloc_complete", "co_create_vc", "co_delete_vc",
+			"co_activate_vc", "co_deactivate_vc",
+			"co_send_packets", "co_request", "cancel_send_packets",
+			"pnp_event_notify", "shutdown",
+		};
+		func = (void **)&ndis_driver->mp.queryinfo;
+		for (i = 0; i < ARRAY_SIZE(mp_funcs); i++)
+			TRACE0("function '%s' is at %p", mp_funcs[i], func[i]);
+	}
+	EXIT1(return NDIS_STATUS_SUCCESS);
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMRegisterDevice,6)
+	(struct driver_object *drv_obj, struct unicode_string *dev_name,
+	 struct unicode_string *link, void **funcs,
+	 struct device_object **dev_obj, void **dev_obj_handle)
+{
+	NTSTATUS status;
+	struct device_object *tmp;
+	int i;
+
+	ENTER1("%p, %p, %p", drv_obj, dev_name, link);
+	status = IoCreateDevice(drv_obj, 0, dev_name, FILE_DEVICE_NETWORK, 0,
+				FALSE, &tmp);
+
+	if (status != STATUS_SUCCESS)
+		EXIT1(return NDIS_STATUS_RESOURCES);
+	if (link)
+		status = IoCreateSymbolicLink(link, dev_name);
+	if (status != STATUS_SUCCESS) {
+		IoDeleteDevice(tmp);
+		EXIT1(return NDIS_STATUS_RESOURCES);
+	}
+
+	*dev_obj = tmp;
+	*dev_obj_handle = *dev_obj;
+	for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
+		if (funcs[i] && i != IRP_MJ_PNP && i != IRP_MJ_POWER) {
+			drv_obj->major_func[i] = funcs[i];
+			TRACE1("mj_fn for 0x%x is at %p", i, funcs[i]);
+		}
+	EXIT1(return NDIS_STATUS_SUCCESS);
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMDeregisterDevice,1)
+	(struct device_object *dev_obj)
+{
+	ENTER2("%p", dev_obj);
+	IoDeleteDevice(dev_obj);
+	return NDIS_STATUS_SUCCESS;
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisAllocateMemoryWithTag,3)
+	(void **dest, UINT length, ULONG tag)
+{
+	void *addr;
+
+	assert_irql(_irql_ <= DISPATCH_LEVEL);
+	addr = ExAllocatePoolWithTag(NonPagedPool, length, tag);
+	TRACE4("%p", addr);
+	if (addr) {
+		*dest = addr;
+		EXIT4(return NDIS_STATUS_SUCCESS);
+	} else
+		EXIT4(return NDIS_STATUS_FAILURE);
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisAllocateMemory,4)
+	(void **dest, UINT length, UINT flags, NDIS_PHY_ADDRESS highest_address)
+{
+	return NdisAllocateMemoryWithTag(dest, length, 0);
+}
+
+/* length_tag is either length or tag, depending on if
+ * NdisAllocateMemory or NdisAllocateMemoryTag is used to allocate
+ * memory */
+wstdcall void WIN_FUNC(NdisFreeMemory,3)
+	(void *addr, UINT length_tag, UINT flags)
+{
+	TRACE4("%p", addr);
+	ExFreePool(addr);
+}
+
+noregparm void WIN_FUNC(NdisWriteErrorLogEntry,12)
+	(struct driver_object *drv_obj, ULONG error, ULONG count, ...)
+{
+	va_list args;
+	int i;
+	ULONG code;
+
+	va_start(args, count);
+	ERROR("log: %08X, count: %d, return_address: %p",
+	      error, count, __builtin_return_address(0));
+	for (i = 0; i < count; i++) {
+		code = va_arg(args, ULONG);
+		ERROR("code: 0x%x", code);
+	}
+	va_end(args);
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisOpenConfiguration,3)
+	(NDIS_STATUS *status, struct ndis_mp_block **conf_handle,
+	 struct ndis_mp_block *handle)
+{
+	ENTER2("%p", conf_handle);
+	*conf_handle = handle;
+	*status = NDIS_STATUS_SUCCESS;
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisOpenProtocolConfiguration,3)
+	(NDIS_STATUS *status, void **confhandle,
+	 struct unicode_string *section)
+{
+	ENTER2("%p", confhandle);
+	*status = NDIS_STATUS_SUCCESS;
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisOpenConfigurationKeyByName,4)
+	(NDIS_STATUS *status, void *handle,
+	 struct unicode_string *key, void **subkeyhandle)
+{
+	struct ansi_string ansi;
+	ENTER2("");
+	if (RtlUnicodeStringToAnsiString(&ansi, key, TRUE) == STATUS_SUCCESS) {
+		TRACE2("%s", ansi.buf);
+		RtlFreeAnsiString(&ansi);
+	}
+	*subkeyhandle = handle;
+	*status = NDIS_STATUS_SUCCESS;
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisOpenConfigurationKeyByIndex,5)
+	(NDIS_STATUS *status, void *handle, ULONG index,
+	 struct unicode_string *key, void **subkeyhandle)
+{
+	ENTER2("%u", index);
+//	*subkeyhandle = handle;
+	*status = NDIS_STATUS_FAILURE;
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisCloseConfiguration,1)
+	(void *handle)
+{
+	/* instead of freeing all configuration parameters as we are
+	 * supposed to do here, we free them when the device is
+	 * removed */
+	ENTER2("%p", handle);
+	return;
+}
+
+wstdcall void WIN_FUNC(NdisOpenFile,5)
+	(NDIS_STATUS *status, struct wrap_bin_file **file,
+	 UINT *filelength, struct unicode_string *filename,
+	 NDIS_PHY_ADDRESS highest_address)
+{
+	struct ansi_string ansi;
+	struct wrap_bin_file *bin_file;
+
+	ENTER2("%p, %d, %llx, %p", status, *filelength, highest_address, *file);
+	if (RtlUnicodeStringToAnsiString(&ansi, filename, TRUE) !=
+	    STATUS_SUCCESS) {
+		*status = NDIS_STATUS_RESOURCES;
+		EXIT2(return);
+	}
+	TRACE2("%s", ansi.buf);
+	bin_file = get_bin_file(ansi.buf);
+	if (bin_file) {
+		*file = bin_file;
+		*filelength = bin_file->size;
+		*status = NDIS_STATUS_SUCCESS;
+	} else
+		*status = NDIS_STATUS_FILE_NOT_FOUND;
+
+	RtlFreeAnsiString(&ansi);
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisMapFile,3)
+	(NDIS_STATUS *status, void **mappedbuffer, struct wrap_bin_file *file)
+{
+	ENTER2("%p", file);
+
+	if (!file) {
+		*status = NDIS_STATUS_ALREADY_MAPPED;
+		EXIT2(return);
+	}
+
+	*status = NDIS_STATUS_SUCCESS;
+	*mappedbuffer = file->data;
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisUnmapFile,1)
+	(struct wrap_bin_file *file)
+{
+	ENTER2("%p", file);
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisCloseFile,1)
+	(struct wrap_bin_file *file)
+{
+	ENTER2("%p", file);
+	free_bin_file(file);
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisGetSystemUpTime,1)
+	(ULONG *ms)
+{
+	*ms = 1000 * jiffies / HZ;
+	EXIT5(return);
+}
+
+wstdcall ULONG WIN_FUNC(NDIS_BUFFER_TO_SPAN_PAGES,1)
+	(ndis_buffer *buffer)
+{
+	ULONG n, length;
+
+	if (buffer == NULL)
+		EXIT2(return 0);
+	if (MmGetMdlByteCount(buffer) == 0)
+		EXIT2(return 1);
+
+	length = MmGetMdlByteCount(buffer);
+	n = SPAN_PAGES(MmGetMdlVirtualAddress(buffer), length);
+	TRACE4("%p, %p, %d, %d", buffer->startva, buffer->mappedsystemva,
+	       length, n);
+	EXIT3(return n);
+}
+
+wstdcall void WIN_FUNC(NdisGetBufferPhysicalArraySize,2)
+	(ndis_buffer *buffer, UINT *arraysize)
+{
+	ENTER3("%p", buffer);
+	*arraysize = NDIS_BUFFER_TO_SPAN_PAGES(buffer);
+	EXIT3(return);
+}
+
+static struct ndis_configuration_parameter *
+ndis_encode_setting(struct wrap_device_setting *setting,
+		    enum ndis_parameter_type type)
+{
+	struct ansi_string ansi;
+	struct ndis_configuration_parameter *param;
+
+	param = setting->encoded;
+	if (param) {
+		if (param->type == type)
+			EXIT2(return param);
+		if (param->type == NdisParameterString)
+			RtlFreeUnicodeString(&param->data.string);
+		setting->encoded = NULL;
+	} else
+		param = ExAllocatePoolWithTag(NonPagedPool, sizeof(*param), 0);
+	if (!param) {
+		ERROR("couldn't allocate memory");
+		return NULL;
+	}
+	switch (type) {
+	case NdisParameterInteger:
+		param->data.integer = simple_strtol(setting->value, NULL, 0);
+		TRACE2("0x%x", param->data.integer);
+		break;
+	case NdisParameterHexInteger:
+		param->data.integer = simple_strtol(setting->value, NULL, 16);
+		TRACE2("0x%x", param->data.integer);
+		break;
+	case NdisParameterString:
+		RtlInitAnsiString(&ansi, setting->value);
+		TRACE2("'%s'", ansi.buf);
+		if (RtlAnsiStringToUnicodeString(&param->data.string,
+						 &ansi, TRUE)) {
+			ExFreePool(param);
+			EXIT2(return NULL);
+		}
+		break;
+	case NdisParameterBinary:
+		param->data.integer = simple_strtol(setting->value, NULL, 2);
+		TRACE2("0x%x", param->data.integer);
+		break;
+	default:
+		ERROR("unknown type: %d", type);
+		ExFreePool(param);
+		return NULL;
+	}
+	param->type = type;
+	setting->encoded = param;
+	EXIT2(return param);
+}
+
+static int ndis_decode_setting(struct wrap_device_setting *setting,
+			       struct ndis_configuration_parameter *param)
+{
+	struct ansi_string ansi;
+	struct ndis_configuration_parameter *prev;
+
+	ENTER2("%p, %p", setting, param);
+	prev = setting->encoded;
+	if (prev && prev->type == NdisParameterString) {
+		RtlFreeUnicodeString(&prev->data.string);
+		setting->encoded = NULL;
+	}
+	switch (param->type) {
+	case NdisParameterInteger:
+		snprintf(setting->value, MAX_SETTING_VALUE_LEN, "%u",
+			 param->data.integer);
+		break;
+	case NdisParameterHexInteger:
+		snprintf(setting->value, MAX_SETTING_VALUE_LEN, "%x",
+			 param->data.integer);
+		break;
+	case NdisParameterString:
+		ansi.buf = setting->value;
+		ansi.max_length = MAX_SETTING_VALUE_LEN;
+		if ((RtlUnicodeStringToAnsiString(&ansi, &param->data.string,
+						  FALSE) != STATUS_SUCCESS)
+		    || ansi.length >= MAX_SETTING_VALUE_LEN) {
+			EXIT1(return -1);
+		}
+		if (ansi.length == ansi.max_length)
+			ansi.length--;
+		setting->value[ansi.length] = 0;
+		break;
+	default:
+		TRACE2("unknown setting type: %d", param->type);
+		return -1;
+	}
+	TRACE2("setting changed %s='%s', %d", setting->name, setting->value,
+	       ansi.length);
+	return 0;
+}
+
+static int read_setting(struct nt_list *setting_list, char *keyname, int length,
+			struct ndis_configuration_parameter **param,
+			enum ndis_parameter_type type)
+{
+	struct wrap_device_setting *setting;
+	mutex_lock(&loader_mutex);
+	nt_list_for_each_entry(setting, setting_list, list) {
+		if (strncasecmp(keyname, setting->name, length) == 0) {
+			TRACE2("setting %s='%s'", keyname, setting->value);
+			mutex_unlock(&loader_mutex);
+			*param = ndis_encode_setting(setting, type);
+			if (*param)
+				EXIT2(return 0);
+			else
+				EXIT2(return -1);
+		}
+	}
+	mutex_unlock(&loader_mutex);
+	EXIT2(return -1);
+}
+
+wstdcall void WIN_FUNC(NdisReadConfiguration,5)
+	(NDIS_STATUS *status, struct ndis_configuration_parameter **param,
+	 struct ndis_mp_block *nmb, struct unicode_string *key,
+	 enum ndis_parameter_type type)
+{
+	struct ansi_string ansi;
+	int ret;
+
+	ENTER2("nmb: %p", nmb);
+	ret = RtlUnicodeStringToAnsiString(&ansi, key, TRUE);
+	if (ret != STATUS_SUCCESS || ansi.buf == NULL) {
+		*param = NULL;
+		*status = NDIS_STATUS_FAILURE;
+		RtlFreeAnsiString(&ansi);
+		EXIT2(return);
+	}
+	TRACE2("%d, %s", type, ansi.buf);
+
+	if (read_setting(&nmb->wnd->wd->settings, ansi.buf,
+			 ansi.length, param, type) == 0 ||
+	    read_setting(&nmb->wnd->wd->driver->settings, ansi.buf,
+			 ansi.length, param, type) == 0)
+		*status = NDIS_STATUS_SUCCESS;
+	else {
+		TRACE2("setting %s not found (type:%d)", ansi.buf, type);
+		*status = NDIS_STATUS_FAILURE;
+	}
+	RtlFreeAnsiString(&ansi);
+	EXIT2(return);
+
+}
+
+wstdcall void WIN_FUNC(NdisWriteConfiguration,4)
+	(NDIS_STATUS *status, struct ndis_mp_block *nmb,
+	 struct unicode_string *key, struct ndis_configuration_parameter *param)
+{
+	struct ansi_string ansi;
+	char *keyname;
+	struct wrap_device_setting *setting;
+
+	ENTER2("nmb: %p", nmb);
+	if (RtlUnicodeStringToAnsiString(&ansi, key, TRUE)) {
+		*status = NDIS_STATUS_FAILURE;
+		EXIT2(return);
+	}
+	keyname = ansi.buf;
+	TRACE2("%s", keyname);
+
+	mutex_lock(&loader_mutex);
+	nt_list_for_each_entry(setting, &nmb->wnd->wd->settings, list) {
+		if (strncasecmp(keyname, setting->name, ansi.length) == 0) {
+			mutex_unlock(&loader_mutex);
+			if (ndis_decode_setting(setting, param))
+				*status = NDIS_STATUS_FAILURE;
+			else
+				*status = NDIS_STATUS_SUCCESS;
+			RtlFreeAnsiString(&ansi);
+			EXIT2(return);
+		}
+	}
+	mutex_unlock(&loader_mutex);
+	setting = kzalloc(sizeof(*setting), GFP_KERNEL);
+	if (setting) {
+		if (ansi.length == ansi.max_length)
+			ansi.length--;
+		memcpy(setting->name, keyname, ansi.length);
+		setting->name[ansi.length] = 0;
+		if (ndis_decode_setting(setting, param))
+			*status = NDIS_STATUS_FAILURE;
+		else {
+			*status = NDIS_STATUS_SUCCESS;
+			mutex_lock(&loader_mutex);
+			InsertTailList(&nmb->wnd->wd->settings, &setting->list);
+			mutex_unlock(&loader_mutex);
+		}
+	} else
+		*status = NDIS_STATUS_RESOURCES;
+
+	RtlFreeAnsiString(&ansi);
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisReadNetworkAddress,4)
+	(NDIS_STATUS *status, void **addr, UINT *len,
+	 struct ndis_mp_block *nmb)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	struct ndis_configuration_parameter *param;
+	struct unicode_string key;
+	struct ansi_string ansi;
+	typeof(wnd->mac) mac;
+	int i, ret;
+
+	ENTER2("%p", nmb);
+	RtlInitAnsiString(&ansi, "NetworkAddress");
+	*status = NDIS_STATUS_FAILURE;
+	if (RtlAnsiStringToUnicodeString(&key, &ansi, TRUE) != STATUS_SUCCESS)
+		EXIT1(return);
+
+	NdisReadConfiguration(&ret, &param, nmb, &key, NdisParameterString);
+	RtlFreeUnicodeString(&key);
+	if (ret != NDIS_STATUS_SUCCESS)
+		EXIT1(return);
+	ret = RtlUnicodeStringToAnsiString(&ansi, &param->data.string, TRUE);
+	if (ret != STATUS_SUCCESS)
+		EXIT1(return);
+
+	i = 0;
+	if (ansi.length >= 2 * sizeof(mac)) {
+		for (i = 0; i < sizeof(mac); i++) {
+			char c[3];
+			int x;
+			c[0] = ansi.buf[i*2];
+			c[1] = ansi.buf[i*2+1];
+			c[2] = 0;
+			ret = sscanf(c, "%x", &x);
+			if (ret != 1)
+				break;
+			mac[i] = x;
+		}
+	}
+	TRACE2("%s, %d, " MACSTR, ansi.buf, i, MAC2STR(mac));
+	RtlFreeAnsiString(&ansi);
+	if (i == sizeof(mac)) {
+		memcpy(wnd->mac, mac, sizeof(wnd->mac));
+		*len = sizeof(mac);
+		*addr = wnd->mac;
+		*status = NDIS_STATUS_SUCCESS;
+	}
+	EXIT1(return);
+}
+
+wstdcall void WIN_FUNC(NdisInitializeString,2)
+	(struct unicode_string *dest, UCHAR *src)
+{
+	struct ansi_string ansi;
+
+	ENTER2("");
+	if (src == NULL) {
+		dest->length = dest->max_length = 0;
+		dest->buf = NULL;
+	} else {
+		RtlInitAnsiString(&ansi, src);
+		/* the string is freed with NdisFreeMemory */
+		RtlAnsiStringToUnicodeString(dest, &ansi, TRUE);
+	}
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisInitAnsiString,2)
+	(struct ansi_string *dst, CHAR *src)
+{
+	RtlInitAnsiString(dst, src);
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisInitUnicodeString,2)
+	(struct unicode_string *dest, const wchar_t *src)
+{
+	RtlInitUnicodeString(dest, src);
+	return;
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisAnsiStringToUnicodeString,2)
+	(struct unicode_string *dst, struct ansi_string *src)
+{
+	ENTER2("");
+	if (dst == NULL || src == NULL)
+		EXIT2(return NDIS_STATUS_FAILURE);
+	if (RtlAnsiStringToUnicodeString(dst, src, FALSE) == STATUS_SUCCESS)
+		return NDIS_STATUS_SUCCESS;
+	else
+		return NDIS_STATUS_FAILURE;
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisUnicodeStringToAnsiString,2)
+	(struct ansi_string *dst, struct unicode_string *src)
+{
+	ENTER2("");
+	if (dst == NULL || src == NULL)
+		EXIT2(return NDIS_STATUS_FAILURE);
+	if (RtlUnicodeStringToAnsiString(dst, src, FALSE) == STATUS_SUCCESS)
+		return NDIS_STATUS_SUCCESS;
+	else
+		return NDIS_STATUS_FAILURE;
+}
+
+wstdcall NTSTATUS WIN_FUNC(NdisUpcaseUnicodeString,2)
+	(struct unicode_string *dst, struct unicode_string *src)
+{
+	EXIT2(return RtlUpcaseUnicodeString(dst, src, FALSE));
+}
+
+wstdcall void WIN_FUNC(NdisMSetAttributesEx,5)
+	(struct ndis_mp_block *nmb, void *mp_ctx,
+	 UINT hangcheck_interval, UINT attributes, ULONG adaptertype)
+{
+	struct ndis_device *wnd;
+
+	ENTER1("%p, %p, %d, %08x, %d", nmb, mp_ctx, hangcheck_interval,
+	       attributes, adaptertype);
+	wnd = nmb->wnd;
+	nmb->mp_ctx = mp_ctx;
+	wnd->attributes = attributes;
+
+	if ((attributes & NDIS_ATTRIBUTE_BUS_MASTER) &&
+	    wrap_is_pci_bus(wnd->wd->dev_bus))
+		pci_set_master(wnd->wd->pci.pdev);
+
+	if (hangcheck_interval > 0)
+		wnd->hangcheck_interval = 2 * hangcheck_interval * HZ;
+	else
+		wnd->hangcheck_interval = 2 * HZ;
+
+	EXIT1(return);
+}
+
+wstdcall ULONG WIN_FUNC(NdisReadPciSlotInformation,5)
+	(struct ndis_mp_block *nmb, ULONG slot,
+	 ULONG offset, char *buf, ULONG len)
+{
+	struct wrap_device *wd = nmb->wnd->wd;
+	ULONG i;
+	if (!wrap_is_pci_bus(wd->dev_bus)) {
+		ERROR("used on a non-PCI device");
+		return 0;
+	}
+	for (i = 0; i < len; i++)
+		if (pci_read_config_byte(wd->pci.pdev, offset + i, &buf[i]) !=
+		    PCIBIOS_SUCCESSFUL)
+			break;
+	DBG_BLOCK(2) {
+		if (i != len)
+			WARNING("%u, %u", i, len);
+	}
+	return i;
+}
+
+wstdcall ULONG WIN_FUNC(NdisImmediateReadPciSlotInformation,5)
+	(struct ndis_mp_block *nmb, ULONG slot,
+	 ULONG offset, char *buf, ULONG len)
+{
+	return NdisReadPciSlotInformation(nmb, slot, offset, buf, len);
+}
+
+wstdcall ULONG WIN_FUNC(NdisWritePciSlotInformation,5)
+	(struct ndis_mp_block *nmb, ULONG slot,
+	 ULONG offset, char *buf, ULONG len)
+{
+	struct wrap_device *wd = nmb->wnd->wd;
+	ULONG i;
+	if (!wrap_is_pci_bus(wd->dev_bus)) {
+		ERROR("used on a non-PCI device");
+		return 0;
+	}
+	for (i = 0; i < len; i++)
+		if (pci_write_config_byte(wd->pci.pdev, offset + i, buf[i]) !=
+		    PCIBIOS_SUCCESSFUL)
+			break;
+	DBG_BLOCK(2) {
+		if (i != len)
+			WARNING("%u, %u", i, len);
+	}
+	return i;
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMRegisterIoPortRange,4)
+	(void **virt, struct ndis_mp_block *nmb, UINT start, UINT len)
+{
+	ENTER3("%08x %08x", start, len);
+	*virt = (void *)(ULONG_PTR)start;
+	return NDIS_STATUS_SUCCESS;
+}
+
+wstdcall void WIN_FUNC(NdisMDeregisterIoPortRange,4)
+	(struct ndis_mp_block *nmb, UINT start, UINT len, void* virt)
+{
+	ENTER1("%08x %08x", start, len);
+}
+
+wstdcall void WIN_FUNC(NdisReadPortUchar,3)
+	(struct ndis_mp_block *nmb, ULONG port, char *data)
+{
+	*data = inb(port);
+}
+
+wstdcall void WIN_FUNC(NdisImmediateReadPortUchar,3)
+	(struct ndis_mp_block *nmb, ULONG port, char *data)
+{
+	*data = inb(port);
+}
+
+wstdcall void WIN_FUNC(NdisWritePortUchar,3)
+	(struct ndis_mp_block *nmb, ULONG port, char data)
+{
+	outb(data, port);
+}
+
+wstdcall void WIN_FUNC(NdisImmediateWritePortUchar,3)
+	(struct ndis_mp_block *nmb, ULONG port, char data)
+{
+	outb(data, port);
+}
+
+wstdcall void WIN_FUNC(NdisMQueryAdapterResources,4)
+	(NDIS_STATUS *status, struct ndis_mp_block *nmb,
+	 NDIS_RESOURCE_LIST *resource_list, UINT *size)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	NDIS_RESOURCE_LIST *list;
+	UINT resource_length;
+
+	list = &wnd->wd->resource_list->list->partial_resource_list;
+	resource_length = sizeof(struct cm_partial_resource_list) +
+		sizeof(struct cm_partial_resource_descriptor) *
+		(list->count - 1);
+	TRACE2("%p, %p,%d (%d), %p %d %d", wnd, resource_list, *size,
+	       resource_length, &list->partial_descriptors[list->count-1],
+	       list->partial_descriptors[list->count-1].u.interrupt.level,
+	       list->partial_descriptors[list->count-1].u.interrupt.vector);
+	if (*size < sizeof(*list)) {
+		*size = resource_length;
+		*status = NDIS_STATUS_BUFFER_TOO_SHORT;
+	} else {
+		ULONG count;
+		if (*size >= resource_length) {
+			*size = resource_length;
+			count = list->count;
+		} else {
+			UINT n = sizeof(*list);
+			count = 1;
+			while (count++ < list->count && n < *size)
+				n += sizeof(list->partial_descriptors);
+			*size = n;
+		}
+		memcpy(resource_list, list, *size);
+		resource_list->count = count;
+		*status = NDIS_STATUS_SUCCESS;
+	}
+	EXIT2(return);
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMPciAssignResources,3)
+	(struct ndis_mp_block *nmb, ULONG slot_number,
+	 NDIS_RESOURCE_LIST **resources)
+{
+	struct ndis_device *wnd = nmb->wnd;
+
+	ENTER2("%p, %p", wnd, wnd->wd->resource_list);
+	*resources = &wnd->wd->resource_list->list->partial_resource_list;
+	EXIT2(return NDIS_STATUS_SUCCESS);
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMMapIoSpace,4)
+	(void __iomem **virt, struct ndis_mp_block *nmb,
+	 NDIS_PHY_ADDRESS phy_addr, UINT len)
+{
+	struct ndis_device *wnd = nmb->wnd;
+
+	ENTER2("%llx, %d", phy_addr, len);
+	*virt = MmMapIoSpace(phy_addr, len, MmCached);
+	if (*virt == NULL) {
+		ERROR("ioremap failed");
+		EXIT2(return NDIS_STATUS_FAILURE);
+	}
+	wnd->mem_start = phy_addr;
+	wnd->mem_end = phy_addr + len;
+	TRACE2("%p", *virt);
+	EXIT2(return NDIS_STATUS_SUCCESS);
+}
+
+wstdcall void WIN_FUNC(NdisMUnmapIoSpace,3)
+	(struct ndis_mp_block *nmb, void __iomem *virt, UINT len)
+{
+	ENTER2("%p, %d", virt, len);
+	MmUnmapIoSpace(virt, len);
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisAllocateSpinLock,1)
+	(struct ndis_spinlock *lock)
+{
+	TRACE4("lock %p, %p", lock, &lock->klock);
+	KeInitializeSpinLock(&lock->klock);
+	lock->irql = PASSIVE_LEVEL;
+	return;
+}
+
+wstdcall void WIN_FUNC(NdisFreeSpinLock,1)
+	(struct ndis_spinlock *lock)
+{
+	TRACE4("lock %p, %p", lock, &lock->klock);
+	return;
+}
+
+wstdcall void WIN_FUNC(NdisAcquireSpinLock,1)
+	(struct ndis_spinlock *lock)
+{
+	ENTER6("lock %p, %p", lock, &lock->klock);
+//	assert_irql(_irql_ <= DISPATCH_LEVEL);
+	lock->irql = nt_spin_lock_irql(&lock->klock, DISPATCH_LEVEL);
+	return;
+}
+
+wstdcall void WIN_FUNC(NdisReleaseSpinLock,1)
+	(struct ndis_spinlock *lock)
+{
+	ENTER6("lock %p, %p", lock, &lock->klock);
+//	assert_irql(_irql_ == DISPATCH_LEVEL);
+	nt_spin_unlock_irql(&lock->klock, lock->irql);
+	return;
+}
+
+wstdcall void WIN_FUNC(NdisDprAcquireSpinLock,1)
+	(struct ndis_spinlock *lock)
+{
+	ENTER6("lock %p", &lock->klock);
+//	assert_irql(_irql_ == DISPATCH_LEVEL);
+	nt_spin_lock(&lock->klock);
+	return;
+}
+
+wstdcall void WIN_FUNC(NdisDprReleaseSpinLock,1)
+	(struct ndis_spinlock *lock)
+{
+	ENTER6("lock %p", &lock->klock);
+//	assert_irql(_irql_ == DISPATCH_LEVEL);
+	nt_spin_unlock(&lock->klock);
+	return;
+}
+
+wstdcall void WIN_FUNC(NdisInitializeReadWriteLock,1)
+	(struct ndis_rw_lock *rw_lock)
+{
+	ENTER3("%p", rw_lock);
+	memset(rw_lock, 0, sizeof(*rw_lock));
+	KeInitializeSpinLock(&rw_lock->klock);
+	return;
+}
+
+/* read/write locks are implemented in a rather simplistic way - we
+ * should probably use Linux's rw_lock implementation */
+
+wstdcall void WIN_FUNC(NdisAcquireReadWriteLock,3)
+	(struct ndis_rw_lock *rw_lock, BOOLEAN write,
+	 struct lock_state *lock_state)
+{
+	if (write) {
+		while (1) {
+			if (cmpxchg(&rw_lock->count, 0, -1) == 0)
+				return;
+			while (rw_lock->count)
+				cpu_relax();
+		}
+		return;
+	}
+	while (1) {
+		typeof(rw_lock->count) count;
+		while ((count = rw_lock->count) < 0)
+			cpu_relax();
+		if (cmpxchg(&rw_lock->count, count, count + 1) == count)
+			return;
+	}
+}
+
+wstdcall void WIN_FUNC(NdisReleaseReadWriteLock,2)
+	(struct ndis_rw_lock *rw_lock, struct lock_state *lock_state)
+{
+	if (rw_lock->count > 0)
+		pre_atomic_add(rw_lock->count, -1);
+	else if (rw_lock->count == -1)
+		rw_lock->count = 0;
+	else
+		WARNING("invalid state: %d", rw_lock->count);
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMAllocateMapRegisters,5)
+	(struct ndis_mp_block *nmb, UINT dmachan,
+	 NDIS_DMA_SIZE dmasize, ULONG basemap, ULONG max_buf_size)
+{
+	struct ndis_device *wnd = nmb->wnd;
+
+	ENTER2("%p, %d %d %d %d", wnd, dmachan, dmasize, basemap, max_buf_size);
+	if (!wrap_is_pci_bus(wnd->wd->dev_bus)) {
+		ERROR("used on a non-PCI device");
+		return NDIS_STATUS_NOT_SUPPORTED;
+	}
+	if (wnd->dma_map_count > 0) {
+		WARNING("%s: map registers already allocated: %u",
+			wnd->net_dev->name, wnd->dma_map_count);
+		EXIT2(return NDIS_STATUS_RESOURCES);
+	}
+	if (dmasize == NDIS_DMA_24BITS) {
+		if (pci_set_dma_mask(wnd->wd->pci.pdev, DMA_BIT_MASK(24)) ||
+		    pci_set_consistent_dma_mask(wnd->wd->pci.pdev,
+						DMA_BIT_MASK(24)))
+			WARNING("setting dma mask failed");
+	} else if (dmasize == NDIS_DMA_32BITS) {
+		/* consistent dma is in low 32-bits by default */
+		if (pci_set_dma_mask(wnd->wd->pci.pdev, DMA_BIT_MASK(32)))
+			WARNING("setting dma mask failed");
+#ifdef CONFIG_X86_64
+	} else if (dmasize == NDIS_DMA_64BITS) {
+		if (pci_set_dma_mask(wnd->wd->pci.pdev, DMA_BIT_MASK(64)) ||
+		    pci_set_consistent_dma_mask(wnd->wd->pci.pdev,
+						DMA_BIT_MASK(64)))
+			WARNING("setting dma mask failed");
+		else
+			wnd->net_dev->features |= NETIF_F_HIGHDMA;
+#endif
+	} else {
+		ERROR("dmasize %d not supported", dmasize);
+		EXIT2(return NDIS_STATUS_NOT_SUPPORTED);
+	}
+	/* since memory for buffer is allocated with kmalloc, buffer
+	 * is physically contiguous, so entire map will fit in one
+	 * register */
+	if (basemap > 64) {
+		WARNING("Windows driver %s requesting too many (%u) "
+			"map registers", wnd->wd->driver->name, basemap);
+		/* As per NDIS, NDIS_STATUS_RESOURCES should be
+		 * returned, but with that Atheros PCI driver fails -
+		 * for now tolerate it */
+//		EXIT2(return NDIS_STATUS_RESOURCES);
+	}
+
+	wnd->dma_map_addr = kzalloc(basemap * sizeof(*(wnd->dma_map_addr)),
+				    GFP_KERNEL);
+	if (!wnd->dma_map_addr)
+		EXIT2(return NDIS_STATUS_RESOURCES);
+	wnd->dma_map_count = basemap;
+	TRACE2("%u", wnd->dma_map_count);
+	EXIT2(return NDIS_STATUS_SUCCESS);
+}
+
+wstdcall void WIN_FUNC(NdisMFreeMapRegisters,1)
+	(struct ndis_mp_block *nmb)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	int i;
+
+	ENTER2("wnd: %p", wnd);
+	if (wnd->dma_map_addr) {
+		for (i = 0; i < wnd->dma_map_count; i++) {
+			if (wnd->dma_map_addr[i])
+				WARNING("%s: dma addr 0x%llx not freed by "
+					"Windows driver", wnd->net_dev->name,
+					(unsigned long long)wnd->dma_map_addr[i]);
+		}
+		kfree(wnd->dma_map_addr);
+		wnd->dma_map_addr = NULL;
+	} else
+		WARNING("map registers already freed?");
+	wnd->dma_map_count = 0;
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisMStartBufferPhysicalMapping,6)
+	(struct ndis_mp_block *nmb, ndis_buffer *buf,
+	 ULONG index, BOOLEAN write_to_dev,
+	 struct ndis_phy_addr_unit *phy_addr_array, UINT *array_size)
+{
+	struct ndis_device *wnd = nmb->wnd;
+
+	ENTER3("%p, %p, %u, %u", wnd, buf, index, wnd->dma_map_count);
+	if (!wrap_is_pci_bus(wnd->wd->dev_bus)) {
+		ERROR("used on a non-PCI device");
+		return;
+	}
+	if (unlikely(wnd->sg_dma_size || !write_to_dev ||
+		     index >= wnd->dma_map_count)) {
+		WARNING("invalid request: %d, %d, %d, %d", wnd->sg_dma_size,
+			write_to_dev, index, wnd->dma_map_count);
+		phy_addr_array[0].phy_addr = 0;
+		phy_addr_array[0].length = 0;
+		*array_size = 0;
+		return;
+	}
+	if (wnd->dma_map_addr[index]) {
+		TRACE2("buffer %p at %d is already mapped: %llx", buf, index,
+		       (unsigned long long)wnd->dma_map_addr[index]);
+//		*array_size = 1;
+		return;
+	}
+	TRACE3("%p, %p, %u", buf, MmGetSystemAddressForMdl(buf),
+	       MmGetMdlByteCount(buf));
+	DBG_BLOCK(4) {
+		dump_bytes(__func__, MmGetSystemAddressForMdl(buf),
+			   MmGetMdlByteCount(buf));
+	}
+	wnd->dma_map_addr[index] =
+		PCI_DMA_MAP_SINGLE(wnd->wd->pci.pdev,
+				   MmGetSystemAddressForMdl(buf),
+				   MmGetMdlByteCount(buf), PCI_DMA_TODEVICE);
+	phy_addr_array[0].phy_addr = wnd->dma_map_addr[index];
+	phy_addr_array[0].length = MmGetMdlByteCount(buf);
+	TRACE4("%llx, %d, %d", phy_addr_array[0].phy_addr,
+	       phy_addr_array[0].length, index);
+	*array_size = 1;
+}
+
+wstdcall void WIN_FUNC(NdisMCompleteBufferPhysicalMapping,3)
+	(struct ndis_mp_block *nmb, ndis_buffer *buf, ULONG index)
+{
+	struct ndis_device *wnd = nmb->wnd;
+
+	ENTER3("%p, %p %u (%u)", wnd, buf, index, wnd->dma_map_count);
+
+	if (!wrap_is_pci_bus(wnd->wd->dev_bus)) {
+		ERROR("used on a non-PCI device");
+		return;
+	}
+	if (unlikely(wnd->sg_dma_size))
+		WARNING("buffer %p may have been unmapped already", buf);
+	if (index >= wnd->dma_map_count) {
+		ERROR("invalid map register (%u >= %u)",
+		      index, wnd->dma_map_count);
+		return;
+	}
+	TRACE4("%llx", (unsigned long long)wnd->dma_map_addr[index]);
+	if (wnd->dma_map_addr[index]) {
+		PCI_DMA_UNMAP_SINGLE(wnd->wd->pci.pdev, wnd->dma_map_addr[index],
+				     MmGetMdlByteCount(buf), PCI_DMA_TODEVICE);
+		wnd->dma_map_addr[index] = 0;
+	} else
+		WARNING("map registers at %u not used", index);
+}
+
+wstdcall void WIN_FUNC(NdisMAllocateSharedMemory,5)
+	(struct ndis_mp_block *nmb, ULONG size,
+	 BOOLEAN cached, void **virt, NDIS_PHY_ADDRESS *phys)
+{
+	dma_addr_t dma_addr;
+	struct wrap_device *wd = nmb->wnd->wd;
+
+	ENTER3("size: %u, cached: %d", size, cached);
+	if (!wrap_is_pci_bus(wd->dev_bus)) {
+		ERROR("used on a non-PCI device");
+		return;
+	}
+	*virt = PCI_DMA_ALLOC_COHERENT(wd->pci.pdev, size, &dma_addr);
+	if (*virt)
+		*phys = dma_addr;
+	else
+		WARNING("couldn't allocate %d bytes of %scached DMA memory",
+			size, cached ? "" : "un-");
+	EXIT3(return);
+}
+
+wstdcall void WIN_FUNC(NdisMFreeSharedMemory,5)
+	(struct ndis_mp_block *nmb, ULONG size, BOOLEAN cached,
+	 void *virt, NDIS_PHY_ADDRESS addr)
+{
+	struct wrap_device *wd = nmb->wnd->wd;
+	ENTER3("%p, %llx, %u", virt, addr, size);
+	if (!wrap_is_pci_bus(wd->dev_bus)) {
+		ERROR("used on a non-PCI device");
+		return;
+	}
+	PCI_DMA_FREE_COHERENT(wd->pci.pdev, size, virt, addr);
+	EXIT3(return);
+}
+
+wstdcall void alloc_shared_memory_async(void *arg1, void *arg2)
+{
+	struct ndis_device *wnd;
+	struct alloc_shared_mem *alloc_shared_mem;
+	struct miniport *mp;
+	void *virt;
+	NDIS_PHY_ADDRESS phys;
+	KIRQL irql;
+
+	wnd = arg1;
+	alloc_shared_mem = arg2;
+	mp = &wnd->wd->driver->ndis_driver->mp;
+	NdisMAllocateSharedMemory(wnd->nmb, alloc_shared_mem->size,
+				  alloc_shared_mem->cached, &virt, &phys);
+	irql = serialize_lock_irql(wnd);
+	assert_irql(_irql_ == DISPATCH_LEVEL);
+	LIN2WIN5(mp->alloc_complete, wnd->nmb, virt,
+		 &phys, alloc_shared_mem->size, alloc_shared_mem->ctx);
+	serialize_unlock_irql(wnd, irql);
+	kfree(alloc_shared_mem);
+}
+WIN_FUNC_DECL(alloc_shared_memory_async,2)
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMAllocateSharedMemoryAsync,4)
+	(struct ndis_mp_block *nmb, ULONG size, BOOLEAN cached, void *ctx)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	struct alloc_shared_mem *alloc_shared_mem;
+
+	ENTER3("wnd: %p", wnd);
+	alloc_shared_mem = kmalloc(sizeof(*alloc_shared_mem), irql_gfp());
+	if (!alloc_shared_mem) {
+		WARNING("couldn't allocate memory");
+		return NDIS_STATUS_FAILURE;
+	}
+
+	alloc_shared_mem->size = size;
+	alloc_shared_mem->cached = cached;
+	alloc_shared_mem->ctx = ctx;
+	if (schedule_ntos_work_item(WIN_FUNC_PTR(alloc_shared_memory_async,2),
+				    wnd, alloc_shared_mem))
+		EXIT3(return NDIS_STATUS_FAILURE);
+	EXIT3(return NDIS_STATUS_PENDING);
+}
+
+/* Some drivers allocate NDIS_BUFFER (aka MDL) very often; instead of
+ * allocating and freeing with kernel functions, we chain them into
+ * ndis_buffer_pool. When an MDL is freed, it is added to the list of
+ * free MDLs. When allocated, we first check if there is one in free
+ * list and if so just return it; otherwise, we allocate a new one and
+ * return that. This reduces memory fragmentation. Windows DDK says
+ * that the driver itself shouldn't check what is returned in
+ * pool_handle, presumably because buffer pools are not used in
+ * XP. However, as long as driver follows rest of the semantics - that
+ * it should indicate maximum number of MDLs used with num_descr and
+ * pass the same pool_handle in other buffer functions, this should
+ * work. Sadly, though, NdisFreeBuffer doesn't pass the pool_handle,
+ * so we use 'process' field of MDL to store pool_handle. */
+
+wstdcall void WIN_FUNC(NdisAllocateBufferPool,3)
+	(NDIS_STATUS *status, struct ndis_buffer_pool **pool_handle,
+	 UINT num_descr)
+{
+	struct ndis_buffer_pool *pool;
+
+	ENTER1("buffers: %d", num_descr);
+	pool = kmalloc(sizeof(*pool), irql_gfp());
+	if (!pool) {
+		*status = NDIS_STATUS_RESOURCES;
+		EXIT3(return);
+	}
+	spin_lock_init(&pool->lock);
+	pool->max_descr = num_descr;
+	pool->num_allocated_descr = 0;
+	pool->free_descr = NULL;
+	*pool_handle = pool;
+	*status = NDIS_STATUS_SUCCESS;
+	TRACE1("pool: %p, num_descr: %d", pool, num_descr);
+	EXIT1(return);
+}
+
+wstdcall void WIN_FUNC(NdisAllocateBuffer,5)
+	(NDIS_STATUS *status, ndis_buffer **buffer,
+	 struct ndis_buffer_pool *pool, void *virt, UINT length)
+{
+	ndis_buffer *descr;
+
+	ENTER4("pool: %p (%d)", pool, pool->num_allocated_descr);
+	/* NDIS drivers should call this at DISPATCH_LEVEL, but
+	 * alloc_tx_packet calls at SOFT_IRQL */
+	assert_irql(_irql_ <= SOFT_LEVEL);
+	if (!pool) {
+		*status = NDIS_STATUS_FAILURE;
+		*buffer = NULL;
+		EXIT4(return);
+	}
+	spin_lock_bh(&pool->lock);
+	if ((descr = pool->free_descr))
+		pool->free_descr = descr->next;
+	spin_unlock_bh(&pool->lock);
+	if (descr) {
+		typeof(descr->flags) flags;
+		flags = descr->flags;
+		memset(descr, 0, sizeof(*descr));
+		MmInitializeMdl(descr, virt, length);
+		if (flags & MDL_CACHE_ALLOCATED)
+			descr->flags |= MDL_CACHE_ALLOCATED;
+	} else {
+		if (pool->num_allocated_descr > pool->max_descr) {
+			TRACE2("pool %p is full: %d(%d)", pool,
+			       pool->num_allocated_descr, pool->max_descr);
+#ifndef ALLOW_POOL_OVERFLOW
+			*status = NDIS_STATUS_FAILURE;
+			*buffer = NULL;
+			return;
+#endif
+		}
+		descr = allocate_init_mdl(virt, length);
+		if (!descr) {
+			WARNING("couldn't allocate buffer");
+			*status = NDIS_STATUS_FAILURE;
+			*buffer = NULL;
+			EXIT4(return);
+		}
+		TRACE4("buffer %p for %p, %d", descr, virt, length);
+		atomic_inc_var(pool->num_allocated_descr);
+	}
+	/* TODO: make sure this mdl can map given buffer */
+	MmBuildMdlForNonPagedPool(descr);
+//	descr->flags |= MDL_ALLOCATED_FIXED_SIZE |
+//		MDL_MAPPED_TO_SYSTEM_VA | MDL_PAGES_LOCKED;
+	descr->pool = pool;
+	*buffer = descr;
+	*status = NDIS_STATUS_SUCCESS;
+	TRACE4("buffer: %p", descr);
+	EXIT4(return);
+}
+
+wstdcall void WIN_FUNC(NdisFreeBuffer,1)
+	(ndis_buffer *buffer)
+{
+	struct ndis_buffer_pool *pool;
+
+	ENTER4("%p", buffer);
+	if (!buffer || !buffer->pool) {
+		ERROR("invalid buffer");
+		EXIT4(return);
+	}
+	pool = buffer->pool;
+	if (pool->num_allocated_descr > MAX_ALLOCATED_NDIS_BUFFERS) {
+		/* NB NB NB: set mdl's 'pool' field to NULL before
+		 * calling free_mdl; otherwise free_mdl calls
+		 * NdisFreeBuffer back */
+		atomic_dec_var(pool->num_allocated_descr);
+		buffer->pool = NULL;
+		free_mdl(buffer);
+	} else {
+		spin_lock_bh(&pool->lock);
+		buffer->next = pool->free_descr;
+		pool->free_descr = buffer;
+		spin_unlock_bh(&pool->lock);
+	}
+	EXIT4(return);
+}
+
+wstdcall void WIN_FUNC(NdisFreeBufferPool,1)
+	(struct ndis_buffer_pool *pool)
+{
+	ndis_buffer *cur, *next;
+
+	TRACE3("pool: %p", pool);
+	if (!pool) {
+		WARNING("invalid pool");
+		EXIT3(return);
+	}
+	spin_lock_bh(&pool->lock);
+	cur = pool->free_descr;
+	while (cur) {
+		next = cur->next;
+		cur->pool = NULL;
+		free_mdl(cur);
+		cur = next;
+	}
+	spin_unlock_bh(&pool->lock);
+	kfree(pool);
+	pool = NULL;
+	EXIT3(return);
+}
+
+wstdcall void WIN_FUNC(NdisAdjustBufferLength,2)
+	(ndis_buffer *buffer, UINT length)
+{
+	ENTER4("%p, %d", buffer, length);
+	buffer->bytecount = length;
+}
+
+wstdcall void WIN_FUNC(NdisQueryBuffer,3)
+	(ndis_buffer *buffer, void **virt, UINT *length)
+{
+	ENTER4("buffer: %p", buffer);
+	if (virt)
+		*virt = MmGetSystemAddressForMdl(buffer);
+	*length = MmGetMdlByteCount(buffer);
+	TRACE4("%p, %u", virt ? *virt : NULL, *length);
+	return;
+}
+
+wstdcall void WIN_FUNC(NdisQueryBufferSafe,4)
+	(ndis_buffer *buffer, void **virt, UINT *length,
+	 enum mm_page_priority priority)
+{
+	ENTER4("%p, %p, %p, %d", buffer, virt, length, priority);
+	if (virt)
+		*virt = MmGetSystemAddressForMdlSafe(buffer, priority);
+	*length = MmGetMdlByteCount(buffer);
+	TRACE4("%p, %u", virt ? *virt : NULL, *length);
+}
+
+wstdcall void *WIN_FUNC(NdisBufferVirtualAddress,1)
+	(ndis_buffer *buffer)
+{
+	ENTER3("%p", buffer);
+	return MmGetSystemAddressForMdl(buffer);
+}
+
+wstdcall ULONG WIN_FUNC(NdisBufferLength,1)
+	(ndis_buffer *buffer)
+{
+	ENTER3("%p", buffer);
+	return MmGetMdlByteCount(buffer);
+}
+
+wstdcall void WIN_FUNC(NdisQueryBufferOffset,3)
+	(ndis_buffer *buffer, UINT *offset, UINT *length)
+{
+	ENTER3("%p", buffer);
+	*offset = MmGetMdlByteOffset(buffer);
+	*length = MmGetMdlByteCount(buffer);
+	TRACE3("%d, %d", *offset, *length);
+}
+
+wstdcall void WIN_FUNC(NdisUnchainBufferAtBack,2)
+	(struct ndis_packet *packet, ndis_buffer **buffer)
+{
+	ndis_buffer *b, *btail;
+
+	ENTER3("%p", packet);
+	b = packet->private.buffer_head;
+	if (!b) {
+		/* no buffer in packet */
+		*buffer = NULL;
+		EXIT3(return);
+	}
+	btail = packet->private.buffer_tail;
+	*buffer = btail;
+	if (b == btail) {
+		/* one buffer in packet */
+		packet->private.buffer_head = NULL;
+		packet->private.buffer_tail = NULL;
+	} else {
+		while (b->next != btail)
+			b = b->next;
+		packet->private.buffer_tail = b;
+		b->next = NULL;
+	}
+	packet->private.valid_counts = FALSE;
+	EXIT3(return);
+}
+
+wstdcall void WIN_FUNC(NdisUnchainBufferAtFront,2)
+	(struct ndis_packet *packet, ndis_buffer **buffer)
+{
+	ENTER3("%p", packet);
+	if (packet->private.buffer_head == NULL) {
+		/* no buffer in packet */
+		*buffer = NULL;
+		EXIT3(return);
+	}
+
+	*buffer = packet->private.buffer_head;
+	if (packet->private.buffer_head == packet->private.buffer_tail) {
+		/* one buffer in packet */
+		packet->private.buffer_head = NULL;
+		packet->private.buffer_tail = NULL;
+	} else
+		packet->private.buffer_head = (*buffer)->next;
+
+	packet->private.valid_counts = FALSE;
+	EXIT3(return);
+}
+
+wstdcall void WIN_FUNC(NdisGetFirstBufferFromPacketSafe,6)
+	(struct ndis_packet *packet, ndis_buffer **first_buffer,
+	 void **first_buffer_va, UINT *first_buffer_length,
+	 UINT *total_buffer_length, enum mm_page_priority priority)
+{
+	ndis_buffer *b = packet->private.buffer_head;
+
+	ENTER3("%p(%p)", packet, b);
+	*first_buffer = b;
+	if (b) {
+		*first_buffer_va = MmGetSystemAddressForMdlSafe(b, priority);
+		*first_buffer_length = *total_buffer_length =
+			MmGetMdlByteCount(b);
+		for (b = b->next; b; b = b->next)
+			*total_buffer_length += MmGetMdlByteCount(b);
+	} else {
+		*first_buffer_va = NULL;
+		*first_buffer_length = 0;
+		*total_buffer_length = 0;
+	}
+	TRACE3("%p, %d, %d", *first_buffer_va, *first_buffer_length,
+	       *total_buffer_length);
+	EXIT3(return);
+}
+
+wstdcall void WIN_FUNC(NdisGetFirstBufferFromPacket,6)
+	(struct ndis_packet *packet, ndis_buffer **first_buffer,
+	 void **first_buffer_va, UINT *first_buffer_length,
+	 UINT *total_buffer_length, enum mm_page_priority priority)
+{
+	NdisGetFirstBufferFromPacketSafe(packet, first_buffer,
+					 first_buffer_va, first_buffer_length,
+					 total_buffer_length,
+					 NormalPagePriority);
+}
+
+wstdcall void WIN_FUNC(NdisAllocatePacketPoolEx,5)
+	(NDIS_STATUS *status, struct ndis_packet_pool **pool_handle,
+	 UINT num_descr, UINT overflowsize, UINT proto_rsvd_length)
+{
+	struct ndis_packet_pool *pool;
+
+	ENTER3("buffers: %d, length: %d", num_descr, proto_rsvd_length);
+	pool = kzalloc(sizeof(*pool), irql_gfp());
+	if (!pool) {
+		*status = NDIS_STATUS_RESOURCES;
+		EXIT3(return);
+	}
+	spin_lock_init(&pool->lock);
+	pool->max_descr = num_descr;
+	pool->num_allocated_descr = 0;
+	pool->num_used_descr = 0;
+	pool->free_descr = NULL;
+	pool->proto_rsvd_length = proto_rsvd_length;
+	*pool_handle = pool;
+	*status = NDIS_STATUS_SUCCESS;
+	TRACE3("pool: %p", pool);
+	EXIT3(return);
+}
+
+wstdcall void WIN_FUNC(NdisAllocatePacketPool,4)
+	(NDIS_STATUS *status, struct ndis_packet_pool **pool_handle,
+	 UINT num_descr, UINT proto_rsvd_length)
+{
+	NdisAllocatePacketPoolEx(status, pool_handle, num_descr, 0,
+				 proto_rsvd_length);
+	EXIT3(return);
+}
+
+wstdcall void WIN_FUNC(NdisFreePacketPool,1)
+	(struct ndis_packet_pool *pool)
+{
+	struct ndis_packet *packet, *next;
+
+	ENTER3("pool: %p", pool);
+	if (!pool) {
+		WARNING("invalid pool");
+		EXIT3(return);
+	}
+	spin_lock_bh(&pool->lock);
+	packet = pool->free_descr;
+	while (packet) {
+		next = (struct ndis_packet *)packet->reserved[0];
+		kfree(packet);
+		packet = next;
+	}
+	pool->num_allocated_descr = 0;
+	pool->num_used_descr = 0;
+	pool->free_descr = NULL;
+	spin_unlock_bh(&pool->lock);
+	kfree(pool);
+	EXIT3(return);
+}
+
+wstdcall UINT WIN_FUNC(NdisPacketPoolUsage,1)
+	(struct ndis_packet_pool *pool)
+{
+	EXIT4(return pool->num_used_descr);
+}
+
+wstdcall void WIN_FUNC(NdisAllocatePacket,3)
+	(NDIS_STATUS *status, struct ndis_packet **ndis_packet,
+	 struct ndis_packet_pool *pool)
+{
+	struct ndis_packet *packet;
+	int packet_length;
+
+	ENTER4("pool: %p", pool);
+	if (!pool) {
+		*status = NDIS_STATUS_RESOURCES;
+		*ndis_packet = NULL;
+		EXIT4(return);
+	}
+	assert_irql(_irql_ <= SOFT_LEVEL);
+	if (pool->num_used_descr > pool->max_descr) {
+		TRACE3("pool %p is full: %d(%d)", pool,
+		       pool->num_used_descr, pool->max_descr);
+#ifndef ALLOW_POOL_OVERFLOW
+		*status = NDIS_STATUS_RESOURCES;
+		*ndis_packet = NULL;
+		return;
+#endif
+	}
+	/* packet has space for 1 byte in protocol_reserved field */
+	packet_length = sizeof(*packet) - 1 + pool->proto_rsvd_length +
+		sizeof(struct ndis_packet_oob_data);
+	spin_lock_bh(&pool->lock);
+	if ((packet = pool->free_descr))
+		pool->free_descr = (void *)packet->reserved[0];
+	spin_unlock_bh(&pool->lock);
+	if (!packet) {
+		packet = kmalloc(packet_length, irql_gfp());
+		if (!packet) {
+			WARNING("couldn't allocate packet");
+			*status = NDIS_STATUS_RESOURCES;
+			*ndis_packet = NULL;
+			return;
+		}
+		atomic_inc_var(pool->num_allocated_descr);
+	}
+	TRACE4("%p, %p", pool, packet);
+	atomic_inc_var(pool->num_used_descr);
+	memset(packet, 0, packet_length);
+	packet->private.oob_offset =
+		packet_length - sizeof(struct ndis_packet_oob_data);
+	packet->private.packet_flags = fPACKET_ALLOCATED_BY_NDIS;
+	packet->private.pool = pool;
+	*ndis_packet = packet;
+	*status = NDIS_STATUS_SUCCESS;
+	EXIT4(return);
+}
+
+wstdcall void WIN_FUNC(NdisDprAllocatePacket,3)
+	(NDIS_STATUS *status, struct ndis_packet **packet,
+	 struct ndis_packet_pool *pool)
+{
+	NdisAllocatePacket(status, packet, pool);
+}
+
+wstdcall void WIN_FUNC(NdisFreePacket,1)
+	(struct ndis_packet *packet)
+{
+	struct ndis_packet_pool *pool;
+
+	ENTER4("%p, %p", packet, packet->private.pool);
+	pool = packet->private.pool;
+	if (!pool) {
+		ERROR("invalid pool %p", packet);
+		EXIT4(return);
+	}
+	assert((int)pool->num_used_descr > 0);
+	atomic_dec_var(pool->num_used_descr);
+	if (packet->reserved[1]) {
+		TRACE3("%p, %p", packet, (void *)packet->reserved[1]);
+		kfree((void *)packet->reserved[1]);
+		packet->reserved[1] = 0;
+	}
+	if (pool->num_allocated_descr > MAX_ALLOCATED_NDIS_PACKETS) {
+		TRACE3("%p", pool);
+		atomic_dec_var(pool->num_allocated_descr);
+		kfree(packet);
+	} else {
+		TRACE4("%p, %p, %p", pool, packet, pool->free_descr);
+		spin_lock_bh(&pool->lock);
+		packet->reserved[0] =
+			(typeof(packet->reserved[0]))pool->free_descr;
+		pool->free_descr = packet;
+		spin_unlock_bh(&pool->lock);
+	}
+	EXIT4(return);
+}
+
+wstdcall struct ndis_packet_stack *WIN_FUNC(NdisIMGetCurrentPacketStack,2)
+	(struct ndis_packet *packet, BOOLEAN *stacks_remain)
+{
+	struct ndis_packet_stack *stack;
+
+	if (!packet->reserved[1]) {
+		stack = kzalloc(2 * sizeof(*stack), irql_gfp());
+		TRACE3("%p, %p", packet, stack);
+		packet->reserved[1] = (typeof(packet->reserved[1]))stack;
+	} else {
+		stack = (void *)packet->reserved[1];;
+		if (xchg(&stack->ndis_reserved[0], 1)) {
+			stack++;
+			if (xchg(&stack->ndis_reserved[0], 1))
+				stack = NULL;
+		}
+		TRACE3("%p", stack);
+	}
+	if (stack)
+		*stacks_remain = TRUE;
+	else
+		*stacks_remain = FALSE;
+
+	EXIT3(return stack);
+}
+
+wstdcall void WIN_FUNC(NdisCopyFromPacketToPacketSafe,7)
+	(struct ndis_packet *dst, UINT dst_offset, UINT num_to_copy,
+	 struct ndis_packet *src, UINT src_offset, UINT *num_copied,
+	 enum mm_page_priority priority)
+{
+	UINT dst_n, src_n, n, left;
+	ndis_buffer *dst_buf;
+	ndis_buffer *src_buf;
+
+	ENTER4("");
+	if (!dst || !src) {
+		*num_copied = 0;
+		EXIT4(return);
+	}
+
+	dst_buf = dst->private.buffer_head;
+	src_buf = src->private.buffer_head;
+
+	if (!dst_buf || !src_buf) {
+		*num_copied = 0;
+		EXIT4(return);
+	}
+	dst_n = MmGetMdlByteCount(dst_buf) - dst_offset;
+	src_n = MmGetMdlByteCount(src_buf) - src_offset;
+
+	n = min(src_n, dst_n);
+	n = min(n, num_to_copy);
+	memcpy(MmGetSystemAddressForMdl(dst_buf) + dst_offset,
+	       MmGetSystemAddressForMdl(src_buf) + src_offset, n);
+
+	left = num_to_copy - n;
+	while (left > 0) {
+		src_offset += n;
+		dst_offset += n;
+		dst_n -= n;
+		src_n -= n;
+		if (dst_n == 0) {
+			dst_buf = dst_buf->next;
+			if (!dst_buf)
+				break;
+			dst_n = MmGetMdlByteCount(dst_buf);
+			dst_offset = 0;
+		}
+		if (src_n == 0) {
+			src_buf = src_buf->next;
+			if (!src_buf)
+				break;
+			src_n = MmGetMdlByteCount(src_buf);
+			src_offset = 0;
+		}
+
+		n = min(src_n, dst_n);
+		n = min(n, left);
+		memcpy(MmGetSystemAddressForMdl(dst_buf) + dst_offset,
+		       MmGetSystemAddressForMdl(src_buf) + src_offset, n);
+		left -= n;
+	}
+	*num_copied = num_to_copy - left;
+	EXIT4(return);
+}
+
+wstdcall void WIN_FUNC(NdisCopyFromPacketToPacket,6)
+	(struct ndis_packet *dst, UINT dst_offset, UINT num_to_copy,
+	 struct ndis_packet *src, UINT src_offset, UINT *num_copied)
+{
+	NdisCopyFromPacketToPacketSafe(dst, dst_offset, num_to_copy,
+				       src, src_offset, num_copied,
+				       NormalPagePriority);
+	return;
+}
+
+wstdcall void WIN_FUNC(NdisIMCopySendPerPacketInfo,2)
+	(struct ndis_packet *dst, struct ndis_packet *src)
+{
+	struct ndis_packet_oob_data *dst_oob, *src_oob;
+	dst_oob = NDIS_PACKET_OOB_DATA(dst);
+	src_oob = NDIS_PACKET_OOB_DATA(src);
+	memcpy(&dst_oob->ext, &src_oob->ext, sizeof(dst_oob->ext));
+	return;
+}
+
+wstdcall void WIN_FUNC(NdisSend,3)
+	(NDIS_STATUS *status, struct ndis_mp_block *nmb,
+	 struct ndis_packet *packet)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	struct miniport *mp;
+	KIRQL irql;
+
+	mp = &wnd->wd->driver->ndis_driver->mp;
+	if (mp->send_packets) {
+		irql = serialize_lock_irql(wnd);
+		assert_irql(_irql_ == DISPATCH_LEVEL);
+		LIN2WIN3(mp->send_packets, wnd->nmb->mp_ctx, &packet, 1);
+		serialize_unlock_irql(wnd, irql);
+		if (deserialized_driver(wnd))
+			*status = NDIS_STATUS_PENDING;
+		else {
+			struct ndis_packet_oob_data *oob_data;
+			oob_data = NDIS_PACKET_OOB_DATA(packet);
+			*status = oob_data->status;
+			switch (*status) {
+			case NDIS_STATUS_SUCCESS:
+				free_tx_packet(wnd, packet, *status);
+				break;
+			case NDIS_STATUS_PENDING:
+				break;
+			case NDIS_STATUS_RESOURCES:
+				wnd->tx_ok = 0;
+				break;
+			case NDIS_STATUS_FAILURE:
+			default:
+				free_tx_packet(wnd, packet, *status);
+				break;
+			}
+		}
+	} else {
+		irql = serialize_lock_irql(wnd);
+		assert_irql(_irql_ == DISPATCH_LEVEL);
+		*status = LIN2WIN3(mp->send, wnd->nmb->mp_ctx, packet, 0);
+		serialize_unlock_irql(wnd, irql);
+		switch (*status) {
+		case NDIS_STATUS_SUCCESS:
+			free_tx_packet(wnd, packet, *status);
+			break;
+		case NDIS_STATUS_PENDING:
+			break;
+		case NDIS_STATUS_RESOURCES:
+			wnd->tx_ok = 0;
+			break;
+		case NDIS_STATUS_FAILURE:
+		default:
+			free_tx_packet(wnd, packet, *status);
+			break;
+		}
+	}
+	EXIT3(return);
+}
+
+/* called for serialized drivers only */
+wstdcall void mp_timer_dpc(struct kdpc *kdpc, void *ctx, void *arg1, void *arg2)
+{
+	struct ndis_mp_timer *timer;
+	struct ndis_mp_block *nmb;
+
+	timer = ctx;
+	TIMERENTER("%p, %p, %p, %p", timer, timer->func, timer->ctx, timer->nmb);
+	assert_irql(_irql_ == DISPATCH_LEVEL);
+	nmb = timer->nmb;
+	serialize_lock(nmb->wnd);
+	LIN2WIN4(timer->func, NULL, timer->ctx, NULL, NULL);
+	serialize_unlock(nmb->wnd);
+	TIMEREXIT(return);
+}
+WIN_FUNC_DECL(mp_timer_dpc,4)
+
+wstdcall void WIN_FUNC(NdisMInitializeTimer,4)
+	(struct ndis_mp_timer *timer, struct ndis_mp_block *nmb,
+	 DPC func, void *ctx)
+{
+	TIMERENTER("%p, %p, %p, %p", timer, func, ctx, nmb);
+	assert_irql(_irql_ == PASSIVE_LEVEL);
+	timer->func = func;
+	timer->ctx = ctx;
+	timer->nmb = nmb;
+	if (deserialized_driver(nmb->wnd))
+		KeInitializeDpc(&timer->kdpc, func, ctx);
+	else
+		KeInitializeDpc(&timer->kdpc, WIN_FUNC_PTR(mp_timer_dpc,4),
+				timer);
+	wrap_init_timer(&timer->nt_timer, NotificationTimer, nmb);
+	TIMEREXIT(return);
+}
+
+wstdcall void WIN_FUNC(NdisMSetPeriodicTimer,2)
+	(struct ndis_mp_timer *timer, UINT period_ms)
+{
+	unsigned long expires = MSEC_TO_HZ(period_ms);
+
+	TIMERENTER("%p, %u, %ld", timer, period_ms, expires);
+	assert_irql(_irql_ <= DISPATCH_LEVEL);
+	wrap_set_timer(&timer->nt_timer, expires, expires, &timer->kdpc);
+	TIMEREXIT(return);
+}
+
+wstdcall void WIN_FUNC(NdisMCancelTimer,2)
+	(struct ndis_mp_timer *timer, BOOLEAN *canceled)
+{
+	TIMERENTER("%p", timer);
+	assert_irql(_irql_ <= DISPATCH_LEVEL);
+	*canceled = KeCancelTimer(&timer->nt_timer);
+	TIMERTRACE("%d", *canceled);
+	return;
+}
+
+wstdcall void WIN_FUNC(NdisInitializeTimer,3)
+	(struct ndis_timer *timer, void *func, void *ctx)
+{
+	TIMERENTER("%p, %p, %p", timer, func, ctx);
+	assert_irql(_irql_ == PASSIVE_LEVEL);
+	KeInitializeDpc(&timer->kdpc, func, ctx);
+	wrap_init_timer(&timer->nt_timer, NotificationTimer, NULL);
+	TIMEREXIT(return);
+}
+
+/* NdisMSetTimer is a macro that calls NdisSetTimer with
+ * ndis_mp_timer typecast to ndis_timer */
+
+wstdcall void WIN_FUNC(NdisSetTimer,2)
+	(struct ndis_timer *timer, UINT duetime_ms)
+{
+	unsigned long expires = MSEC_TO_HZ(duetime_ms);
+
+	TIMERENTER("%p, %p, %u, %ld", timer, timer->nt_timer.wrap_timer,
+		   duetime_ms, expires);
+	assert_irql(_irql_ <= DISPATCH_LEVEL);
+	wrap_set_timer(&timer->nt_timer, expires, 0, &timer->kdpc);
+	TIMEREXIT(return);
+}
+
+wstdcall void WIN_FUNC(NdisCancelTimer,2)
+	(struct ndis_timer *timer, BOOLEAN *canceled)
+{
+	TIMERENTER("%p", timer);
+	assert_irql(_irql_ <= DISPATCH_LEVEL);
+	*canceled = KeCancelTimer(&timer->nt_timer);
+	TIMEREXIT(return);
+}
+
+wstdcall void WIN_FUNC(NdisMRegisterAdapterShutdownHandler,3)
+	(struct ndis_mp_block *nmb, void *ctx, void *func)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	ENTER1("%p", func);
+	wnd->wd->driver->ndis_driver->mp.shutdown = func;
+	wnd->shutdown_ctx = ctx;
+}
+
+wstdcall void WIN_FUNC(NdisMDeregisterAdapterShutdownHandler,1)
+	(struct ndis_mp_block *nmb)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	wnd->wd->driver->ndis_driver->mp.shutdown = NULL;
+	wnd->shutdown_ctx = NULL;
+}
+
+/* TODO: rt61 (serialized) driver doesn't want MiniportEnableInterrupt
+ * to be called in irq handler, but mrv800c (deserialized) driver
+ * wants. NDIS is confusing about when to call MiniportEnableInterrupt
+ * For now, handle these cases with two separate irq handlers based on
+ * observation of these two drivers. However, it is likely not
+ * correct. */
+wstdcall void deserialized_irq_handler(struct kdpc *kdpc, void *ctx,
+				       void *arg1, void *arg2)
+{
+	struct ndis_device *wnd = ctx;
+	ndis_interrupt_handler irq_handler = arg1;
+	struct miniport *mp = arg2;
+
+	TRACE6("%p", irq_handler);
+	assert_irql(_irql_ == DISPATCH_LEVEL);
+	LIN2WIN1(irq_handler, wnd->nmb->mp_ctx);
+	if (mp->enable_interrupt)
+		LIN2WIN1(mp->enable_interrupt, wnd->nmb->mp_ctx);
+	EXIT6(return);
+}
+WIN_FUNC_DECL(deserialized_irq_handler,4)
+
+wstdcall void serialized_irq_handler(struct kdpc *kdpc, void *ctx,
+				     void *arg1, void *arg2)
+{
+	struct ndis_device *wnd = ctx;
+	ndis_interrupt_handler irq_handler = arg1;
+
+	TRACE6("%p, %p, %p", wnd, irq_handler, arg2);
+	assert_irql(_irql_ == DISPATCH_LEVEL);
+	serialize_lock(wnd);
+	LIN2WIN1(irq_handler, arg2);
+	serialize_unlock(wnd);
+	EXIT6(return);
+}
+WIN_FUNC_DECL(serialized_irq_handler,4)
+
+wstdcall BOOLEAN ndis_isr(struct kinterrupt *kinterrupt, void *ctx)
+{
+	struct ndis_mp_interrupt *mp_interrupt = ctx;
+	struct ndis_device *wnd = mp_interrupt->nmb->wnd;
+	BOOLEAN recognized = TRUE, queue_handler = TRUE;
+
+	TRACE6("%p", wnd);
+	/* kernel may call ISR when registering interrupt, in
+	 * the same context if DEBUG_SHIRQ is enabled */
+	assert_irql(_irql_ == DIRQL || _irql_ == PASSIVE_LEVEL);
+	if (mp_interrupt->shared)
+		LIN2WIN3(mp_interrupt->isr, &recognized, &queue_handler,
+			 wnd->nmb->mp_ctx);
+	else {
+		struct miniport *mp;
+		mp = &wnd->wd->driver->ndis_driver->mp;
+		LIN2WIN1(mp->disable_interrupt, wnd->nmb->mp_ctx);
+		/* it is not shared interrupt, so handler must be called */
+		recognized = queue_handler = TRUE;
+	}
+	if (recognized) {
+		if (queue_handler) {
+			TRACE5("%p", &wnd->irq_kdpc);
+			queue_kdpc(&wnd->irq_kdpc);
+		}
+		EXIT6(return TRUE);
+	}
+	EXIT6(return FALSE);
+}
+WIN_FUNC_DECL(ndis_isr,2)
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMRegisterInterrupt,7)
+	(struct ndis_mp_interrupt *mp_interrupt,
+	 struct ndis_mp_block *nmb, UINT vector, UINT level,
+	 BOOLEAN req_isr, BOOLEAN shared, enum kinterrupt_mode mode)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	struct miniport *mp;
+
+	ENTER1("%p, vector:%d, level:%d, req_isr:%d, shared:%d, mode:%d",
+	       mp_interrupt, vector, level, req_isr, shared, mode);
+
+	mp = &wnd->wd->driver->ndis_driver->mp;
+	nt_spin_lock_init(&mp_interrupt->lock);
+	mp_interrupt->irq = vector;
+	mp_interrupt->isr = mp->isr;
+	mp_interrupt->mp_dpc = mp->handle_interrupt;
+	mp_interrupt->nmb = nmb;
+	mp_interrupt->req_isr = req_isr;
+	if (shared && !req_isr)
+		WARNING("shared but dynamic interrupt!");
+	mp_interrupt->shared = shared;
+	wnd->mp_interrupt = mp_interrupt;
+	if (mp->enable_interrupt)
+		mp_interrupt->enable = TRUE;
+	else
+		mp_interrupt->enable = FALSE;
+
+	if (deserialized_driver(wnd)) {
+		KeInitializeDpc(&wnd->irq_kdpc,
+				WIN_FUNC_PTR(deserialized_irq_handler,4),
+				nmb->wnd);
+		wnd->irq_kdpc.arg1 = mp->handle_interrupt;
+		wnd->irq_kdpc.arg2 = mp;
+		TRACE2("%p, %p, %p, %p", wnd->irq_kdpc.arg1, wnd->irq_kdpc.arg2,
+		       nmb->wnd, nmb->mp_ctx);
+	} else {
+		KeInitializeDpc(&wnd->irq_kdpc,
+				WIN_FUNC_PTR(serialized_irq_handler,4),
+				nmb->wnd);
+		wnd->irq_kdpc.arg1 = mp->handle_interrupt;
+		wnd->irq_kdpc.arg2 = nmb->mp_ctx;
+		TRACE2("%p, %p, %p", wnd->irq_kdpc.arg1, wnd->irq_kdpc.arg2,
+		       nmb->wnd);
+	}
+
+	if (IoConnectInterrupt(&mp_interrupt->kinterrupt,
+			       WIN_FUNC_PTR(ndis_isr,2), mp_interrupt, NULL,
+			       vector, DIRQL, DIRQL, mode, shared, 0, FALSE) !=
+	    STATUS_SUCCESS) {
+		printk(KERN_WARNING "%s: request for IRQ %d failed\n",
+		       DRIVER_NAME, vector);
+		return NDIS_STATUS_RESOURCES;
+	}
+	printk(KERN_INFO "%s: using IRQ %d\n", DRIVER_NAME, vector);
+	EXIT1(return NDIS_STATUS_SUCCESS);
+}
+
+wstdcall void WIN_FUNC(NdisMDeregisterInterrupt,1)
+	(struct ndis_mp_interrupt *mp_interrupt)
+{
+	struct ndis_mp_block *nmb;
+
+	ENTER1("%p", mp_interrupt);
+	nmb = xchg(&mp_interrupt->nmb, NULL);
+	TRACE1("%p", nmb);
+	if (!nmb) {
+		WARNING("interrupt already freed?");
+		return;
+	}
+	nmb->wnd->mp_interrupt = NULL;
+	if (dequeue_kdpc(&nmb->wnd->irq_kdpc))
+		TRACE2("interrupt kdpc was pending");
+	flush_workqueue(wrapndis_wq);
+	IoDisconnectInterrupt(mp_interrupt->kinterrupt);
+	EXIT1(return);
+}
+
+wstdcall BOOLEAN WIN_FUNC(NdisMSynchronizeWithInterrupt,3)
+	(struct ndis_mp_interrupt *mp_interrupt,
+	 PKSYNCHRONIZE_ROUTINE sync_func, void *ctx)
+{
+	return KeSynchronizeExecution(mp_interrupt->kinterrupt, sync_func, ctx);
+}
+
+/* called via function pointer; but 64-bit RNDIS driver calls directly */
+wstdcall void WIN_FUNC(NdisMIndicateStatus,4)
+	(struct ndis_mp_block *nmb, NDIS_STATUS status, void *buf, UINT len)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	struct ndis_status_indication *si;
+
+	ENTER2("status=0x%x len=%d", status, len);
+	switch (status) {
+	case NDIS_STATUS_MEDIA_CONNECT:
+		set_media_state(wnd, NdisMediaStateConnected);
+		break;
+	case NDIS_STATUS_MEDIA_DISCONNECT:
+		set_media_state(wnd, NdisMediaStateDisconnected);
+		break;
+	case NDIS_STATUS_MEDIA_SPECIFIC_INDICATION:
+		if (!buf)
+			break;
+		si = buf;
+		TRACE2("status_type=%d", si->status_type);
+		switch (si->status_type) {
+		case Ndis802_11StatusType_MediaStreamMode:
+			break;
+#ifdef CONFIG_WIRELESS_EXT
+		case Ndis802_11StatusType_Authentication:
+			buf = (char *)buf + sizeof(*si);
+			len -= sizeof(*si);
+			while (len > 0) {
+				int pairwise_error = 0, group_error = 0;
+				struct ndis_auth_req *auth_req =
+					(struct ndis_auth_req *)buf;
+				TRACE1(MACSTRSEP, MAC2STR(auth_req->bssid));
+				if (auth_req->flags & 0x01)
+					TRACE2("reauth request");
+				if (auth_req->flags & 0x02)
+					TRACE2("key update request");
+				if (auth_req->flags & 0x06) {
+					pairwise_error = 1;
+					TRACE2("pairwise_error");
+				}
+				if (auth_req->flags & 0x0E) {
+					group_error = 1;
+					TRACE2("group_error");
+				}
+				if (pairwise_error || group_error) {
+					union iwreq_data wrqu;
+					struct iw_michaelmicfailure micfailure;
+
+					memset(&micfailure, 0, sizeof(micfailure));
+					if (pairwise_error)
+						micfailure.flags |=
+							IW_MICFAILURE_PAIRWISE;
+					if (group_error)
+						micfailure.flags |=
+							IW_MICFAILURE_GROUP;
+					memcpy(micfailure.src_addr.sa_data,
+					       auth_req->bssid, ETH_ALEN);
+					memset(&wrqu, 0, sizeof(wrqu));
+					wrqu.data.length = sizeof(micfailure);
+					wireless_send_event(wnd->net_dev,
+							    IWEVMICHAELMICFAILURE,
+							    &wrqu, (u8 *)&micfailure);
+				}
+				len -= auth_req->length;
+				buf = (char *)buf + auth_req->length;
+			}
+			break;
+		case Ndis802_11StatusType_PMKID_CandidateList:
+		{
+			u8 *end;
+			unsigned long i;
+			struct ndis_pmkid_candidate_list *cand;
+
+			cand = buf + sizeof(struct ndis_status_indication);
+			if (len < sizeof(struct ndis_status_indication) +
+			    sizeof(struct ndis_pmkid_candidate_list) ||
+				cand->version != 1) {
+				WARNING("unrecognized PMKID ignored");
+				EXIT1(return);
+			}
+
+			end = (u8 *)buf + len;
+			TRACE2("PMKID ver %d num_cand %d",
+			       cand->version, cand->num_candidates);
+			for (i = 0; i < cand->num_candidates; i++) {
+				struct iw_pmkid_cand pcand;
+				union iwreq_data wrqu;
+				struct ndis_pmkid_candidate *c =
+					&cand->candidates[i];
+				if ((u8 *)(c + 1) > end) {
+					TRACE2("truncated PMKID");
+					break;
+				}
+				TRACE2("%ld: " MACSTRSEP " 0x%x",
+				       i, MAC2STR(c->bssid), c->flags);
+				memset(&pcand, 0, sizeof(pcand));
+				if (c->flags & 0x01)
+					pcand.flags |= IW_PMKID_CAND_PREAUTH;
+				pcand.index = i;
+				memcpy(pcand.bssid.sa_data, c->bssid, ETH_ALEN);
+
+				memset(&wrqu, 0, sizeof(wrqu));
+				wrqu.data.length = sizeof(pcand);
+				wireless_send_event(wnd->net_dev, IWEVPMKIDCAND,
+						    &wrqu, (u8 *)&pcand);
+			}
+			break;
+		}
+		case Ndis802_11StatusType_RadioState:
+		{
+			struct ndis_radio_status_indication *radio_status = buf;
+			if (radio_status->radio_state ==
+			    Ndis802_11RadioStatusOn)
+				INFO("radio is turned on");
+			else if (radio_status->radio_state ==
+				 Ndis802_11RadioStatusHardwareOff)
+				INFO("radio is turned off by hardware");
+			else if (radio_status->radio_state ==
+				 Ndis802_11RadioStatusSoftwareOff)
+				INFO("radio is turned off by software");
+			break;
+		}
+#endif
+		default:
+			/* is this RSSI indication? */
+			TRACE2("unknown indication: %x", si->status_type);
+			break;
+		}
+		break;
+	default:
+		TRACE2("unknown status: %08X", status);
+		break;
+	}
+
+	EXIT2(return);
+}
+
+/* called via function pointer; but 64-bit RNDIS driver calls directly */
+wstdcall void WIN_FUNC(NdisMIndicateStatusComplete,1)
+	(struct ndis_mp_block *nmb)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	ENTER2("%p", wnd);
+	if (wnd->tx_ok)
+		queue_work(wrapndis_wq, &wnd->tx_work);
+}
+
+/* called via function pointer */
+wstdcall void NdisMSendComplete(struct ndis_mp_block *nmb,
+				struct ndis_packet *packet, NDIS_STATUS status)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	ENTER4("%p, %08X", packet, status);
+	assert_irql(_irql_ <= DISPATCH_LEVEL);
+	if (deserialized_driver(wnd))
+		free_tx_packet(wnd, packet, status);
+	else {
+		struct ndis_packet_oob_data *oob_data;
+		NDIS_STATUS pkt_status;
+		TRACE3("%p, %08x", packet, status);
+		oob_data = NDIS_PACKET_OOB_DATA(packet);
+		switch ((pkt_status = xchg(&oob_data->status, status))) {
+		case NDIS_STATUS_NOT_RECOGNIZED:
+			free_tx_packet(wnd, packet, status);
+			break;
+		case NDIS_STATUS_PENDING:
+		case 0:
+			break;
+		default:
+			WARNING("%p: invalid status: %08X", packet, pkt_status);
+			break;
+		}
+		/* In case a serialized driver has earlier requested a
+		 * pause by returning NDIS_STATUS_RESOURCES during
+		 * MiniportSend(Packets), wakeup tx worker now.
+		 */
+		if (xchg(&wnd->tx_ok, 1) == 0) {
+			TRACE3("%d, %d", wnd->tx_ring_start, wnd->tx_ring_end);
+			queue_work(wrapndis_wq, &wnd->tx_work);
+		}
+	}
+	EXIT3(return);
+}
+
+/* called via function pointer */
+wstdcall void NdisMSendResourcesAvailable(struct ndis_mp_block *nmb)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	ENTER3("%d, %d", wnd->tx_ring_start, wnd->tx_ring_end);
+	wnd->tx_ok = 1;
+	queue_work(wrapndis_wq, &wnd->tx_work);
+	EXIT3(return);
+}
+
+wstdcall void return_packet(void *arg1, void *arg2)
+{
+	struct ndis_device *wnd;
+	struct ndis_packet *packet;
+	struct miniport *mp;
+	KIRQL irql;
+
+	wnd = arg1;
+	packet = arg2;
+	ENTER4("%p, %p", wnd, packet);
+	mp = &wnd->wd->driver->ndis_driver->mp;
+	irql = serialize_lock_irql(wnd);
+	assert_irql(_irql_ == DISPATCH_LEVEL);
+	LIN2WIN2(mp->return_packet, wnd->nmb->mp_ctx, packet);
+	serialize_unlock_irql(wnd, irql);
+	EXIT4(return);
+}
+WIN_FUNC_DECL(return_packet,2)
+
+/* called via function pointer */
+wstdcall void NdisMIndicateReceivePacket(struct ndis_mp_block *nmb,
+					 struct ndis_packet **packets,
+					 UINT nr_packets)
+{
+	struct ndis_device *wnd;
+	ndis_buffer *buffer;
+	struct ndis_packet *packet;
+	struct sk_buff *skb;
+	ULONG i, length, total_length;
+	struct ndis_packet_oob_data *oob_data;
+	void *virt;
+	struct ndis_tcp_ip_checksum_packet_info csum;
+
+	ENTER3("%p, %d", nmb, nr_packets);
+	assert_irql(_irql_ <= DISPATCH_LEVEL);
+	wnd = nmb->wnd;
+	for (i = 0; i < nr_packets; i++) {
+		packet = packets[i];
+		if (!packet) {
+			WARNING("empty packet ignored");
+			continue;
+		}
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,11,0)
+		wnd->net_dev->last_rx = jiffies;
+#endif
+		/* get total number of bytes in packet */
+		NdisGetFirstBufferFromPacketSafe(packet, &buffer, &virt,
+						 &length, &total_length,
+						 NormalPagePriority);
+		TRACE3("%d, %d", length, total_length);
+		oob_data = NDIS_PACKET_OOB_DATA(packet);
+		TRACE3("0x%x, 0x%x, %llu", packet->private.flags,
+		       packet->private.packet_flags, oob_data->time_rxed);
+		skb = dev_alloc_skb(total_length);
+		if (skb) {
+			while (buffer) {
+				memcpy_skb(skb, MmGetSystemAddressForMdl(buffer),
+					   MmGetMdlByteCount(buffer));
+				buffer = buffer->next;
+			}
+			skb->dev = wnd->net_dev;
+			skb->protocol = eth_type_trans(skb, wnd->net_dev);
+			pre_atomic_add(wnd->net_stats.rx_bytes, total_length);
+			atomic_inc_var(wnd->net_stats.rx_packets);
+			csum.value = (typeof(csum.value))(ULONG_PTR)
+				oob_data->ext.info[TcpIpChecksumPacketInfo];
+			TRACE3("0x%05x", csum.value);
+			if (wnd->rx_csum.value &&
+			    (csum.rx.tcp_succeeded || csum.rx.udp_succeeded ||
+			     csum.rx.ip_succeeded))
+				skb->ip_summed = CHECKSUM_UNNECESSARY;
+			else
+				skb->ip_summed = CHECKSUM_NONE;
+
+			if (in_interrupt())
+				netif_rx(skb);
+			else
+				netif_rx_ni(skb);
+		} else {
+			WARNING("couldn't allocate skb; packet dropped");
+			atomic_inc_var(wnd->net_stats.rx_dropped);
+		}
+
+		/* serialized drivers check the status upon return
+		 * from this function */
+		if (!deserialized_driver(wnd)) {
+			oob_data->status = NDIS_STATUS_SUCCESS;
+			continue;
+		}
+
+		/* if a deserialized driver sets
+		 * NDIS_STATUS_RESOURCES, then it reclaims the packet
+		 * upon return from this function */
+		if (oob_data->status == NDIS_STATUS_RESOURCES)
+			continue;
+
+		assert(oob_data->status == NDIS_STATUS_SUCCESS);
+		/* deserialized driver doesn't check the status upon
+		 * return from this function; we need to call
+		 * MiniportReturnPacket later for this packet. Calling
+		 * MiniportReturnPacket from here is not correct - the
+		 * driver doesn't expect it (at least Centrino driver
+		 * crashes) */
+		schedule_ntos_work_item(WIN_FUNC_PTR(return_packet,2),
+					wnd, packet);
+	}
+	EXIT3(return);
+}
+
+/* called via function pointer (by NdisMEthIndicateReceive macro); the
+ * first argument is nmb->eth_db */
+wstdcall void EthRxIndicateHandler(struct ndis_mp_block *nmb, void *rx_ctx,
+				   char *header1, char *header, UINT header_size,
+				   void *look_ahead, UINT look_ahead_size,
+				   UINT packet_size)
+{
+	struct sk_buff *skb = NULL;
+	struct ndis_device *wnd;
+	unsigned int skb_size = 0;
+	KIRQL irql;
+	struct ndis_packet_oob_data *oob_data;
+
+	ENTER3("nmb = %p, rx_ctx = %p, buf = %p, size = %d, buf = %p, "
+	       "size = %d, packet = %d", nmb, rx_ctx, header, header_size,
+	       look_ahead, look_ahead_size, packet_size);
+
+	wnd = nmb->wnd;
+	TRACE3("wnd = %p", wnd);
+	if (!wnd) {
+		ERROR("nmb is NULL");
+		EXIT3(return);
+	}
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,11,0)
+	wnd->net_dev->last_rx = jiffies;
+#endif
+
+	if (look_ahead_size < packet_size) {
+		struct ndis_packet *packet;
+		struct miniport *mp;
+		unsigned int bytes_txed;
+		NDIS_STATUS res;
+
+		NdisAllocatePacket(&res, &packet, wnd->tx_packet_pool);
+		if (res != NDIS_STATUS_SUCCESS) {
+			atomic_inc_var(wnd->net_stats.rx_dropped);
+			EXIT3(return);
+		}
+		oob_data = NDIS_PACKET_OOB_DATA(packet);
+		mp = &wnd->wd->driver->ndis_driver->mp;
+		irql = serialize_lock_irql(wnd);
+		assert_irql(_irql_ == DISPATCH_LEVEL);
+		res = LIN2WIN6(mp->tx_data, packet, &bytes_txed, nmb,
+			       rx_ctx, look_ahead_size, packet_size);
+		serialize_unlock_irql(wnd, irql);
+		TRACE3("%d, %d, %d", header_size, look_ahead_size, bytes_txed);
+		if (res == NDIS_STATUS_SUCCESS) {
+			ndis_buffer *buffer;
+			struct ndis_tcp_ip_checksum_packet_info csum;
+			skb = dev_alloc_skb(header_size + look_ahead_size +
+					    bytes_txed);
+			if (!skb) {
+				ERROR("couldn't allocate skb; packet dropped");
+				atomic_inc_var(wnd->net_stats.rx_dropped);
+				NdisFreePacket(packet);
+				return;
+			}
+			memcpy_skb(skb, header, header_size);
+			memcpy_skb(skb, look_ahead, look_ahead_size);
+			buffer = packet->private.buffer_head;
+			while (buffer) {
+				memcpy_skb(skb,
+					   MmGetSystemAddressForMdl(buffer),
+					   MmGetMdlByteCount(buffer));
+				buffer = buffer->next;
+			}
+			skb_size = header_size + look_ahead_size + bytes_txed;
+			csum.value = (typeof(csum.value))(ULONG_PTR)
+				oob_data->ext.info[TcpIpChecksumPacketInfo];
+			TRACE3("0x%05x", csum.value);
+			if (wnd->rx_csum.value &&
+			    (csum.rx.tcp_succeeded || csum.rx.udp_succeeded))
+				skb->ip_summed = CHECKSUM_UNNECESSARY;
+			else
+				skb->ip_summed = CHECKSUM_NONE;
+			NdisFreePacket(packet);
+		} else if (res == NDIS_STATUS_PENDING) {
+			/* driver will call td_complete */
+			oob_data->look_ahead = kmalloc(look_ahead_size,
+						       GFP_ATOMIC);
+			if (!oob_data->look_ahead) {
+				NdisFreePacket(packet);
+				ERROR("packet dropped");
+				atomic_inc_var(wnd->net_stats.rx_dropped);
+				EXIT3(return);
+			}
+			assert(sizeof(oob_data->header) == header_size);
+			memcpy(oob_data->header, header,
+			       sizeof(oob_data->header));
+			memcpy(oob_data->look_ahead, look_ahead,
+			       look_ahead_size);
+			oob_data->look_ahead_size = look_ahead_size;
+			EXIT3(return);
+		} else {
+			WARNING("packet dropped: %08X", res);
+			atomic_inc_var(wnd->net_stats.rx_dropped);
+			NdisFreePacket(packet);
+			EXIT3(return);
+		}
+	} else {
+		skb_size = header_size + packet_size;
+		skb = dev_alloc_skb(skb_size);
+		if (skb) {
+			memcpy_skb(skb, header, header_size);
+			memcpy_skb(skb, look_ahead, packet_size);
+		}
+	}
+
+	if (skb) {
+		skb->dev = wnd->net_dev;
+		skb->protocol = eth_type_trans(skb, wnd->net_dev);
+		pre_atomic_add(wnd->net_stats.rx_bytes, skb_size);
+		atomic_inc_var(wnd->net_stats.rx_packets);
+		if (in_interrupt())
+			netif_rx(skb);
+		else
+			netif_rx_ni(skb);
+	}
+
+	EXIT3(return);
+}
+
+/* called via function pointer */
+wstdcall void NdisMTransferDataComplete(struct ndis_mp_block *nmb,
+					struct ndis_packet *packet,
+					NDIS_STATUS status, UINT bytes_txed)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	struct sk_buff *skb;
+	unsigned int skb_size;
+	struct ndis_packet_oob_data *oob_data;
+	ndis_buffer *buffer;
+	struct ndis_tcp_ip_checksum_packet_info csum;
+
+	ENTER3("wnd = %p, packet = %p, bytes_txed = %d",
+	       wnd, packet, bytes_txed);
+	if (!packet) {
+		WARNING("illegal packet");
+		EXIT3(return);
+	}
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,11,0)
+	wnd->net_dev->last_rx = jiffies;
+#endif
+	oob_data = NDIS_PACKET_OOB_DATA(packet);
+	skb_size = sizeof(oob_data->header) + oob_data->look_ahead_size +
+		bytes_txed;
+	skb = dev_alloc_skb(skb_size);
+	if (!skb) {
+		kfree(oob_data->look_ahead);
+		NdisFreePacket(packet);
+		ERROR("couldn't allocate skb; packet dropped");
+		atomic_inc_var(wnd->net_stats.rx_dropped);
+		EXIT3(return);
+	}
+	memcpy_skb(skb, oob_data->header, sizeof(oob_data->header));
+	memcpy_skb(skb, oob_data->look_ahead, oob_data->look_ahead_size);
+	buffer = packet->private.buffer_head;
+	while (buffer) {
+		memcpy_skb(skb, MmGetSystemAddressForMdl(buffer),
+			   MmGetMdlByteCount(buffer));
+		buffer = buffer->next;
+	}
+	kfree(oob_data->look_ahead);
+	NdisFreePacket(packet);
+	skb->dev = wnd->net_dev;
+	skb->protocol = eth_type_trans(skb, wnd->net_dev);
+	pre_atomic_add(wnd->net_stats.rx_bytes, skb_size);
+	atomic_inc_var(wnd->net_stats.rx_packets);
+
+	csum.value = (typeof(csum.value))(ULONG_PTR)
+		oob_data->ext.info[TcpIpChecksumPacketInfo];
+	TRACE3("0x%05x", csum.value);
+	if (wnd->rx_csum.value &&
+	    (csum.rx.tcp_succeeded || csum.rx.udp_succeeded))
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	else
+		skb->ip_summed = CHECKSUM_NONE;
+
+	if (in_interrupt())
+		netif_rx(skb);
+	else
+		netif_rx_ni(skb);
+}
+
+/* called via function pointer */
+wstdcall void EthRxComplete(struct ndis_mp_block *nmb)
+{
+	TRACE3("");
+}
+
+/* called via function pointer */
+wstdcall void NdisMQueryInformationComplete(struct ndis_mp_block *nmb,
+					    NDIS_STATUS status)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	typeof(wnd->ndis_req_task) task;
+
+	ENTER2("nmb: %p, wnd: %p, %08X", nmb, wnd, status);
+	wnd->ndis_req_status = status;
+	wnd->ndis_req_done = 1;
+	if ((task = xchg(&wnd->ndis_req_task, NULL)))
+		wake_up_process(task);
+	else
+		WARNING("invalid task");
+	EXIT2(return);
+}
+
+/* called via function pointer */
+wstdcall void NdisMSetInformationComplete(struct ndis_mp_block *nmb,
+					  NDIS_STATUS status)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	typeof(wnd->ndis_req_task) task;
+
+	ENTER2("status = %08X", status);
+	wnd->ndis_req_status = status;
+	wnd->ndis_req_done = 1;
+	if ((task = xchg(&wnd->ndis_req_task, NULL)))
+		wake_up_process(task);
+	else
+		WARNING("invalid task");
+	EXIT2(return);
+}
+
+/* called via function pointer */
+wstdcall void NdisMResetComplete(struct ndis_mp_block *nmb,
+				 NDIS_STATUS status, BOOLEAN address_reset)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	typeof(wnd->ndis_req_task) task;
+
+	ENTER2("status: %08X, %u", status, address_reset);
+	wnd->ndis_req_status = status;
+	wnd->ndis_req_done = address_reset + 1;
+	if ((task = xchg(&wnd->ndis_req_task, NULL)))
+		wake_up_process(task);
+	else
+		WARNING("invalid task");
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(NdisMSleep,1)
+	(ULONG us)
+{
+	unsigned long delay;
+
+	ENTER4("%p: us: %u", current, us);
+	delay = USEC_TO_HZ(us);
+	sleep_hz(delay);
+	TRACE4("%p: done", current);
+}
+
+wstdcall void WIN_FUNC(NdisGetCurrentSystemTime,1)
+	(LARGE_INTEGER *time)
+{
+	*time = ticks_1601();
+	TRACE5("%llu, %lu", *time, jiffies);
+}
+
+wstdcall LONG WIN_FUNC(NdisInterlockedDecrement,1)
+	(LONG *val)
+{
+	return InterlockedDecrement(val);
+}
+
+wstdcall LONG WIN_FUNC(NdisInterlockedIncrement,1)
+	(LONG *val)
+{
+	return InterlockedIncrement(val);
+}
+
+wstdcall struct nt_list *WIN_FUNC(NdisInterlockedInsertHeadList,3)
+	(struct nt_list *head, struct nt_list *entry,
+	 struct ndis_spinlock *lock)
+{
+	return ExInterlockedInsertHeadList(head, entry, &lock->klock);
+}
+
+wstdcall struct nt_list *WIN_FUNC(NdisInterlockedInsertTailList,3)
+	(struct nt_list *head, struct nt_list *entry,
+	 struct ndis_spinlock *lock)
+{
+	return ExInterlockedInsertTailList(head, entry, &lock->klock);
+}
+
+wstdcall struct nt_list *WIN_FUNC(NdisInterlockedRemoveHeadList,2)
+	(struct nt_list *head, struct ndis_spinlock *lock)
+{
+	return ExInterlockedRemoveHeadList(head, &lock->klock);
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMInitializeScatterGatherDma,3)
+	(struct ndis_mp_block *nmb, BOOLEAN dma64_supported, ULONG max_phy_map)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	ENTER2("dma64_supported=%d, maxtransfer=%u", dma64_supported,
+	       max_phy_map);
+	if (!wrap_is_pci_bus(wnd->wd->dev_bus)) {
+		ERROR("used on a non-PCI device");
+		return NDIS_STATUS_NOT_SUPPORTED;
+	}
+#ifdef CONFIG_X86_64
+	if (!dma64_supported) {
+		TRACE1("64-bit DMA size is not supported");
+		if (pci_set_dma_mask(wnd->wd->pci.pdev, DMA_BIT_MASK(32)) ||
+		    pci_set_consistent_dma_mask(wnd->wd->pci.pdev,
+						DMA_BIT_MASK(32)))
+			WARNING("setting dma mask failed");
+	}
+#endif
+	if ((wnd->attributes & NDIS_ATTRIBUTE_BUS_MASTER) &&
+	    wrap_is_pci_bus(wnd->wd->dev_bus)) {
+		wnd->sg_dma_size = max_phy_map;
+		return NDIS_STATUS_SUCCESS;
+	} else
+		EXIT1(return NDIS_STATUS_NOT_SUPPORTED);
+}
+
+wstdcall ULONG WIN_FUNC(NdisMGetDmaAlignment,1)
+	(struct ndis_mp_block *nmb)
+{
+	ENTER3("");
+	return dma_get_cache_alignment();
+}
+
+wstdcall CHAR WIN_FUNC(NdisSystemProcessorCount,0)
+	(void)
+{
+	return num_online_cpus();
+}
+
+wstdcall void WIN_FUNC(NdisGetCurrentProcessorCounts,3)
+	(ULONG *idle, ULONG *kernel_user, ULONG *index)
+{
+	int cpu = smp_processor_id();
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)
+	*idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE];
+	*kernel_user = kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM] +
+		kcpustat_cpu(cpu).cpustat[CPUTIME_USER];
+#else
+	*idle = kstat_cpu(cpu).cpustat.idle;
+	*kernel_user = kstat_cpu(cpu).cpustat.system +
+		kstat_cpu(cpu).cpustat.user;
+#endif
+	*index = cpu;
+}
+
+wstdcall void WIN_FUNC(NdisInitializeEvent,1)
+	(struct ndis_event *ndis_event)
+{
+	EVENTENTER("%p", ndis_event);
+	KeInitializeEvent(&ndis_event->nt_event, NotificationEvent, 0);
+}
+
+wstdcall BOOLEAN WIN_FUNC(NdisWaitEvent,2)
+	(struct ndis_event *ndis_event, UINT ms)
+{
+	LARGE_INTEGER ticks;
+	NTSTATUS res;
+
+	EVENTENTER("%p %u", ndis_event, ms);
+	ticks = -((LARGE_INTEGER)ms * TICKSPERMSEC);
+	res = KeWaitForSingleObject(&ndis_event->nt_event, 0, 0, TRUE,
+				    ms == 0 ? NULL : &ticks);
+	if (res == STATUS_SUCCESS)
+		EXIT3(return TRUE);
+	else
+		EXIT3(return FALSE);
+}
+
+wstdcall void WIN_FUNC(NdisSetEvent,1)
+	(struct ndis_event *ndis_event)
+{
+	EVENTENTER("%p", ndis_event);
+	KeSetEvent(&ndis_event->nt_event, 0, 0);
+}
+
+wstdcall void WIN_FUNC(NdisResetEvent,1)
+	(struct ndis_event *ndis_event)
+{
+	EVENTENTER("%p", ndis_event);
+	KeResetEvent(&ndis_event->nt_event);
+}
+
+static void ndis_worker(struct work_struct *dummy)
+{
+	struct nt_list *ent;
+	struct ndis_work_item *ndis_work_item;
+
+	WORKENTER("");
+	while (1) {
+		spin_lock_bh(&ndis_work_list_lock);
+		ent = RemoveHeadList(&ndis_work_list);
+		spin_unlock_bh(&ndis_work_list_lock);
+		if (!ent)
+			break;
+		ndis_work_item = container_of(ent, struct ndis_work_item, list);
+		WORKTRACE("%p: %p, %p", ndis_work_item,
+			  ndis_work_item->func, ndis_work_item->ctx);
+		LIN2WIN2(ndis_work_item->func, ndis_work_item,
+			 ndis_work_item->ctx);
+		WORKTRACE("%p done", ndis_work_item);
+	}
+	WORKEXIT(return);
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisScheduleWorkItem,1)
+	(struct ndis_work_item *ndis_work_item)
+{
+	ENTER3("%p", ndis_work_item);
+	spin_lock_bh(&ndis_work_list_lock);
+	InsertTailList(&ndis_work_list, &ndis_work_item->list);
+	spin_unlock_bh(&ndis_work_list_lock);
+	WORKTRACE("scheduling %p", ndis_work_item);
+	queue_work(ndis_wq, &ndis_work);
+	EXIT3(return NDIS_STATUS_SUCCESS);
+}
+
+wstdcall void WIN_FUNC(NdisMGetDeviceProperty,6)
+	(struct ndis_mp_block *nmb, void **phy_dev, void **func_dev,
+	 void **next_dev, void **alloc_res, void**trans_res)
+{
+	ENTER2("nmb: %p, phy_dev = %p, func_dev = %p, next_dev = %p, "
+	       "alloc_res = %p, trans_res = %p", nmb, phy_dev, func_dev,
+	       next_dev, alloc_res, trans_res);
+	if (phy_dev)
+		*phy_dev = nmb->pdo;
+	if (func_dev)
+		*func_dev = nmb->fdo;
+	if (next_dev)
+		*next_dev = nmb->next_device;
+}
+
+wstdcall void WIN_FUNC(NdisMRegisterUnloadHandler,2)
+	(struct driver_object *drv_obj, void *unload)
+{
+	if (drv_obj)
+		drv_obj->unload = unload;
+	return;
+}
+
+wstdcall UINT WIN_FUNC(NdisGetVersion,0)
+	(void)
+{
+	return 0x00050001;
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMQueryAdapterInstanceName,2)
+	(struct unicode_string *name, struct ndis_mp_block *nmb)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	struct ansi_string ansi;
+
+	if (wrap_is_pci_bus(wnd->wd->dev_bus))
+		RtlInitAnsiString(&ansi, "PCI Ethernet Adapter");
+	else
+		RtlInitAnsiString(&ansi, "USB Ethernet Adapter");
+
+	if (RtlAnsiStringToUnicodeString(name, &ansi, TRUE))
+		EXIT2(return NDIS_STATUS_RESOURCES);
+	else
+		EXIT2(return NDIS_STATUS_SUCCESS);
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisWriteEventLogEntry,7)
+	(void *handle, NDIS_STATUS code, ULONG value, USHORT n,
+	 void *strings, ULONG datasize, void *data)
+{
+	TRACE1("0x%x, 0x%x, %u, %u", code, value, n, datasize);
+	return NDIS_STATUS_SUCCESS;
+}
+
+wstdcall void *WIN_FUNC(NdisGetRoutineAddress,1)
+	(struct unicode_string *unicode_string)
+{
+	struct ansi_string ansi_string;
+	void *address;
+
+	if (RtlUnicodeStringToAnsiString(&ansi_string, unicode_string, TRUE) !=
+	    STATUS_SUCCESS)
+		EXIT1(return NULL);
+	INFO("%s", ansi_string.buf);
+	address = ndis_get_routine_address(ansi_string.buf);
+	RtlFreeAnsiString(&ansi_string);
+	return address;
+}
+
+wstdcall ULONG WIN_FUNC(NdisReadPcmciaAttributeMemory,4)
+	(struct ndis_mp_block *nmb, ULONG offset, void *buffer,
+	 ULONG length)
+{
+	TODO();
+	return 0;
+}
+
+wstdcall ULONG WIN_FUNC(NdisWritePcmciaAttributeMemory,4)
+	(struct ndis_mp_block *nmb, ULONG offset, void *buffer,
+	 ULONG length)
+{
+	TODO();
+	return 0;
+}
+
+wstdcall void WIN_FUNC(NdisMCoIndicateReceivePacket,3)
+	(struct ndis_mp_block *nmb, struct ndis_packet **packets,
+	 UINT nr_packets)
+{
+	ENTER3("nmb = %p", nmb);
+	NdisMIndicateReceivePacket(nmb, packets, nr_packets);
+	EXIT3(return);
+}
+
+wstdcall void WIN_FUNC(NdisMCoSendComplete,3)
+	(NDIS_STATUS status, struct ndis_mp_block *nmb,
+	 struct ndis_packet *packet)
+{
+	ENTER3("%08x", status);
+	NdisMSendComplete(nmb, packet, status);
+	EXIT3(return);
+}
+
+wstdcall void WIN_FUNC(NdisMCoRequestComplete,3)
+	(NDIS_STATUS status, struct ndis_mp_block *nmb,
+	 struct ndis_request *ndis_request)
+{
+	struct ndis_device *wnd = nmb->wnd;
+	typeof(wnd->ndis_req_task) task;
+
+	ENTER3("%08X", status);
+	wnd->ndis_req_status = status;
+	wnd->ndis_req_done = 1;
+	if ((task = xchg(&wnd->ndis_req_task, NULL)))
+		wake_up_process(task);
+	else
+		WARNING("invalid task");
+	EXIT3(return);
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisIMNotifiyPnPEvent,2)
+	(struct ndis_mp_block *nmb, struct net_pnp_event *event)
+{
+	ENTER2("%p, %d", nmb, event->code);
+	/* NdisWrapper never calls protocol's pnp event notifier, so
+	 * nothing to do here */
+	EXIT2(return NDIS_STATUS_SUCCESS);
+}
+
+wstdcall void WIN_FUNC(NdisCompletePnPEvent,3)
+	(NDIS_STATUS status, void *handle, struct net_pnp_event *event)
+{
+	ENTER2("%d, %p, %d", status, handle, event->code);
+	/* NdisWrapper never calls protocol's pnp event notifier, so
+	 * nothing to do here */
+	EXIT2(return);
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMSetMiniportSecondary,2)
+	(struct ndis_mp_block *nmb2, struct ndis_mp_block *nmb1)
+{
+	ENTER3("%p, %p", nmb1, nmb2);
+	TODO();
+	EXIT3(return NDIS_STATUS_SUCCESS);
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMPromoteMiniport,1)
+	(struct ndis_mp_block *nmb)
+{
+	ENTER3("%p", nmb);
+	TODO();
+	EXIT3(return NDIS_STATUS_SUCCESS);
+}
+
+wstdcall void WIN_FUNC(NdisMCoActivateVcComplete,3)
+	(NDIS_STATUS status, void *handle, void *params)
+{
+	TODO();
+}
+
+wstdcall void WIN_FUNC(NdisMCoDeactivateVcComplete,2)
+	(NDIS_STATUS status, void *handle)
+{
+	TODO();
+}
+
+wstdcall void WIN_FUNC(NdisMRemoveMiniport,1)
+	(void *handle)
+{
+	TODO();
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMOpenLog,3)
+	(struct ndis_device *wnd, UINT size, void *handle)
+{
+	if (size >= sizeof(int))
+		*((int *)handle) = 42;
+	return NDIS_STATUS_SUCCESS;
+}
+
+wstdcall NDIS_STATUS WIN_FUNC(NdisMWriteLogData,3)
+	(void *handle, char *buffer, UINT buffer_size)
+{
+	TODO();
+	return NDIS_STATUS_SUCCESS;
+}
+
+wstdcall void WIN_FUNC(NdisMFlushLog,1)
+	(void *handle)
+{
+	TODO();
+}
+
+wstdcall void WIN_FUNC(NdisMCloseLog,1)
+	(void *handle)
+{
+	TODO();
+}
+
+static void *ndis_get_routine_address(char *name)
+{
+	int i;
+	ENTER2("%p", name);
+	for (i = 0; i < ARRAY_SIZE(ndis_exports); i++) {
+		if (strcmp(name, ndis_exports[i].name) == 0) {
+			TRACE2("%p", ndis_exports[i].func);
+			return ndis_exports[i].func;
+		}
+	}
+	EXIT2(return NULL);
+}
+
+/* ndis_init_device is called for each device */
+int ndis_init_device(struct ndis_device *wnd)
+{
+	struct ndis_mp_block *nmb = wnd->nmb;
+
+	KeInitializeSpinLock(&nmb->lock);
+	wnd->mp_interrupt = NULL;
+	wnd->wrap_timer_slist.next = NULL;
+	if (wnd->wd->driver->ndis_driver)
+		wnd->wd->driver->ndis_driver->mp.shutdown = NULL;
+
+	nmb->filterdbs.eth_db = nmb;
+	nmb->filterdbs.tr_db = nmb;
+	nmb->filterdbs.fddi_db = nmb;
+	nmb->filterdbs.arc_db = nmb;
+
+	nmb->rx_packet = WIN_FUNC_PTR(NdisMIndicateReceivePacket,3);
+	nmb->send_complete = WIN_FUNC_PTR(NdisMSendComplete,3);
+	nmb->send_resource_avail = WIN_FUNC_PTR(NdisMSendResourcesAvailable,1);
+	nmb->status = WIN_FUNC_PTR(NdisMIndicateStatus,4);
+	nmb->status_complete = WIN_FUNC_PTR(NdisMIndicateStatusComplete,1);
+	nmb->queryinfo_complete = WIN_FUNC_PTR(NdisMQueryInformationComplete,2);
+	nmb->setinfo_complete = WIN_FUNC_PTR(NdisMSetInformationComplete,2);
+	nmb->reset_complete = WIN_FUNC_PTR(NdisMResetComplete,3);
+	nmb->eth_rx_indicate = WIN_FUNC_PTR(EthRxIndicateHandler,8);
+	nmb->eth_rx_complete = WIN_FUNC_PTR(EthRxComplete,1);
+	nmb->td_complete = WIN_FUNC_PTR(NdisMTransferDataComplete,4);
+	return 0;
+}
+
+/* ndis_exit_device is called for each device */
+void ndis_exit_device(struct ndis_device *wnd)
+{
+	struct wrap_device_setting *setting;
+	ENTER2("%p", wnd);
+	mutex_lock(&loader_mutex);
+	nt_list_for_each_entry(setting, &wnd->wd->settings, list) {
+		struct ndis_configuration_parameter *param;
+		param = setting->encoded;
+		if (param) {
+			if (param->type == NdisParameterString)
+				RtlFreeUnicodeString(&param->data.string);
+			ExFreePool(param);
+			setting->encoded = NULL;
+		}
+	}
+	mutex_unlock(&loader_mutex);
+}
+
+/* ndis_init is called once when module is loaded */
+int ndis_init(void)
+{
+	InitializeListHead(&ndis_work_list);
+	spin_lock_init(&ndis_work_list_lock);
+	INIT_WORK(&ndis_work, ndis_worker);
+
+	ndis_wq = create_singlethread_workqueue("ndis_wq");
+	if (!ndis_wq) {
+		WARNING("couldn't create worker thread");
+		EXIT1(return -ENOMEM);
+	}
+
+	TRACE1("ndis_wq: %p", ndis_wq);
+	return 0;
+}
+
+/* ndis_exit is called once when module is removed */
+void ndis_exit(void)
+{
+	ENTER1("");
+	if (ndis_wq)
+		destroy_workqueue(ndis_wq);
+	EXIT1(return);
+}
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/ndis.h linux-5.6.11-ndis/3rdparty/ndiswrapper/ndis.h
--- linux-5.6.11/3rdparty/ndiswrapper/ndis.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/ndis.h	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,1309 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#ifndef _NDIS_H_
+#define _NDIS_H_
+
+#include "ntoskernel.h"
+
+//#define ALLOW_POOL_OVERFLOW 1
+
+#define NDIS_DMA_24BITS 0
+#define NDIS_DMA_32BITS 1
+#define NDIS_DMA_64BITS 2
+
+#ifdef CONFIG_X86_64
+#define MAXIMUM_PROCESSORS 64
+#else
+#define MAXIMUM_PROCESSORS 32
+#endif
+
+typedef UINT NDIS_STATUS;
+typedef UCHAR NDIS_DMA_SIZE;
+typedef LONG ndis_rssi;
+typedef ULONG ndis_key_index;
+typedef ULONG ndis_tx_power_level;
+typedef ULONGULONG ndis_key_rsc;
+typedef UCHAR mac_address[ETH_ALEN];
+typedef ULONG ndis_fragmentation_threshold;
+typedef ULONG ndis_rts_threshold;
+typedef ULONG ndis_antenna;
+typedef ULONG ndis_oid;
+
+typedef uint64_t NDIS_PHY_ADDRESS;
+
+struct ndis_sg_element {
+	PHYSICAL_ADDRESS address;
+	ULONG length;
+	ULONG_PTR reserved;
+};
+
+struct ndis_sg_list {
+	ULONG nent;
+	ULONG_PTR reserved;
+	struct ndis_sg_element elements[];
+};
+
+/* when sending packets, ndiswrapper associates exactly one sg element
+ * in sg list */
+struct wrap_tx_sg_list {
+	ULONG nent;
+	ULONG_PTR reserved;
+	struct ndis_sg_element elements[1];
+};
+
+struct ndis_phy_addr_unit {
+	NDIS_PHY_ADDRESS phy_addr;
+	UINT length;
+};
+
+typedef struct mdl ndis_buffer;
+
+struct ndis_buffer_pool {
+	ndis_buffer *free_descr;
+//	NT_SPIN_LOCK lock;
+	spinlock_t lock;
+	UINT max_descr;
+	UINT num_allocated_descr;
+};
+
+#define NDIS_PROTOCOL_ID_DEFAULT	0x00
+#define NDIS_PROTOCOL_ID_TCP_IP		0x02
+#define NDIS_PROTOCOL_ID_IPX		0x06
+#define NDIS_PROTOCOL_ID_NBF		0x07
+#define NDIS_PROTOCOL_ID_MAX		0x0F
+#define NDIS_PROTOCOL_ID_MASK		0x0F
+
+#define fPACKET_WRAPPER_RESERVED		0x3F
+#define fPACKET_CONTAINS_MEDIA_SPECIFIC_INFO	0x40
+#define fPACKET_ALLOCATED_BY_NDIS		0x80
+
+#define PROTOCOL_RESERVED_SIZE_IN_PACKET (4 * sizeof(void *))
+
+struct transport_header_offset {
+	USHORT protocol_type;
+	USHORT header_offset;
+};
+
+struct ndis_network_address {
+	USHORT length;
+	USHORT type;
+	UCHAR address[1];
+};
+
+struct ndis_network_address_list {
+	LONG count;
+	USHORT type;
+	struct ndis_network_address address[1];
+};
+
+struct ndis_tcp_ip_checksum_packet_info {
+	union {
+		struct {
+			ULONG v4:1;
+			ULONG v6:1;
+			ULONG tcp:1;
+			ULONG udp:1;
+			ULONG ip:1;
+		} tx;
+		struct {
+			ULONG tcp_failed:1;
+			ULONG udp_failed:1;
+			ULONG ip_failed:1;
+			ULONG tcp_succeeded:1;
+			ULONG udp_succeeded:1;
+			ULONG ip_succeeded:1;
+			ULONG loopback:1;
+		} rx;
+		ULONG value;
+	};
+};
+
+enum ndis_task {
+	TcpIpChecksumNdisTask, IpSecNdisTask, TcpLargeSendNdisTask, MaxNdisTask
+};
+
+enum ndis_encapsulation {
+	UNSPECIFIED_Encapsulation, NULL_Encapsulation,
+	IEEE_802_3_Encapsulation, IEEE_802_5_Encapsulation,
+	LLC_SNAP_ROUTED_Encapsulation, LLC_SNAP_BRIDGED_Encapsulation
+};
+
+#define NDIS_TASK_OFFLOAD_VERSION 1
+
+struct ndis_encapsulation_format {
+	enum ndis_encapsulation encap;
+	struct {
+		ULONG fixed_header_size:1;
+		ULONG reserved:31;
+	} flags;
+	ULONG header_size;
+};
+
+struct ndis_task_offload_header {
+	ULONG version;
+	ULONG size;
+	ULONG reserved;
+	ULONG offset_first_task;
+	struct ndis_encapsulation_format encap_format;
+};
+
+struct ndis_task_offload {
+	ULONG version;
+	ULONG size;
+	enum ndis_task task;
+	ULONG offset_next_task;
+	ULONG task_buf_length;
+	UCHAR task_buf[1];
+};
+
+struct v4_checksum {
+	union {
+		struct {
+			ULONG ip_opts:1;
+			ULONG tcp_opts:1;
+			ULONG tcp_csum:1;
+			ULONG udp_csum:1;
+			ULONG ip_csum:1;
+		};
+		ULONG value;
+	};
+};
+
+struct v6_checksum {
+	ULONG ip_supported:1;
+	ULONG tcp_supported:1;
+	ULONG tcp_csum:1;
+	ULONG udp_csum:1;
+};
+
+struct ndis_task_tcp_ip_checksum {
+	struct v4_checksum v4_tx;
+	struct v4_checksum v4_rx;
+	struct v6_checksum v6_tx;
+	struct v6_checksum v6_rx;
+};
+
+struct ndis_task_tcp_large_send {
+	ULONG version;
+	ULONG max_size;
+	ULONG min_seg_count;
+	BOOLEAN tcp_opts;
+	BOOLEAN ip_opts;
+};
+
+struct ndis_packet;
+
+struct ndis_packet_pool {
+	struct ndis_packet *free_descr;
+//	NT_SPIN_LOCK lock;
+	spinlock_t lock;
+	UINT max_descr;
+	UINT num_allocated_descr;
+	UINT num_used_descr;
+	UINT proto_rsvd_length;
+};
+
+struct ndis_packet_stack {
+	ULONG_PTR IM_reserved[2];
+	ULONG_PTR ndis_reserved[4];
+};
+
+enum ndis_per_packet_info {
+	TcpIpChecksumPacketInfo, IpSecPacketInfo, TcpLargeSendPacketInfo,
+	ClassificationHandlePacketInfo, NdisReserved,
+	ScatterGatherListPacketInfo, Ieee8021QInfo, OriginalPacketInfo,
+	PacketCancelId, MaxPerPacketInfo
+};
+
+struct ndis_packet_extension {
+	void *info[MaxPerPacketInfo];
+};
+
+struct ndis_packet_private {
+	UINT nr_pages;
+	UINT len;
+	ndis_buffer *buffer_head;
+	ndis_buffer *buffer_tail;
+	struct ndis_packet_pool *pool;
+	UINT count;
+	ULONG flags;
+	BOOLEAN valid_counts;
+	UCHAR packet_flags;
+	USHORT oob_offset;
+};
+
+struct ndis_packet {
+	struct ndis_packet_private private;
+	/* for use by miniport */
+	union {
+		/* for connectionless mininports */
+		struct {
+			UCHAR miniport_reserved[2 * sizeof(void *)];
+			UCHAR wrapper_reserved[2 * sizeof(void *)];
+		} cl_reserved;
+		/* for deserialized miniports */
+		struct {
+			UCHAR miniport_reserved_ex[3 * sizeof(void *)];
+			UCHAR wrapper_reserved_ex[sizeof(void *)];
+		} deserialized_reserved;
+		struct {
+			UCHAR mac_reserved[4 * sizeof(void *)];
+		} mac_reserved;
+	};
+	ULONG_PTR reserved[2];
+	UCHAR protocol_reserved[1];
+};
+
+/* OOB data */
+struct ndis_packet_oob_data {
+	union {
+		ULONGLONG time_to_tx;
+		ULONGLONG time_txed;
+	};
+	ULONGLONG time_rxed;
+	UINT header_size;
+	UINT media_size;
+	void *media;
+	NDIS_STATUS status;
+
+	/* ndiswrapper specific info; extension should be right after
+	 * ndis's oob_data */
+	struct ndis_packet_extension ext;
+	union {
+		/* used for tx only */
+		struct {
+			struct sk_buff *tx_skb;
+			union {
+				struct wrap_tx_sg_list wrap_tx_sg_list;
+				struct ndis_sg_list *tx_sg_list;
+			};
+		};
+		/* used for rx only */
+		struct {
+			unsigned char header[ETH_HLEN];
+			unsigned char *look_ahead;
+			UINT look_ahead_size;
+		};
+	};
+};
+
+#define NDIS_PACKET_OOB_DATA(packet)					\
+	(struct ndis_packet_oob_data *)(((void *)(packet)) +		\
+					(packet)->private.oob_offset)
+
+enum ndis_device_pnp_event {
+	NdisDevicePnPEventQueryRemoved, NdisDevicePnPEventRemoved,
+	NdisDevicePnPEventSurpriseRemoved, NdisDevicePnPEventQueryStopped,
+	NdisDevicePnPEventStopped, NdisDevicePnPEventPowerProfileChanged,
+	NdisDevicePnPEventMaximum
+};
+
+enum ndis_request_type {
+	NdisRequestQueryInformation, NdisRequestSetInformation,
+	NdisRequestQueryStatistics, NdisRequestOpen, NdisRequestClose,
+	NdisRequestSend, NdisRequestTransferData, NdisRequestReset,
+	NdisRequestGeneric1, NdisRequestGeneric2, NdisRequestGeneric3,
+	NdisRequestGeneric4
+};
+
+struct ndis_request {
+	mac_address mac;
+	enum ndis_request_type request_type;
+	union data {
+		struct query_info {
+			UINT oid;
+			void *buf;
+			UINT buf_len;
+			UINT written;
+			UINT needed;
+		} query_info;
+		struct set_info {
+			UINT oid;
+			void *buf;
+			UINT buf_len;
+			UINT written;
+			UINT needed;
+		} set_info;
+	} data;
+};
+
+enum ndis_medium {
+	NdisMedium802_3, NdisMedium802_5, NdisMediumFddi, NdisMediumWan,
+	NdisMediumLocalTalk, NdisMediumDix, NdisMediumArcnetRaw,
+	NdisMediumArcnet878_2, NdisMediumAtm, NdisMediumWirelessWan,
+	NdisMediumIrda, NdisMediumBpc, NdisMediumCoWan,
+	NdisMedium1394, NdisMediumMax
+};
+
+enum ndis_physical_medium {
+	NdisPhysicalMediumUnspecified, NdisPhysicalMediumWirelessLan,
+	NdisPhysicalMediumCableModem, NdisPhysicalMediumPhoneLine,
+	NdisPhysicalMediumPowerLine, NdisPhysicalMediumDSL,
+	NdisPhysicalMediumFibreChannel, NdisPhysicalMedium1394,
+	NdisPhysicalMediumWirelessWan, NdisPhysicalMediumMax
+};
+
+enum ndis_power_state {
+	NdisDeviceStateUnspecified = 0,
+	NdisDeviceStateD0, NdisDeviceStateD1, NdisDeviceStateD2,
+	NdisDeviceStateD3, NdisDeviceStateMaximum
+};
+
+enum ndis_power_profile {
+	NdisPowerProfileBattery, NdisPowerProfileAcOnLine
+};
+
+struct ndis_pm_wakeup_capabilities {
+	enum ndis_power_state min_magic_packet_wakeup;
+	enum ndis_power_state min_pattern_wakeup;
+	enum ndis_power_state min_link_change_wakeup;
+};
+
+#define NDIS_PNP_WAKE_UP_MAGIC_PACKET			0x00000001
+#define NDIS_PNP_WAKE_UP_PATTERN_MATCH			0x00000002
+#define NDIS_PNP_WAKE_UP_LINK_CHANGE			0x00000004
+
+enum net_pnp_event_code {
+	NetEventSetPower, NetEventQueryPower, NetEventQueryRemoveDevice,
+	NetEventCancelRemoveDevice, NetEventReconfigure, NetEventBindList,
+	NetEventBindsComplete, NetEventPnPCapabilities, NetEventMaximum
+};
+
+struct net_pnp_event {
+	enum net_pnp_event_code code;
+	void *buf;
+	ULONG buf_length;
+	ULONG_PTR ndis_reserved[4];
+	ULONG_PTR transport_reserved[4];
+	ULONG_PTR tdi_reserved[4];
+	ULONG_PTR tdi_client_reserved[4];
+};
+
+struct ndis_pnp_capabilities {
+	ULONG flags;
+	struct ndis_pm_wakeup_capabilities wakeup;
+};
+
+typedef void (*ndis_isr_handler)(BOOLEAN *recognized, BOOLEAN *queue_handler,
+				 void *handle) wstdcall;
+typedef void (*ndis_interrupt_handler)(void *ctx) wstdcall;
+
+struct miniport {
+	/* NDIS 3.0 */
+	UCHAR major_version;
+	UCHAR minor_version;
+	USHORT filler;
+	UINT reserved;
+	BOOLEAN (*hangcheck)(void *ctx) wstdcall;
+	void (*disable_interrupt)(void *ctx) wstdcall;
+	void (*enable_interrupt)(void *ctx) wstdcall;
+	void (*mp_halt)(void *ctx) wstdcall;
+	ndis_interrupt_handler handle_interrupt;
+	NDIS_STATUS (*init)(NDIS_STATUS *error_status, UINT *medium_index,
+			    enum ndis_medium medium[], UINT medium_array_size,
+			    void *handle, void *conf_handle) wstdcall;
+	ndis_isr_handler isr;
+	NDIS_STATUS (*queryinfo)(void *ctx, ndis_oid oid, void *buffer,
+			     ULONG buflen, ULONG *written,
+			     ULONG *needed) wstdcall;
+	void *reconfig;
+	NDIS_STATUS (*reset)(BOOLEAN *reset_address, void *ctx) wstdcall;
+	NDIS_STATUS (*send)(void *ctx, struct ndis_packet *packet,
+			    UINT flags) wstdcall;
+	NDIS_STATUS (*setinfo)(void *ctx, ndis_oid oid, void *buffer,
+			       ULONG buflen, ULONG *written,
+			       ULONG *needed) wstdcall;
+	NDIS_STATUS (*tx_data)(struct ndis_packet *ndis_packet,
+			       UINT *bytes_txed, void *mp_ctx, void *rx_ctx,
+			       UINT offset, UINT bytes_to_tx) wstdcall;
+	/* NDIS 4.0 extensions */
+	void (*return_packet)(void *ctx, void *packet) wstdcall;
+	void (*send_packets)(void *ctx, struct ndis_packet **packets,
+			     INT nr_of_packets) wstdcall;
+	void (*alloc_complete)(void *handle, void *virt,
+			       NDIS_PHY_ADDRESS *phys,
+			       ULONG size, void *ctx) wstdcall;
+	/* NDIS 5.0 extensions */
+	NDIS_STATUS (*co_create_vc)(void *ctx, void *vc_handle,
+				    void *vc_ctx) wstdcall;
+	NDIS_STATUS (*co_delete_vc)(void *vc_ctx) wstdcall;
+	NDIS_STATUS (*co_activate_vc)(void *vc_ctx, void *call_params) wstdcall;
+	NDIS_STATUS (*co_deactivate_vc)(void *vc_ctx) wstdcall;
+	NDIS_STATUS (*co_send_packets)(void *vc_ctx, void **packets,
+				       UINT nr_of_packets) wstdcall;
+	NDIS_STATUS (*co_request)(void *ctx, void *vc_ctx, UINT *req) wstdcall;
+	/* NDIS 5.1 extensions */
+	void (*cancel_send_packets)(void *ctx, void *id) wstdcall;
+	void (*pnp_event_notify)(void *ctx, enum ndis_device_pnp_event event,
+				 void *inf_buf, ULONG inf_buf_len) wstdcall;
+	void (*shutdown)(void *ctx) wstdcall;
+	void *reserved1;
+	void *reserved2;
+	void *reserved3;
+	void *reserved4;
+};
+
+struct ndis_spinlock {
+	NT_SPIN_LOCK klock;
+	KIRQL irql;
+};
+
+union ndis_rw_lock_refcount {
+	UCHAR cache_line[16];
+};
+
+struct ndis_rw_lock {
+	union {
+		struct {
+			NT_SPIN_LOCK klock;
+			void *context;
+		};
+		UCHAR reserved[16];
+	};
+	union {
+		union ndis_rw_lock_refcount ref_count[MAXIMUM_PROCESSORS];
+		/* ndiswrapper specific */
+		volatile int count;
+	};
+};
+
+struct lock_state {
+	USHORT state;
+	KIRQL irql;
+};
+
+struct ndis_work_item;
+typedef void (*NDIS_PROC)(struct ndis_work_item *, void *) wstdcall;
+
+struct ndis_work_item {
+	void *ctx;
+	NDIS_PROC func;
+	union {
+		UCHAR reserved[8 * sizeof(void *)];
+		/* ndiswrapper specific */
+		struct nt_list list;
+	};
+};
+
+struct alloc_shared_mem {
+	void *ctx;
+	ULONG size;
+	BOOLEAN cached;
+};
+
+struct ndis_mp_block;
+
+/* this is opaque to drivers, so we can use it as we please */
+struct ndis_mp_interrupt {
+	struct kinterrupt *kinterrupt;
+	NT_SPIN_LOCK lock;
+	union {
+		void *reserved;
+		unsigned int irq;
+	};
+	ndis_isr_handler isr;
+	ndis_interrupt_handler mp_dpc;
+	struct kdpc intr_dpc;
+	struct ndis_mp_block *nmb;
+	UCHAR dpc_count;
+	BOOLEAN enable;
+	struct nt_event dpc_completed_event;
+	BOOLEAN shared;
+	BOOLEAN req_isr;
+};
+
+struct ndis_binary_data {
+	USHORT len;
+	void *buf;
+};
+
+enum ndis_parameter_type {
+	NdisParameterInteger, NdisParameterHexInteger,
+	NdisParameterString, NdisParameterMultiString, NdisParameterBinary,
+};
+
+typedef struct unicode_string NDIS_STRING;
+
+struct ndis_configuration_parameter {
+	enum ndis_parameter_type type;
+	union {
+		ULONG integer;
+		NDIS_STRING string;
+	} data;
+};
+
+struct ndis_driver {
+	struct miniport mp;
+};
+
+/* IDs used to store extensions in driver_object's custom extension */
+#define NDIS_DRIVER_CLIENT_ID 10
+
+struct ndis_wireless_stats {
+	ULONG length;
+	LARGE_INTEGER tx_frag;
+	LARGE_INTEGER tx_multi_frag;
+	LARGE_INTEGER failed;
+	LARGE_INTEGER retry;
+	LARGE_INTEGER multi_retry;
+	LARGE_INTEGER rtss_succ;
+	LARGE_INTEGER rtss_fail;
+	LARGE_INTEGER ack_fail;
+	LARGE_INTEGER frame_dup;
+	LARGE_INTEGER rx_frag;
+	LARGE_INTEGER rx_multi_frag;
+	LARGE_INTEGER fcs_err;
+	LARGE_INTEGER tkip_local_mic_failures;
+	LARGE_INTEGER tkip_icv_errors;
+	LARGE_INTEGER tkip_counter_measures_invoked;
+	LARGE_INTEGER tkip_replays;
+	LARGE_INTEGER ccmp_format_errors;
+	LARGE_INTEGER ccmp_replays;
+	LARGE_INTEGER ccmp_decrypt_errors;
+	LARGE_INTEGER fourway_handshake_failures;
+	LARGE_INTEGER wep_undecryptable_count;
+	LARGE_INTEGER wep_icv_errorcount;
+	LARGE_INTEGER decrypt_success_count;
+	LARGE_INTEGER decrypt_failure_count;
+};
+
+enum ndis_status_type {
+	Ndis802_11StatusType_Authentication,
+	Ndis802_11StatusType_MediaStreamMode,
+	Ndis802_11StatusType_PMKID_CandidateList,
+	Ndis802_11StatusType_RadioState,
+};
+
+struct ndis_status_indication {
+	enum ndis_status_type status_type;
+};
+
+enum ndis_radio_status {
+	Ndis802_11RadioStatusOn, Ndis802_11RadioStatusHardwareOff,
+	Ndis802_11RadioStatusSoftwareOff,
+};
+
+struct ndis_radio_status_indication
+{
+	enum ndis_status_type status_type;
+	enum ndis_radio_status radio_state;
+};
+
+enum ndis_media_state {
+	NdisMediaStateConnected,
+	NdisMediaStateDisconnected,
+};
+
+enum ndis_media_stream_mode {
+	Ndis802_11MediaStreamOff, Ndis802_11MediaStreamOn
+};
+
+enum wrapper_work {
+	LINK_STATUS_OFF, LINK_STATUS_ON, SET_MULTICAST_LIST, COLLECT_IW_STATS,
+	HANGCHECK, NETIF_WAKEQ,
+};
+
+struct encr_info {
+	struct encr_key {
+		ULONG length;
+		UCHAR key[NDIS_ENCODING_TOKEN_MAX];
+	} keys[MAX_ENCR_KEYS];
+	unsigned short tx_key_index;
+};
+
+struct ndis_essid {
+	ULONG length;
+	UCHAR essid[NDIS_ESSID_MAX_SIZE];
+};
+
+enum ndis_infrastructure_mode {
+	Ndis802_11IBSS, Ndis802_11Infrastructure, Ndis802_11AutoUnknown,
+	Ndis802_11InfrastructureMax
+};
+
+enum authentication_mode {
+	Ndis802_11AuthModeOpen, Ndis802_11AuthModeShared,
+	Ndis802_11AuthModeAutoSwitch, Ndis802_11AuthModeWPA,
+	Ndis802_11AuthModeWPAPSK, Ndis802_11AuthModeWPANone,
+	Ndis802_11AuthModeWPA2, Ndis802_11AuthModeWPA2PSK,
+	Ndis802_11AuthModeMax
+};
+
+enum encryption_status {
+	Ndis802_11WEPEnabled,
+	Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+	Ndis802_11WEPDisabled,
+	Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+	Ndis802_11WEPKeyAbsent,
+	Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+	Ndis802_11WEPNotSupported,
+	Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+	Ndis802_11Encryption2Enabled, Ndis802_11Encryption2KeyAbsent,
+	Ndis802_11Encryption3Enabled, Ndis802_11Encryption3KeyAbsent
+};
+
+struct ndis_auth_encr_pair {
+	enum authentication_mode auth_mode;
+	enum encryption_status encr_mode;
+};
+
+struct ndis_capability {
+	ULONG length;
+	ULONG version;
+	ULONG num_PMKIDs;
+	ULONG num_auth_encr_pair;
+	struct ndis_auth_encr_pair auth_encr_pair[1];
+};
+
+struct ndis_guid {
+	struct guid guid;
+	union {
+		ndis_oid oid;
+		NDIS_STATUS status;
+	};
+	ULONG size;
+	ULONG flags;
+};
+
+struct ndis_timer {
+	struct nt_timer nt_timer;
+	struct kdpc kdpc;
+};
+
+struct ndis_mp_timer {
+	struct nt_timer nt_timer;
+	struct kdpc kdpc;
+	DPC func;
+	void *ctx;
+	struct ndis_mp_block *nmb;
+	struct ndis_mp_timer *next;
+};
+
+typedef struct cm_partial_resource_list NDIS_RESOURCE_LIST;
+
+struct ndis_event {
+	struct nt_event nt_event;
+};
+
+struct ndis_bind_paths {
+	UINT number;
+	struct unicode_string paths[1];
+};
+
+struct ndis_reference {
+	NT_SPIN_LOCK lock;
+	USHORT ref_count;
+	BOOLEAN closing;
+};
+
+struct ndis_filterdbs {
+	union {
+		void *eth_db;
+		void *null_db;
+	};
+	void *tr_db;
+	void *fddi_db;
+	void *arc_db;
+};
+
+enum ndis_interface_type {
+	NdisInterfaceInternal, NdisInterfaceIsa, NdisInterfaceEisa,
+	NdisInterfaceMca, NdisInterfaceTurboChannel, NdisInterfacePci,
+	NdisInterfacePcMcia,
+};
+
+struct auth_encr_capa {
+	unsigned long auth;
+	unsigned long encr;
+};
+
+struct ndis_pmkid_candidate {
+	mac_address bssid;
+	DWORD flags;
+};
+
+struct ndis_pmkid_candidate_list {
+	ULONG version;
+	ULONG num_candidates;
+	struct ndis_pmkid_candidate candidates[1];
+};
+
+/*
+ * This struct contains function pointers that the drivers references
+ * directly via macros, so it's important that they are at the correct
+ * position.
+ */
+struct ndis_mp_block {
+	void *signature;
+	struct ndis_mp_block *next;
+	struct driver_object *drv_obj;
+	void *mp_ctx;
+	struct unicode_string name;
+	struct ndis_bind_paths *bindpaths;
+	void *openqueue;
+	struct ndis_reference reference;
+	void *device_ctx;
+	UCHAR padding;
+	UCHAR lock_acquired;
+	UCHAR pmode_opens;
+	UCHAR assigned_cpu;
+	NT_SPIN_LOCK lock;
+	enum ndis_request_type *mediarequest;
+	struct ndis_mp_interrupt *interrupt;
+	ULONG flags;
+	ULONG pnp_flags;
+	struct nt_list packet_list;
+	struct ndis_packet *first_pending_tx_packet;
+	struct ndis_packet *return_packet_queue;
+	ULONG request_buffer;
+	void *set_mcast_buffer;
+	struct ndis_mp_block *primary_mp;
+	void *wrapper_ctx;
+	void *bus_data_ctx;
+	ULONG pnp_capa;
+	void *resources;
+	struct ndis_timer wakeup_dpc_timer;
+	struct unicode_string basename;
+	struct unicode_string symlink_name;
+	ULONG ndis_hangcheck_interval;
+	USHORT hangcheck_ticks;
+	USHORT hangcheck_tick;
+	NDIS_STATUS ndis_reset_status;
+	void *resetopen;
+	struct ndis_filterdbs filterdbs;
+	void *rx_packet;
+	void *send_complete;
+	void *send_resource_avail;
+	void *reset_complete;
+
+	enum ndis_medium media_type;
+	ULONG bus_number;
+	enum ndis_interface_type bus_type;
+	enum ndis_interface_type adapter_type;
+	struct device_object *fdo;
+	struct device_object *pdo;
+	struct device_object *next_device;
+	void *mapreg;
+	void *call_mgraflist;
+	void *mp_thread;
+	void *setinfobuf;
+	USHORT setinfo_buf_len;
+	USHORT max_send_pkts;
+	NDIS_STATUS fake_status;
+	void *lock_handler;
+	struct unicode_string *adapter_instance_name;
+	void *timer_queue;
+	UINT mac_options;
+	void *pending_req;
+	UINT max_long_addrs;
+	UINT max_short_addrs;
+	UINT cur_lookahead;
+	UINT max_lookahead;
+
+	ndis_interrupt_handler irq_bh;
+	void *disable_intr;
+	void *enable_intr;
+	void *send_pkts;
+	void *deferred_send;
+	void *eth_rx_indicate;
+	void *tr_rx_indicate;
+	void *fddi_rx_indicate;
+	void *eth_rx_complete;
+	void *tr_rx_complete;
+	void *fddi_rx_complete;
+
+	void *status;
+	void *status_complete;
+	void *td_complete;
+
+	void *queryinfo_complete;
+	void *setinfo_complete;
+	void *wan_tx_complete;
+	void *wan_rx;
+	void *wan_rx_complete;
+	/* ndiswrapper specific */
+	struct ndis_device *wnd;
+};
+
+struct ndis_device {
+	struct ndis_mp_block *nmb;
+	struct wrap_device *wd;
+	struct net_device *net_dev;
+	void *shutdown_ctx;
+	struct ndis_mp_interrupt *mp_interrupt;
+	struct kdpc irq_kdpc;
+	unsigned long mem_start;
+	unsigned long mem_end;
+
+	struct net_device_stats net_stats;
+	struct iw_statistics iw_stats;
+	BOOLEAN iw_stats_enabled;
+	struct ndis_wireless_stats ndis_stats;
+
+	struct work_struct tx_work;
+	struct ndis_packet *tx_ring[TX_RING_SIZE];
+	u8 tx_ring_start;
+	u8 tx_ring_end;
+	u8 is_tx_ring_full;
+	u8 tx_ok;
+	spinlock_t tx_ring_lock;
+	struct mutex tx_ring_mutex;
+	unsigned int max_tx_packets;
+	struct mutex ndis_req_mutex;
+	struct task_struct *ndis_req_task;
+	int ndis_req_done;
+	NDIS_STATUS ndis_req_status;
+	ULONG packet_filter;
+
+	ULONG sg_dma_size;
+	ULONG dma_map_count;
+	dma_addr_t *dma_map_addr;
+
+	int hangcheck_interval;
+	struct timer_list hangcheck_timer;
+	int iw_stats_interval;
+	struct timer_list iw_stats_timer;
+	unsigned long scan_timestamp;
+	struct encr_info encr_info;
+	char nick[IW_ESSID_MAX_SIZE + 1];
+	struct ndis_essid essid;
+	struct auth_encr_capa capa;
+	enum ndis_infrastructure_mode infrastructure_mode;
+	int max_pmkids;
+	int num_pmkids;
+	struct ndis_pmkid *pmkids;
+	mac_address mac;
+	struct proc_dir_entry *procfs_iface;
+
+	struct work_struct ndis_work;
+	unsigned long ndis_pending_work;
+	UINT attributes;
+	int iw_auth_wpa_version;
+	int iw_auth_cipher_pairwise;
+	int iw_auth_cipher_group;
+	int iw_auth_key_mgmt;
+	int iw_auth_80211_alg;
+	struct ndis_packet_pool *tx_packet_pool;
+	struct ndis_buffer_pool *tx_buffer_pool;
+	int multicast_size;
+	struct v4_checksum rx_csum;
+	struct v4_checksum tx_csum;
+	enum ndis_physical_medium physical_medium;
+	ULONG ndis_wolopts;
+	struct nt_slist wrap_timer_slist;
+	int drv_ndis_version;
+	struct ndis_pnp_capabilities pnp_capa;
+};
+
+BOOLEAN ndis_isr(struct kinterrupt *kinterrupt, void *ctx) wstdcall;
+
+int ndis_init(void);
+void ndis_exit(void);
+int ndis_init_device(struct ndis_device *wnd);
+void ndis_exit_device(struct ndis_device *wnd);
+
+int wrap_procfs_add_ndis_device(struct ndis_device *wnd);
+void wrap_procfs_remove_ndis_device(struct ndis_device *wnd);
+
+void NdisAllocatePacketPoolEx(NDIS_STATUS *status,
+			      struct ndis_packet_pool **pool_handle,
+			      UINT num_descr, UINT overflowsize,
+			      UINT proto_rsvd_length) wstdcall;
+void NdisFreePacketPool(struct ndis_packet_pool *pool) wstdcall;
+void NdisAllocatePacket(NDIS_STATUS *status, struct ndis_packet **packet,
+			struct ndis_packet_pool *pool) wstdcall;
+void NdisFreePacket(struct ndis_packet *descr) wstdcall;
+void NdisAllocateBufferPool(NDIS_STATUS *status,
+			    struct ndis_buffer_pool **pool_handle,
+			    UINT num_descr) wstdcall;
+void NdisFreeBufferPool(struct ndis_buffer_pool *pool) wstdcall;
+void NdisAllocateBuffer(NDIS_STATUS *status, ndis_buffer **buffer,
+			struct ndis_buffer_pool *pool, void *virt,
+			UINT length) wstdcall;
+void NdisFreeBuffer(ndis_buffer *descr) wstdcall;
+void NdisMIndicateReceivePacket(struct ndis_mp_block *nmb,
+				struct ndis_packet **packets,
+				UINT nr_packets) wstdcall;
+void NdisMSendComplete(struct ndis_mp_block *nmb, struct ndis_packet *packet,
+		       NDIS_STATUS status) wstdcall;
+void NdisMSendResourcesAvailable(struct ndis_mp_block *nmb) wstdcall;
+void NdisMIndicateStatus(struct ndis_mp_block *nmb,
+			 NDIS_STATUS status, void *buf, UINT len) wstdcall;
+void NdisMIndicateStatusComplete(struct ndis_mp_block *nmb) wstdcall;
+void NdisMQueryInformationComplete(struct ndis_mp_block *nmb,
+				   NDIS_STATUS status) wstdcall;
+void NdisMSetInformationComplete(struct ndis_mp_block *nmb,
+				 NDIS_STATUS status) wstdcall;
+void NdisMResetComplete(struct ndis_mp_block *nmb, NDIS_STATUS status,
+			BOOLEAN address_reset) wstdcall;
+ULONG NDIS_BUFFER_TO_SPAN_PAGES(ndis_buffer *buffer) wstdcall;
+BOOLEAN NdisWaitEvent(struct ndis_event *event, UINT timeout) wstdcall;
+void NdisSetEvent(struct ndis_event *event) wstdcall;
+void NdisMDeregisterInterrupt(struct ndis_mp_interrupt *mp_interrupt) wstdcall;
+void EthRxIndicateHandler(struct ndis_mp_block *nmb, void *rx_ctx,
+			  char *header1, char *header, UINT header_size,
+			  void *look_ahead, UINT look_ahead_size,
+			  UINT packet_size) wstdcall;
+void EthRxComplete(struct ndis_mp_block *nmb) wstdcall;
+void NdisMTransferDataComplete(struct ndis_mp_block *nmb,
+			       struct ndis_packet *packet, NDIS_STATUS status,
+			       UINT bytes_txed) wstdcall;
+void NdisWriteConfiguration(NDIS_STATUS *status, struct ndis_mp_block *nmb,
+			    struct unicode_string *key,
+			    struct ndis_configuration_parameter *param) wstdcall;
+void NdisReadConfiguration(NDIS_STATUS *status,
+			   struct ndis_configuration_parameter **param,
+			   struct ndis_mp_block *nmb,
+			   struct unicode_string *key,
+			   enum ndis_parameter_type type) wstdcall;
+
+/* Required OIDs */
+#define OID_GEN_SUPPORTED_LIST			0x00010101
+#define OID_GEN_HARDWARE_STATUS			0x00010102
+#define OID_GEN_MEDIA_SUPPORTED			0x00010103
+#define OID_GEN_MEDIA_IN_USE			0x00010104
+#define OID_GEN_MAXIMUM_LOOKAHEAD		0x00010105
+#define OID_GEN_MAXIMUM_FRAME_SIZE		0x00010106
+#define OID_GEN_LINK_SPEED			0x00010107
+#define OID_GEN_TRANSMIT_BUFFER_SPACE		0x00010108
+#define OID_GEN_RECEIVE_BUFFER_SPACE		0x00010109
+#define OID_GEN_TRANSMIT_BLOCK_SIZE		0x0001010A
+#define OID_GEN_RECEIVE_BLOCK_SIZE		0x0001010B
+#define OID_GEN_VENDOR_ID			0x0001010C
+#define OID_GEN_VENDOR_DESCRIPTION		0x0001010D
+#define OID_GEN_CURRENT_PACKET_FILTER		0x0001010E
+#define OID_GEN_CURRENT_LOOKAHEAD		0x0001010F
+#define OID_GEN_DRIVER_VERSION			0x00010110
+#define OID_GEN_MAXIMUM_TOTAL_SIZE		0x00010111
+#define OID_GEN_PROTOCOL_OPTIONS		0x00010112
+#define OID_GEN_MAC_OPTIONS			0x00010113
+#define OID_GEN_MEDIA_CONNECT_STATUS		0x00010114
+#define OID_GEN_MAXIMUM_SEND_PACKETS		0x00010115
+#define OID_GEN_VENDOR_DRIVER_VERSION		0x00010116
+#define OID_GEN_SUPPORTED_GUIDS			0x00010117
+#define OID_GEN_NETWORK_LAYER_ADDRESSES		0x00010118	/* Set only */
+#define OID_GEN_TRANSPORT_HEADER_OFFSET		0x00010119	/* Set only */
+#define OID_GEN_MACHINE_NAME			0x0001021A
+#define OID_GEN_RNDIS_CONFIG_PARAMETER		0x0001021B	/* Set only */
+#define OID_GEN_VLAN_ID				0x0001021C
+
+/* Optional OIDs. */
+#define OID_GEN_MEDIA_CAPABILITIES		0x00010201
+#define OID_GEN_PHYSICAL_MEDIUM			0x00010202
+
+/* Required statistics OIDs. */
+#define OID_GEN_XMIT_OK				0x00020101
+#define OID_GEN_RCV_OK				0x00020102
+#define OID_GEN_XMIT_ERROR			0x00020103
+#define OID_GEN_RCV_ERROR			0x00020104
+#define OID_GEN_RCV_NO_BUFFER			0x00020105
+
+/* Optional OID statistics */
+#define OID_GEN_DIRECTED_BYTES_XMIT		0x00020201
+#define OID_GEN_DIRECTED_FRAMES_XMIT		0x00020202
+#define OID_GEN_MULTICAST_BYTES_XMIT		0x00020203
+#define OID_GEN_MULTICAST_FRAMES_XMIT		0x00020204
+#define OID_GEN_BROADCAST_BYTES_XMIT		0x00020205
+#define OID_GEN_BROADCAST_FRAMES_XMIT		0x00020206
+#define OID_GEN_DIRECTED_BYTES_RCV		0x00020207
+#define OID_GEN_DIRECTED_FRAMES_RCV		0x00020208
+#define OID_GEN_MULTICAST_BYTES_RCV		0x00020209
+#define OID_GEN_MULTICAST_FRAMES_RCV		0x0002020A
+#define OID_GEN_BROADCAST_BYTES_RCV		0x0002020B
+#define OID_GEN_BROADCAST_FRAMES_RCV		0x0002020C
+#define OID_GEN_RCV_CRC_ERROR			0x0002020D
+#define OID_GEN_TRANSMIT_QUEUE_LENGTH		0x0002020E
+#define OID_GEN_GET_TIME_CAPS			0x0002020F
+#define OID_GEN_GET_NETCARD_TIME		0x00020210
+#define OID_GEN_NETCARD_LOAD			0x00020211
+#define OID_GEN_DEVICE_PROFILE			0x00020212
+
+/* 802.3 (ethernet) OIDs */
+#define OID_802_3_PERMANENT_ADDRESS		0x01010101
+#define OID_802_3_CURRENT_ADDRESS		0x01010102
+#define OID_802_3_MULTICAST_LIST		0x01010103
+#define OID_802_3_MAXIMUM_LIST_SIZE		0x01010104
+#define OID_802_3_MAC_OPTIONS			0x01010105
+#define NDIS_802_3_MAC_OPTION_PRIORITY		0x00000001
+#define OID_802_3_RCV_ERROR_ALIGNMENT		0x01020101
+#define OID_802_3_XMIT_ONE_COLLISION		0x01020102
+#define OID_802_3_XMIT_MORE_COLLISIONS		0x01020103
+#define OID_802_3_XMIT_DEFERRED			0x01020201
+#define OID_802_3_XMIT_MAX_COLLISIONS		0x01020202
+#define OID_802_3_RCV_OVERRUN			0x01020203
+#define OID_802_3_XMIT_UNDERRUN			0x01020204
+#define OID_802_3_XMIT_HEARTBEAT_FAILURE	0x01020205
+#define OID_802_3_XMIT_TIMES_CRS_LOST		0x01020206
+#define OID_802_3_XMIT_LATE_COLLISIONS		0x01020207
+
+/* PnP and power management OIDs */
+#define OID_PNP_CAPABILITIES			0xFD010100
+#define OID_PNP_SET_POWER			0xFD010101
+#define OID_PNP_QUERY_POWER			0xFD010102
+#define OID_PNP_ADD_WAKE_UP_PATTERN		0xFD010103
+#define OID_PNP_REMOVE_WAKE_UP_PATTERN		0xFD010104
+#define OID_PNP_WAKE_UP_PATTERN_LIST		0xFD010105
+#define OID_PNP_ENABLE_WAKE_UP			0xFD010106
+
+/* PnP/PM Statistics (Optional). */
+#define OID_PNP_WAKE_UP_OK			0xFD020200
+#define OID_PNP_WAKE_UP_ERROR			0xFD020201
+
+/* The following bits are defined for OID_PNP_ENABLE_WAKE_UP */
+#define NDIS_PNP_WAKE_UP_MAGIC_PACKET		0x00000001
+#define NDIS_PNP_WAKE_UP_PATTERN_MATCH		0x00000002
+#define NDIS_PNP_WAKE_UP_LINK_CHANGE		0x00000004
+
+/* 802.11 OIDs */
+#define OID_802_11_BSSID			0x0D010101
+#define OID_802_11_SSID				0x0D010102
+#define OID_802_11_NETWORK_TYPES_SUPPORTED	0x0D010203
+#define OID_802_11_NETWORK_TYPE_IN_USE		0x0D010204
+#define OID_802_11_TX_POWER_LEVEL		0x0D010205
+#define OID_802_11_RSSI				0x0D010206
+#define OID_802_11_RSSI_TRIGGER			0x0D010207
+#define OID_802_11_INFRASTRUCTURE_MODE		0x0D010108
+#define OID_802_11_FRAGMENTATION_THRESHOLD	0x0D010209
+#define OID_802_11_RTS_THRESHOLD		0x0D01020A
+#define OID_802_11_NUMBER_OF_ANTENNAS		0x0D01020B
+#define OID_802_11_RX_ANTENNA_SELECTED		0x0D01020C
+#define OID_802_11_TX_ANTENNA_SELECTED		0x0D01020D
+#define OID_802_11_SUPPORTED_RATES		0x0D01020E
+#define OID_802_11_DESIRED_RATES		0x0D010210
+#define OID_802_11_CONFIGURATION		0x0D010211
+#define OID_802_11_STATISTICS			0x0D020212
+#define OID_802_11_ADD_WEP			0x0D010113
+#define OID_802_11_REMOVE_WEP			0x0D010114
+#define OID_802_11_DISASSOCIATE			0x0D010115
+#define OID_802_11_POWER_MODE			0x0D010216
+#define OID_802_11_BSSID_LIST			0x0D010217
+#define OID_802_11_AUTHENTICATION_MODE		0x0D010118
+#define OID_802_11_PRIVACY_FILTER		0x0D010119
+#define OID_802_11_BSSID_LIST_SCAN		0x0D01011A
+#define OID_802_11_WEP_STATUS			0x0D01011B
+#define OID_802_11_ENCRYPTION_STATUS		OID_802_11_WEP_STATUS
+#define OID_802_11_RELOAD_DEFAULTS		0x0D01011C
+#define OID_802_11_ADD_KEY			0x0D01011D
+#define OID_802_11_REMOVE_KEY			0x0D01011E
+#define OID_802_11_ASSOCIATION_INFORMATION	0x0D01011F
+#define OID_802_11_TEST				0x0D010120
+#define OID_802_11_MEDIA_STREAM_MODE		0x0D010121
+#define OID_802_11_CAPABILITY			0x0D010122
+#define OID_802_11_PMKID			0x0D010123
+
+#define NDIS_STATUS_SUCCESS			0
+#define NDIS_STATUS_PENDING			0x00000103
+#define NDIS_STATUS_NOT_RECOGNIZED		0x00010001
+#define NDIS_STATUS_NOT_COPIED			0x00010002
+#define NDIS_STATUS_NOT_ACCEPTED		0x00010003
+#define NDIS_STATUS_CALL_ACTIVE			0x00010007
+#define NDIS_STATUS_ONLINE			0x40010003
+#define NDIS_STATUS_RESET_START			0x40010004
+#define NDIS_STATUS_RESET_END			0x40010005
+#define NDIS_STATUS_RING_STATUS			0x40010006
+#define NDIS_STATUS_CLOSED			0x40010007
+#define NDIS_STATUS_WAN_LINE_UP			0x40010008
+#define NDIS_STATUS_WAN_LINE_DOWN		0x40010009
+#define NDIS_STATUS_WAN_FRAGMENT		0x4001000A
+#define NDIS_STATUS_MEDIA_CONNECT		0x4001000B
+#define NDIS_STATUS_MEDIA_DISCONNECT		0x4001000C
+#define NDIS_STATUS_HARDWARE_LINE_UP		0x4001000D
+#define NDIS_STATUS_HARDWARE_LINE_DOWN		0x4001000E
+#define NDIS_STATUS_INTERFACE_UP		0x4001000F
+#define NDIS_STATUS_INTERFACE_DOWN		0x40010010
+#define NDIS_STATUS_MEDIA_BUSY			0x40010011
+#define NDIS_STATUS_MEDIA_SPECIFIC_INDICATION	0x40010012
+#define NDIS_STATUS_WW_INDICATION NDIS_STATUS_MEDIA_SPECIFIC_INDICATION
+#define NDIS_STATUS_LINK_SPEED_CHANGE		0x40010013
+#define NDIS_STATUS_WAN_GET_STATS		0x40010014
+#define NDIS_STATUS_WAN_CO_FRAGMENT		0x40010015
+#define NDIS_STATUS_WAN_CO_LINKPARAMS		0x40010016
+#define NDIS_STATUS_NOT_RESETTABLE		0x80010001
+#define NDIS_STATUS_SOFT_ERRORS			0x80010003
+#define NDIS_STATUS_HARD_ERRORS			0x80010004
+#define NDIS_STATUS_BUFFER_OVERFLOW		0x80000005
+#define NDIS_STATUS_FAILURE			0xC0000001
+#define NDIS_STATUS_INVALID_PARAMETER		0xC000000D
+#define NDIS_STATUS_RESOURCES			0xC000009A
+#define NDIS_STATUS_CLOSING			0xC0010002
+#define NDIS_STATUS_BAD_VERSION			0xC0010004
+#define NDIS_STATUS_BAD_CHARACTERISTICS		0xC0010005
+#define NDIS_STATUS_ADAPTER_NOT_FOUND		0xC0010006
+#define NDIS_STATUS_OPEN_FAILED			0xC0010007
+#define NDIS_STATUS_DEVICE_FAILED		0xC0010008
+#define NDIS_STATUS_MULTICAST_FULL		0xC0010009
+#define NDIS_STATUS_MULTICAST_EXISTS		0xC001000A
+#define NDIS_STATUS_MULTICAST_NOT_FOUND		0xC001000B
+#define NDIS_STATUS_REQUEST_ABORTED		0xC001000C
+#define NDIS_STATUS_RESET_IN_PROGRESS		0xC001000D
+#define NDIS_STATUS_CLOSING_INDICATING		0xC001000E
+#define NDIS_STATUS_BAD_VERSION			0xC0010004
+#define NDIS_STATUS_NOT_SUPPORTED		0xC00000BB
+#define NDIS_STATUS_INVALID_PACKET		0xC001000F
+#define NDIS_STATUS_OPEN_LIST_FULL		0xC0010010
+#define NDIS_STATUS_ADAPTER_NOT_READY		0xC0010011
+#define NDIS_STATUS_ADAPTER_NOT_OPEN		0xC0010012
+#define NDIS_STATUS_NOT_INDICATING		0xC0010013
+#define NDIS_STATUS_INVALID_LENGTH		0xC0010014
+#define NDIS_STATUS_INVALID_DATA		0xC0010015
+#define NDIS_STATUS_BUFFER_TOO_SHORT		0xC0010016
+#define NDIS_STATUS_INVALID_OID			0xC0010017
+#define NDIS_STATUS_ADAPTER_REMOVED		0xC0010018
+#define NDIS_STATUS_UNSUPPORTED_MEDIA		0xC0010019
+#define NDIS_STATUS_GROUP_ADDRESS_IN_USE	0xC001001A
+#define NDIS_STATUS_FILE_NOT_FOUND		0xC001001B
+#define NDIS_STATUS_ERROR_READING_FILE		0xC001001C
+#define NDIS_STATUS_ALREADY_MAPPED		0xC001001D
+#define NDIS_STATUS_RESOURCE_CONFLICT		0xC001001E
+#define NDIS_STATUS_NO_CABLE			0xC001001F
+#define NDIS_STATUS_INVALID_SAP			0xC0010020
+#define NDIS_STATUS_SAP_IN_USE			0xC0010021
+#define NDIS_STATUS_INVALID_ADDRESS		0xC0010022
+#define NDIS_STATUS_VC_NOT_ACTIVATED		0xC0010023
+#define NDIS_STATUS_DEST_OUT_OF_ORDER		0xC0010024
+#define NDIS_STATUS_VC_NOT_AVAILABLE		0xC0010025
+#define NDIS_STATUS_CELLRATE_NOT_AVAILABLE	0xC0010026
+#define NDIS_STATUS_INCOMPATABLE_QOS		0xC0010027
+#define NDIS_STATUS_AAL_PARAMS_UNSUPPORTED	0xC0010028
+#define NDIS_STATUS_NO_ROUTE_TO_DESTINATION	0xC0010029
+#define NDIS_STATUS_TOKEN_RING_OPEN_ERROR	0xC0011000
+#define NDIS_STATUS_INVALID_DEVICE_REQUEST	0xC0000010
+#define NDIS_STATUS_NETWORK_UNREACHABLE		0xC000023C
+
+/* Event codes */
+
+#define EVENT_NDIS_RESOURCE_CONFLICT	0xC0001388
+#define EVENT_NDIS_OUT_OF_RESOURCE	0xC0001389
+#define EVENT_NDIS_HARDWARE_FAILURE	0xC000138A
+#define EVENT_NDIS_ADAPTER_NOT_FOUND	0xC000138B
+#define EVENT_NDIS_INTERRUPT_CONNECT	0xC000138C
+#define EVENT_NDIS_DRIVER_FAILURE	0xC000138D
+#define EVENT_NDIS_BAD_VERSION		0xC000138E
+#define EVENT_NDIS_TIMEOUT		0x8000138F
+#define EVENT_NDIS_NETWORK_ADDRESS	0xC0001390
+#define EVENT_NDIS_UNSUPPORTED_CONFIGURATION	0xC0001391
+#define EVENT_NDIS_INVALID_VALUE_FROM_ADAPTER	0xC0001392
+#define EVENT_NDIS_MISSING_CONFIGURATION_PARAMETER	0xC0001393
+#define EVENT_NDIS_BAD_IO_BASE_ADDRESS	0xC0001394
+#define EVENT_NDIS_RECEIVE_SPACE_SMALL	0x40001395
+#define EVENT_NDIS_ADAPTER_DISABLED	0x80001396
+#define EVENT_NDIS_IO_PORT_CONFLICT	0x80001397
+#define EVENT_NDIS_PORT_OR_DMA_CONFLICT	0x80001398
+#define EVENT_NDIS_MEMORY_CONFLICT	0x80001399
+#define EVENT_NDIS_INTERRUPT_CONFLICT	0x8000139A
+#define EVENT_NDIS_DMA_CONFLICT		0x8000139B
+#define EVENT_NDIS_INVALID_DOWNLOAD_FILE_ERROR	0xC000139C
+#define EVENT_NDIS_MAXRECEIVES_ERROR	0x8000139D
+#define EVENT_NDIS_MAXTRANSMITS_ERROR	0x8000139E
+#define EVENT_NDIS_MAXFRAMESIZE_ERROR	0x8000139F
+#define EVENT_NDIS_MAXINTERNALBUFS_ERROR	0x800013A0
+#define EVENT_NDIS_MAXMULTICAST_ERROR	0x800013A1
+#define EVENT_NDIS_PRODUCTID_ERROR	0x800013A2
+#define EVENT_NDIS_LOBE_FAILUE_ERROR	0x800013A3
+#define EVENT_NDIS_SIGNAL_LOSS_ERROR	0x800013A4
+#define EVENT_NDIS_REMOVE_RECEIVED_ERROR	0x800013A5
+#define EVENT_NDIS_TOKEN_RING_CORRECTION	0x400013A6
+#define EVENT_NDIS_ADAPTER_CHECK_ERROR	0xC00013A7
+#define EVENT_NDIS_RESET_FAILURE_ERROR	0x800013A8
+#define EVENT_NDIS_CABLE_DISCONNECTED_ERROR	0x800013A9
+#define EVENT_NDIS_RESET_FAILURE_CORRECTION	0x800013AA
+
+/* packet filter bits used by NDIS_OID_PACKET_FILTER */
+#define NDIS_PACKET_TYPE_DIRECTED		0x00000001
+#define NDIS_PACKET_TYPE_MULTICAST		0x00000002
+#define NDIS_PACKET_TYPE_ALL_MULTICAST		0x00000004
+#define NDIS_PACKET_TYPE_BROADCAST		0x00000008
+#define NDIS_PACKET_TYPE_SOURCE_ROUTING		0x00000010
+#define NDIS_PACKET_TYPE_PROMISCUOUS		0x00000020
+#define NDIS_PACKET_TYPE_SMT			0x00000040
+#define NDIS_PACKET_TYPE_ALL_LOCAL		0x00000080
+#define NDIS_PACKET_TYPE_GROUP			0x00001000
+#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL		0x00002000
+#define NDIS_PACKET_TYPE_FUNCTIONAL		0x00004000
+#define NDIS_PACKET_TYPE_MAC_FRAME		0x00008000
+
+/* memory allocation flags */
+#define NDIS_MEMORY_CONTIGUOUS			0x00000001
+#define NDIS_MEMORY_NONCACHED			0x00000002
+
+/* Attribute flags to NdisMSetAtrributesEx */
+#define NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT	0x00000001
+#define NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT	0x00000002
+#define NDIS_ATTRIBUTE_IGNORE_TOKEN_RING_ERRORS	0x00000004
+#define NDIS_ATTRIBUTE_BUS_MASTER		0x00000008
+#define NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER	0x00000010
+#define NDIS_ATTRIBUTE_DESERIALIZE		0x00000020
+#define NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND	0x00000040
+#define NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK	0x00000080
+#define NDIS_ATTRIBUTE_NOT_CO_NDIS		0x00000100
+#define NDIS_ATTRIBUTE_USES_SAFE_BUFFER_APIS	0x00000200
+
+#define OID_TCP_TASK_OFFLOAD			0xFC010201
+
+#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA	0x00000001
+#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED	0x00000002
+#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND	0x00000004
+#define NDIS_MAC_OPTION_NO_LOOPBACK		0x00000008
+#define NDIS_MAC_OPTION_FULL_DUPLEX		0x00000010
+#define NDIS_MAC_OPTION_EOTX_INDICATION		0x00000020
+#define NDIS_MAC_OPTION_8021P_PRIORITY		0x00000040
+#define NDIS_MAC_OPTION_SUPPORTS_MAC_ADDRESS_OVERWRITE	0x00000080
+#define NDIS_MAC_OPTION_RECEIVE_AT_DPC		0x00000100
+#define NDIS_MAC_OPTION_8021Q_VLAN		0x00000200
+#define NDIS_MAC_OPTION_RESERVED		0x80000000
+
+#define deserialized_driver(wnd) (wnd->attributes & NDIS_ATTRIBUTE_DESERIALIZE)
+
+static inline void serialize_lock(struct ndis_device *wnd)
+{
+	nt_spin_lock(&wnd->nmb->lock);
+}
+
+static inline void serialize_unlock(struct ndis_device *wnd)
+{
+	nt_spin_unlock(&wnd->nmb->lock);
+}
+
+static inline KIRQL serialize_lock_irql(struct ndis_device *wnd)
+{
+	if (deserialized_driver(wnd))
+		return raise_irql(DISPATCH_LEVEL);
+	else
+		return nt_spin_lock_irql(&wnd->nmb->lock, DISPATCH_LEVEL);
+}
+
+static inline void serialize_unlock_irql(struct ndis_device *wnd,
+					 KIRQL irql)
+{
+	if (deserialized_driver(wnd))
+		lower_irql(irql);
+	else
+		nt_spin_unlock_irql(&wnd->nmb->lock, irql);
+}
+
+static inline void if_serialize_lock(struct ndis_device *wnd)
+{
+	if (!deserialized_driver(wnd))
+		nt_spin_lock(&wnd->nmb->lock);
+}
+
+static inline void if_serialize_unlock(struct ndis_device *wnd)
+{
+	if (!deserialized_driver(wnd))
+		nt_spin_unlock(&wnd->nmb->lock);
+}
+
+#endif /* NDIS_H */
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/ndiswrapper.h linux-5.6.11-ndis/3rdparty/ndiswrapper/ndiswrapper.h
--- linux-5.6.11/3rdparty/ndiswrapper/ndiswrapper.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/ndiswrapper.h	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,219 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#ifndef _NDISWRAPPER_H_
+#define _NDISWRAPPER_H_
+
+#define DRIVER_VERSION "1.63"
+#define UTILS_VERSION "1.9"
+
+#define DRIVER_NAME "ndiswrapper"
+#define DRIVER_CONFIG_DIR "/etc/ndiswrapper"
+
+#define NDIS_ESSID_MAX_SIZE 32
+#define NDIS_ENCODING_TOKEN_MAX 32
+#define MAX_ENCR_KEYS 4
+#define TX_RING_SIZE 16
+#define NDIS_MAX_RATES 8
+#define NDIS_MAX_RATES_EX 16
+
+#define WRAP_PCI_BUS 5
+#define WRAP_PCMCIA_BUS 8
+/* some USB devices, e.g., DWL-G120 have BusType as 0 */
+#define WRAP_INTERNAL_BUS 0
+/* documentation at msdn says 15 is PNP bus, but inf files from all
+ * vendors say 15 is USB; which is correct? */
+#define WRAP_USB_BUS 15
+
+/* NDIS device must be 0, for compatibility with old versions of
+ * ndiswrapper where device type for NDIS drivers is 0 */
+#define WRAP_NDIS_DEVICE 0
+#define WRAP_USB_DEVICE 1
+#define WRAP_BLUETOOTH_DEVICE1 2
+#define WRAP_BLUETOOTH_DEVICE2 3
+
+#define WRAP_DEVICE_BUS(dev, bus) ((dev) << 8 | (bus))
+#define WRAP_BUS(dev_bus) ((dev_bus) & 0x000FF)
+#define WRAP_DEVICE(dev_bus) ((dev_bus) >> 8)
+
+#define MAX_DRIVER_NAME_LEN 32
+#define MAX_VERSION_STRING_LEN 64
+#define MAX_SETTING_NAME_LEN 128
+#define MAX_SETTING_VALUE_LEN 256
+
+#define MAX_DRIVER_PE_IMAGES 4
+#define MAX_DRIVER_BIN_FILES 5
+#define MAX_DEVICE_SETTINGS 512
+
+#define MAX_ALLOCATED_URBS 15
+
+#define DEV_ANY_ID -1
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTRSEP "%02x:%02x:%02x:%02x:%02x:%02x"
+#define MACSTR "%02x%02x%02x%02x%02x%02x"
+#define MACINTADR(a) (int*)&((a)[0]), (int*)&((a)[1]), (int*)&((a)[2]), \
+		(int*)&((a)[3]), (int*)&((a)[4]), (int*)&((a)[5])
+
+#ifdef __KERNEL__
+/* DEBUG macros */
+
+#define MSG(level, fmt, ...)				\
+	printk(level DRIVER_NAME " (%s:%d): " fmt "\n",	\
+	       __func__, __LINE__ , ## __VA_ARGS__)
+
+#define WARNING(fmt, ...) MSG(KERN_WARNING, fmt, ## __VA_ARGS__)
+#define ERROR(fmt, ...) MSG(KERN_ERR, fmt , ## __VA_ARGS__)
+#define INFO(fmt, ...) MSG(KERN_INFO, fmt , ## __VA_ARGS__)
+#define TODO() WARNING("not fully implemented (yet)")
+
+#define TRACE(level, fmt, ...)						\
+do {									\
+	if (debug >= level)						\
+		printk(KERN_INFO "%s (%s:%d): " fmt "\n", DRIVER_NAME,	\
+		       __func__, __LINE__ , ## __VA_ARGS__);		\
+} while (0)
+#define TRACE0(fmt, ...) TRACE(0, fmt , ## __VA_ARGS__)
+
+extern int debug;
+
+#ifndef DEBUG
+#define DEBUG 0
+#endif
+
+/* for a block of code */
+#if DEBUG >= 1
+#define DBG_BLOCK(level) if (debug >= level)
+#else
+#define DBG_BLOCK(level) while (0)
+#endif
+
+#if DEBUG >= 1
+#define TRACE1(fmt, ...) TRACE(1, fmt , ## __VA_ARGS__)
+#else
+#define TRACE1(fmt, ...) do { } while (0)
+#endif
+
+#if DEBUG >= 2
+#define TRACE2(fmt, ...) TRACE(2, fmt , ## __VA_ARGS__)
+#else
+#define TRACE2(fmt, ...) do { } while (0)
+#endif
+
+#if DEBUG >= 3
+#define TRACE3(fmt, ...) TRACE(3, fmt , ## __VA_ARGS__)
+#else
+#define TRACE3(fmt, ...) do { } while (0)
+#endif
+
+#if DEBUG >= 4
+#define TRACE4(fmt, ...) TRACE(4, fmt , ## __VA_ARGS__)
+#else
+#define TRACE4(fmt, ...) do { } while (0)
+#endif
+
+#if DEBUG >= 5
+#define TRACE5(fmt, ...) TRACE(5, fmt , ## __VA_ARGS__)
+#else
+#define TRACE5(fmt, ...) do { } while (0)
+#endif
+
+#if DEBUG >= 6
+#define TRACE6(fmt, ...) TRACE(6, fmt , ## __VA_ARGS__)
+#else
+#define TRACE6(fmt, ...) do { } while (0)
+#endif
+
+#define ENTER0(fmt, ...) TRACE0("Enter " fmt , ## __VA_ARGS__)
+#define ENTER1(fmt, ...) TRACE1("Enter " fmt , ## __VA_ARGS__)
+#define ENTER2(fmt, ...) TRACE2("Enter " fmt , ## __VA_ARGS__)
+#define ENTER3(fmt, ...) TRACE3("Enter " fmt , ## __VA_ARGS__)
+#define ENTER4(fmt, ...) TRACE4("Enter " fmt , ## __VA_ARGS__)
+#define ENTER5(fmt, ...) TRACE5("Enter " fmt , ## __VA_ARGS__)
+#define ENTER6(fmt, ...) TRACE6("Enter " fmt , ## __VA_ARGS__)
+
+#define EXIT0(stmt) do { TRACE0("Exit"); stmt; } while (0)
+#define EXIT1(stmt) do { TRACE1("Exit"); stmt; } while (0)
+#define EXIT2(stmt) do { TRACE2("Exit"); stmt; } while (0)
+#define EXIT3(stmt) do { TRACE3("Exit"); stmt; } while (0)
+#define EXIT4(stmt) do { TRACE4("Exit"); stmt; } while (0)
+#define EXIT5(stmt) do { TRACE5("Exit"); stmt; } while (0)
+#define EXIT6(stmt) do { TRACE6("Exit"); stmt; } while (0)
+
+#if defined(USB_DEBUG)
+#define USBTRACE TRACE0
+#define USBENTER ENTER0
+#define USBEXIT EXIT0
+#else
+#define USBTRACE(fmt, ...) do { } while (0)
+#define USBENTER(fmt, ...)
+#define USBEXIT(stmt) stmt
+#endif
+
+#if defined(EVENT_DEBUG)
+#define EVENTTRACE TRACE0
+#define EVENTENTER ENTER0
+#define EVENTEXIT EXIT0
+#else
+#define EVENTTRACE(fmt, ...) do { } while (0)
+#define EVENTENTER(fmt, ...)
+#define EVENTEXIT(stmt) stmt
+#endif
+
+#if defined(TIMER_DEBUG)
+#define TIMERTRACE TRACE0
+#define TIMERENTER ENTER0
+#define TIMEREXIT EXIT0
+#else
+#define TIMERTRACE(fmt, ...) do { } while (0)
+#define TIMERENTER(fmt, ...)
+#define TIMEREXIT(stmt) stmt
+#endif
+
+#if defined(IO_DEBUG)
+#define IOTRACE TRACE0
+#define IOENTER ENTER0
+#define IOEXIT EXIT0
+#else
+#define IOTRACE(fmt, ...) do { } while (0)
+#define IOENTER(fmt, ...)
+#define IOEXIT(stmt) stmt
+#endif
+
+#if defined(WORK_DEBUG)
+#define WORKTRACE TRACE0
+#define WORKENTER ENTER0
+#define WORKEXIT EXIT0
+#else
+#define WORKTRACE(fmt, ...) do { } while (0)
+#define WORKENTER(fmt, ...)
+#define WORKEXIT(stmt) stmt
+#endif
+
+#if DEBUG >= 1
+#define assert(expr)							\
+do {									\
+	if (!(expr)) {							\
+		ERROR("assertion '%s' failed", #expr);			\
+		dump_stack();						\
+	}								\
+} while (0)
+#else
+#define assert(expr) do { } while (0)
+#endif
+
+#endif // __KERNEL__
+
+#endif // NDISWRAPPER_H
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/ntoskernel.c linux-5.6.11-ndis/3rdparty/ndiswrapper/ntoskernel.c
--- linux-5.6.11/3rdparty/ndiswrapper/ntoskernel.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/ntoskernel.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,2716 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include "ntoskernel.h"
+#include "ndis.h"
+#include "usb.h"
+#include "pnp.h"
+#include "loader.h"
+#include "ntoskernel_exports.h"
+
+/* MDLs describe a range of virtual address with an array of physical
+ * pages right after the header. For different ranges of virtual
+ * addresses, the number of entries of physical pages may be different
+ * (depending on number of entries required). If we want to allocate
+ * MDLs from a pool, the size has to be constant. So we assume that
+ * maximum range used by a driver is MDL_CACHE_PAGES; if a driver
+ * requests an MDL for a bigger region, we allocate it with kmalloc;
+ * otherwise, we allocate from the pool */
+
+#define MDL_CACHE_PAGES 3
+#define MDL_CACHE_SIZE (sizeof(struct mdl) + \
+			(sizeof(PFN_NUMBER) * MDL_CACHE_PAGES))
+struct wrap_mdl {
+	struct nt_list list;
+	struct mdl mdl[0];
+};
+
+/* everything here is for all drivers/devices - not per driver/device */
+static spinlock_t dispatcher_lock;
+spinlock_t ntoskernel_lock;
+static void *mdl_cache;
+static struct nt_list wrap_mdl_list;
+
+static struct work_struct kdpc_work;
+static void kdpc_worker(struct work_struct *dummy);
+
+static struct nt_list kdpc_list;
+static spinlock_t kdpc_list_lock;
+
+static struct nt_list callback_objects;
+
+struct nt_list object_list;
+
+struct bus_driver {
+	struct nt_list list;
+	char name[MAX_DRIVER_NAME_LEN];
+	struct driver_object drv_obj;
+};
+
+static struct nt_list bus_driver_list;
+
+static struct work_struct ntos_work;
+static struct nt_list ntos_work_list;
+static spinlock_t ntos_work_lock;
+static void ntos_work_worker(struct work_struct *dummy);
+spinlock_t irp_cancel_lock;
+static NT_SPIN_LOCK nt_list_lock;
+static struct nt_slist wrap_timer_slist;
+CCHAR cpu_count;
+
+/* compute ticks (100ns) since 1601 until when system booted into
+ * wrap_ticks_to_boot */
+u64 wrap_ticks_to_boot;
+
+#if defined(CONFIG_X86_64)
+static struct timer_list shared_data_timer;
+struct kuser_shared_data kuser_shared_data;
+#endif
+
+WIN_SYMBOL_MAP("KeTickCount", &jiffies)
+WIN_SYMBOL_MAP("KeNumberProcessors", &cpu_count)
+WIN_SYMBOL_MAP("NlsMbCodePageTag", FALSE)
+
+struct workqueue_struct *ntos_wq;
+
+#ifdef WRAP_PREEMPT
+DEFINE_PER_CPU(struct irql_info, irql_info);
+#endif
+
+#if defined(CONFIG_X86_64)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+static void update_user_shared_data_proc(struct timer_list *tl)
+#else
+static void update_user_shared_data_proc(unsigned long data)
+#endif
+{
+	/* timer is supposed to be scheduled every 10ms, but bigger
+	 * intervals seem to work (tried up to 50ms) */
+	*((ULONG64 *)&kuser_shared_data.system_time) = ticks_1601();
+	*((ULONG64 *)&kuser_shared_data.interrupt_time) =
+		jiffies * TICKSPERSEC / HZ;
+	*((ULONG64 *)&kuser_shared_data.tick) = jiffies;
+
+	mod_timer(&shared_data_timer, jiffies + MSEC_TO_HZ(30));
+}
+#endif
+
+void *allocate_object(ULONG size, enum common_object_type type,
+		      struct unicode_string *name)
+{
+	struct common_object_header *hdr;
+	void *body;
+
+	/* we pad header as prefix to body */
+	hdr = ExAllocatePoolWithTag(NonPagedPool, OBJECT_SIZE(size), 0);
+	if (!hdr) {
+		WARNING("couldn't allocate memory");
+		return NULL;
+	}
+	memset(hdr, 0, OBJECT_SIZE(size));
+	if (name) {
+		hdr->name.buf = ExAllocatePoolWithTag(NonPagedPool,
+						      name->max_length, 0);
+		if (!hdr->name.buf) {
+			ExFreePool(hdr);
+			return NULL;
+		}
+		memcpy(hdr->name.buf, name->buf, name->max_length);
+		hdr->name.length = name->length;
+		hdr->name.max_length = name->max_length;
+	}
+	hdr->type = type;
+	hdr->ref_count = 1;
+	spin_lock_bh(&ntoskernel_lock);
+	/* threads are looked up often (in KeWaitForXXX), so optimize
+	 * for fast lookups of threads */
+	if (type == OBJECT_TYPE_NT_THREAD)
+		InsertHeadList(&object_list, &hdr->list);
+	else
+		InsertTailList(&object_list, &hdr->list);
+	spin_unlock_bh(&ntoskernel_lock);
+	body = HEADER_TO_OBJECT(hdr);
+	TRACE3("allocated hdr: %p, body: %p", hdr, body);
+	return body;
+}
+
+static void free_object(void *object)
+{
+	struct common_object_header *hdr;
+
+	hdr = OBJECT_TO_HEADER(object);
+	spin_lock_bh(&ntoskernel_lock);
+	RemoveEntryList(&hdr->list);
+	spin_unlock_bh(&ntoskernel_lock);
+	TRACE3("freed hdr: %p, body: %p", hdr, object);
+	if (hdr->name.buf)
+		ExFreePool(hdr->name.buf);
+	ExFreePool(hdr);
+}
+
+static int add_bus_driver(const char *name)
+{
+	struct bus_driver *bus_driver;
+
+	bus_driver = kzalloc(sizeof(*bus_driver), GFP_KERNEL);
+	if (!bus_driver) {
+		ERROR("couldn't allocate memory");
+		return -ENOMEM;
+	}
+	strncpy(bus_driver->name, name, sizeof(bus_driver->name));
+	bus_driver->name[sizeof(bus_driver->name)-1] = 0;
+	spin_lock_bh(&ntoskernel_lock);
+	InsertTailList(&bus_driver_list, &bus_driver->list);
+	spin_unlock_bh(&ntoskernel_lock);
+	TRACE1("bus driver %s is at %p", name, &bus_driver->drv_obj);
+	return STATUS_SUCCESS;
+}
+
+struct driver_object *find_bus_driver(const char *name)
+{
+	struct bus_driver *bus_driver;
+	struct driver_object *drv_obj;
+
+	spin_lock_bh(&ntoskernel_lock);
+	drv_obj = NULL;
+	nt_list_for_each_entry(bus_driver, &bus_driver_list, list) {
+		if (strcmp(bus_driver->name, name) == 0) {
+			drv_obj = &bus_driver->drv_obj;
+			break;
+		}
+	}
+	spin_unlock_bh(&ntoskernel_lock);
+	return drv_obj;
+}
+
+wfastcall struct nt_list *WIN_FUNC(ExfInterlockedInsertHeadList,3)
+	(struct nt_list *head, struct nt_list *entry, NT_SPIN_LOCK *lock)
+{
+	struct nt_list *first;
+	unsigned long flags;
+
+	ENTER5("head = %p, entry = %p", head, entry);
+	nt_spin_lock_irqsave(lock, flags);
+	first = InsertHeadList(head, entry);
+	nt_spin_unlock_irqrestore(lock, flags);
+	TRACE5("head = %p, old = %p", head, first);
+	return first;
+}
+
+wfastcall struct nt_list *WIN_FUNC(ExInterlockedInsertHeadList,3)
+	(struct nt_list *head, struct nt_list *entry, NT_SPIN_LOCK *lock)
+{
+	ENTER5("%p", head);
+	return ExfInterlockedInsertHeadList(head, entry, lock);
+}
+
+wfastcall struct nt_list *WIN_FUNC(ExfInterlockedInsertTailList,3)
+	(struct nt_list *head, struct nt_list *entry, NT_SPIN_LOCK *lock)
+{
+	struct nt_list *last;
+	unsigned long flags;
+
+	ENTER5("head = %p, entry = %p", head, entry);
+	nt_spin_lock_irqsave(lock, flags);
+	last = InsertTailList(head, entry);
+	nt_spin_unlock_irqrestore(lock, flags);
+	TRACE5("head = %p, old = %p", head, last);
+	return last;
+}
+
+wfastcall struct nt_list *WIN_FUNC(ExInterlockedInsertTailList,3)
+	(struct nt_list *head, struct nt_list *entry, NT_SPIN_LOCK *lock)
+{
+	ENTER5("%p", head);
+	return ExfInterlockedInsertTailList(head, entry, lock);
+}
+
+wfastcall struct nt_list *WIN_FUNC(ExfInterlockedRemoveHeadList,2)
+	(struct nt_list *head, NT_SPIN_LOCK *lock)
+{
+	struct nt_list *ret;
+	unsigned long flags;
+
+	ENTER5("head = %p", head);
+	nt_spin_lock_irqsave(lock, flags);
+	ret = RemoveHeadList(head);
+	nt_spin_unlock_irqrestore(lock, flags);
+	TRACE5("head = %p, ret = %p", head, ret);
+	return ret;
+}
+
+wfastcall struct nt_list *WIN_FUNC(ExInterlockedRemoveHeadList,2)
+	(struct nt_list *head, NT_SPIN_LOCK *lock)
+{
+	ENTER5("%p", head);
+	return ExfInterlockedRemoveHeadList(head, lock);
+}
+
+wfastcall struct nt_list *WIN_FUNC(ExfInterlockedRemoveTailList,2)
+	(struct nt_list *head, NT_SPIN_LOCK *lock)
+{
+	struct nt_list *ret;
+	unsigned long flags;
+
+	ENTER5("head = %p", head);
+	nt_spin_lock_irqsave(lock, flags);
+	ret = RemoveTailList(head);
+	nt_spin_unlock_irqrestore(lock, flags);
+	TRACE5("head = %p, ret = %p", head, ret);
+	return ret;
+}
+
+wfastcall struct nt_list *WIN_FUNC(ExInterlockedRemoveTailList,2)
+	(struct nt_list *head, NT_SPIN_LOCK *lock)
+{
+	ENTER5("%p", head);
+	return ExfInterlockedRemoveTailList(head, lock);
+}
+
+wfastcall void WIN_FUNC(InitializeSListHead,1)
+	(nt_slist_header *head)
+{
+	memset(head, 0, sizeof(*head));
+}
+
+wfastcall struct nt_slist *WIN_FUNC(ExInterlockedPushEntrySList,3)
+	(nt_slist_header *head, struct nt_slist *entry, NT_SPIN_LOCK *lock)
+{
+	struct nt_slist *ret;
+
+	ret = PushEntrySList(head, entry, lock);
+	return ret;
+}
+
+wstdcall struct nt_slist *WIN_FUNC(ExpInterlockedPushEntrySList,2)
+	(nt_slist_header *head, struct nt_slist *entry)
+{
+	struct nt_slist *ret;
+
+	ret = PushEntrySList(head, entry, &nt_list_lock);
+	return ret;
+}
+
+wfastcall struct nt_slist *WIN_FUNC(InterlockedPushEntrySList,2)
+	(nt_slist_header *head, struct nt_slist *entry)
+{
+	struct nt_slist *ret;
+
+	ret = PushEntrySList(head, entry, &nt_list_lock);
+	return ret;
+}
+
+wfastcall struct nt_slist *WIN_FUNC(ExInterlockedPopEntrySList,2)
+	(nt_slist_header *head, NT_SPIN_LOCK *lock)
+{
+	struct nt_slist *ret;
+
+	ret = PopEntrySList(head, lock);
+	return ret;
+}
+
+wstdcall struct nt_slist *WIN_FUNC(ExpInterlockedPopEntrySList,1)
+	(nt_slist_header *head)
+{
+	struct nt_slist *ret;
+
+	ret = PopEntrySList(head, &nt_list_lock);
+	return ret;
+}
+
+wfastcall struct nt_slist *WIN_FUNC(InterlockedPopEntrySList,1)
+	(nt_slist_header *head)
+{
+	struct nt_slist *ret;
+
+	ret = PopEntrySList(head, &nt_list_lock);
+	return ret;
+}
+
+wstdcall USHORT WIN_FUNC(ExQueryDepthSList,1)
+	(nt_slist_header *head)
+{
+	USHORT depth;
+	ENTER5("%p", head);
+	depth = head->depth;
+	TRACE5("%d, %p", depth, head->next);
+	return depth;
+}
+
+wfastcall LONG WIN_FUNC(InterlockedIncrement,1)
+	(LONG volatile *val)
+{
+	return post_atomic_add(*val, 1);
+}
+
+wfastcall LONG WIN_FUNC(InterlockedDecrement,1)
+	(LONG volatile *val)
+{
+	return post_atomic_add(*val, -1);
+}
+
+wfastcall LONG WIN_FUNC(InterlockedExchange,2)
+	(LONG volatile *target, LONG val)
+{
+	return xchg(target, val);
+}
+
+wfastcall LONG WIN_FUNC(InterlockedCompareExchange,3)
+	(LONG volatile *dest, LONG new, LONG old)
+{
+	return cmpxchg(dest, old, new);
+}
+
+wfastcall void WIN_FUNC(ExInterlockedAddLargeStatistic,2)
+	(LARGE_INTEGER volatile *plint, ULONG n)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+#ifdef CONFIG_X86_64
+	__asm__ __volatile__(
+		"\n"
+		LOCK_PREFIX "add %1, %0\n\t"
+		: "+m" (*plint)
+		: "r" (n));
+#else
+	__asm__ __volatile__(
+		"1:\t"
+		"   movl %1, %%ebx\n\t"
+		"   movl %%edx, %%ecx\n\t"
+		"   addl %%eax, %%ebx\n\t"
+		"   adcl $0, %%ecx\n\t"
+		    LOCK_PREFIX "cmpxchg8b %0\n\t"
+		"   jnz 1b\n\t"
+		: "+m" (*plint)
+		: "m" (n), "A" (*plint)
+		: "ebx", "ecx");
+#endif
+	local_irq_restore(flags);
+}
+
+static void initialize_object(struct dispatcher_header *dh, enum dh_type type,
+			      int state)
+{
+	memset(dh, 0, sizeof(*dh));
+	set_object_type(dh, type);
+	dh->signal_state = state;
+	InitializeListHead(&dh->wait_blocks);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+static void timer_proc(struct timer_list *tl)
+#else
+static void timer_proc(unsigned long data)
+#endif
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+	struct wrap_timer *wrap_timer = from_timer(wrap_timer, tl, timer);
+#else
+	struct wrap_timer *wrap_timer = (struct wrap_timer *)data;
+#endif
+	struct nt_timer *nt_timer;
+	struct kdpc *kdpc;
+
+	nt_timer = wrap_timer->nt_timer;
+	TIMERENTER("%p(%p), %lu", wrap_timer, nt_timer, jiffies);
+#ifdef TIMER_DEBUG
+	BUG_ON(wrap_timer->wrap_timer_magic != WRAP_TIMER_MAGIC);
+	BUG_ON(nt_timer->wrap_timer_magic != WRAP_TIMER_MAGIC);
+#endif
+	KeSetEvent((struct nt_event *)nt_timer, 0, FALSE);
+	if (wrap_timer->repeat)
+		mod_timer(&wrap_timer->timer, jiffies + wrap_timer->repeat);
+	kdpc = nt_timer->kdpc;
+	if (kdpc)
+		queue_kdpc(kdpc);
+	TIMEREXIT(return);
+}
+
+void wrap_init_timer(struct nt_timer *nt_timer, enum timer_type type,
+		     struct ndis_mp_block *nmb)
+{
+	struct wrap_timer *wrap_timer;
+
+	/* TODO: if a timer is initialized more than once, we allocate
+	 * memory for wrap_timer more than once for the same nt_timer,
+	 * wasting memory. We can check if nt_timer->wrap_timer_magic is
+	 * set and not allocate, but it is not guaranteed always to be
+	 * safe */
+	TIMERENTER("%p", nt_timer);
+	/* we allocate memory for wrap_timer behind driver's back and
+	 * there is no NDIS/DDK function where this memory can be
+	 * freed, so we use slack_kmalloc so it gets freed when driver
+	 * is unloaded */
+	if (nmb)
+		wrap_timer = kzalloc(sizeof(*wrap_timer), irql_gfp());
+	else
+		wrap_timer = slack_kzalloc(sizeof(*wrap_timer));
+	if (!wrap_timer) {
+		ERROR("couldn't allocate memory for timer");
+		return;
+	}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0)
+	init_timer(&wrap_timer->timer);
+	wrap_timer->timer.function = timer_proc;
+	wrap_timer->timer.data = (unsigned long)wrap_timer;
+#else
+	timer_setup(&wrap_timer->timer, timer_proc, 0);
+#endif
+	wrap_timer->nt_timer = nt_timer;
+#ifdef TIMER_DEBUG
+	wrap_timer->wrap_timer_magic = WRAP_TIMER_MAGIC;
+#endif
+	nt_timer->wrap_timer = wrap_timer;
+	nt_timer->kdpc = NULL;
+	initialize_object(&nt_timer->dh, (enum dh_type)type, 0);
+	nt_timer->wrap_timer_magic = WRAP_TIMER_MAGIC;
+	TIMERTRACE("timer %p (%p)", wrap_timer, nt_timer);
+	spin_lock_bh(&ntoskernel_lock);
+	if (nmb) {
+		wrap_timer->slist.next = nmb->wnd->wrap_timer_slist.next;
+		nmb->wnd->wrap_timer_slist.next = &wrap_timer->slist;
+	} else {
+		wrap_timer->slist.next = wrap_timer_slist.next;
+		wrap_timer_slist.next = &wrap_timer->slist;
+	}
+	spin_unlock_bh(&ntoskernel_lock);
+	TIMEREXIT(return);
+}
+
+wstdcall void WIN_FUNC(KeInitializeTimerEx,2)
+	(struct nt_timer *nt_timer, enum timer_type type)
+{
+	TIMERENTER("%p", nt_timer);
+	wrap_init_timer(nt_timer, type, NULL);
+}
+
+wstdcall void WIN_FUNC(KeInitializeTimer,1)
+	(struct nt_timer *nt_timer)
+{
+	TIMERENTER("%p", nt_timer);
+	wrap_init_timer(nt_timer, NotificationTimer, NULL);
+}
+
+/* expires and repeat are in HZ */
+BOOLEAN wrap_set_timer(struct nt_timer *nt_timer, unsigned long expires_hz,
+		       unsigned long repeat_hz, struct kdpc *kdpc)
+{
+	struct wrap_timer *wrap_timer;
+
+	TIMERENTER("%p, %lu, %lu, %p, %lu",
+		   nt_timer, expires_hz, repeat_hz, kdpc, jiffies);
+
+	wrap_timer = nt_timer->wrap_timer;
+	TIMERTRACE("%p", wrap_timer);
+#ifdef TIMER_DEBUG
+	if (wrap_timer->nt_timer != nt_timer)
+		WARNING("bad timers: %p, %p, %p", wrap_timer, nt_timer,
+			wrap_timer->nt_timer);
+	if (nt_timer->wrap_timer_magic != WRAP_TIMER_MAGIC) {
+		WARNING("buggy Windows timer didn't initialize timer %p",
+			nt_timer);
+		return FALSE;
+	}
+	if (wrap_timer->wrap_timer_magic != WRAP_TIMER_MAGIC) {
+		WARNING("timer %p is not initialized (%lx)?",
+			wrap_timer, wrap_timer->wrap_timer_magic);
+		wrap_timer->wrap_timer_magic = WRAP_TIMER_MAGIC;
+	}
+#endif
+	KeClearEvent((struct nt_event *)nt_timer);
+	nt_timer->kdpc = kdpc;
+	wrap_timer->repeat = repeat_hz;
+	if (mod_timer(&wrap_timer->timer, jiffies + expires_hz))
+		TIMEREXIT(return TRUE);
+	else
+		TIMEREXIT(return FALSE);
+}
+
+wstdcall BOOLEAN WIN_FUNC(KeSetTimerEx,4)
+	(struct nt_timer *nt_timer, LARGE_INTEGER duetime_ticks,
+	 LONG period_ms, struct kdpc *kdpc)
+{
+	unsigned long expires_hz, repeat_hz;
+
+	TIMERENTER("%p, %lld, %d", nt_timer, duetime_ticks, period_ms);
+	expires_hz = SYSTEM_TIME_TO_HZ(duetime_ticks);
+	repeat_hz = MSEC_TO_HZ(period_ms);
+	return wrap_set_timer(nt_timer, expires_hz, repeat_hz, kdpc);
+}
+
+wstdcall BOOLEAN WIN_FUNC(KeSetTimer,3)
+	(struct nt_timer *nt_timer, LARGE_INTEGER duetime_ticks,
+	 struct kdpc *kdpc)
+{
+	TIMERENTER("%p, %lld, %p", nt_timer, duetime_ticks, kdpc);
+	return KeSetTimerEx(nt_timer, duetime_ticks, 0, kdpc);
+}
+
+wstdcall BOOLEAN WIN_FUNC(KeCancelTimer,1)
+	(struct nt_timer *nt_timer)
+{
+	struct wrap_timer *wrap_timer;
+	int ret;
+
+	TIMERENTER("%p", nt_timer);
+	wrap_timer = nt_timer->wrap_timer;
+	if (!wrap_timer) {
+		ERROR("invalid wrap_timer");
+		return TRUE;
+	}
+#ifdef TIMER_DEBUG
+	BUG_ON(wrap_timer->wrap_timer_magic != WRAP_TIMER_MAGIC);
+#endif
+	/* disable timer before deleting so if it is periodic timer, it
+	 * won't be re-armed after deleting */
+	wrap_timer->repeat = 0;
+	ret = del_timer_sync(&wrap_timer->timer);
+	/* the documentation for KeCancelTimer suggests the DPC is
+	 * deqeued, but actually DPC is left to run */
+	if (ret)
+		TIMEREXIT(return TRUE);
+	else
+		TIMEREXIT(return FALSE);
+}
+
+wstdcall BOOLEAN WIN_FUNC(KeReadStateTimer,1)
+	(struct nt_timer *nt_timer)
+{
+	if (nt_timer->dh.signal_state)
+		return TRUE;
+	else
+		return FALSE;
+}
+
+wstdcall void WIN_FUNC(KeInitializeDpc,3)
+	(struct kdpc *kdpc, void *func, void *ctx)
+{
+	ENTER3("%p, %p, %p", kdpc, func, ctx);
+	memset(kdpc, 0, sizeof(*kdpc));
+	kdpc->func = func;
+	kdpc->ctx = ctx;
+	InitializeListHead(&kdpc->list);
+}
+
+static void kdpc_worker(struct work_struct *dummy)
+{
+	struct nt_list *entry;
+	struct kdpc *kdpc;
+	unsigned long flags;
+	KIRQL irql;
+
+	WORKENTER("");
+	irql = raise_irql(DISPATCH_LEVEL);
+	while (1) {
+		spin_lock_irqsave(&kdpc_list_lock, flags);
+		entry = RemoveHeadList(&kdpc_list);
+		if (entry) {
+			kdpc = container_of(entry, struct kdpc, list);
+			assert(kdpc->queued);
+			kdpc->queued = 0;
+		} else
+			kdpc = NULL;
+		spin_unlock_irqrestore(&kdpc_list_lock, flags);
+		if (!kdpc)
+			break;
+		WORKTRACE("%p, %p, %p, %p, %p", kdpc, kdpc->func, kdpc->ctx,
+			  kdpc->arg1, kdpc->arg2);
+		assert_irql(_irql_ == DISPATCH_LEVEL);
+		LIN2WIN4(kdpc->func, kdpc, kdpc->ctx, kdpc->arg1, kdpc->arg2);
+		assert_irql(_irql_ == DISPATCH_LEVEL);
+	}
+	lower_irql(irql);
+	WORKEXIT(return);
+}
+
+wstdcall void WIN_FUNC(KeFlushQueuedDpcs,0)
+	(void)
+{
+	kdpc_worker(NULL);
+}
+
+BOOLEAN queue_kdpc(struct kdpc *kdpc)
+{
+	BOOLEAN ret;
+	unsigned long flags;
+
+	WORKENTER("%p", kdpc);
+	spin_lock_irqsave(&kdpc_list_lock, flags);
+	if (kdpc->queued)
+		ret = FALSE;
+	else {
+		if (unlikely(kdpc->importance == HighImportance))
+			InsertHeadList(&kdpc_list, &kdpc->list);
+		else
+			InsertTailList(&kdpc_list, &kdpc->list);
+		kdpc->queued = 1;
+		ret = TRUE;
+	}
+	spin_unlock_irqrestore(&kdpc_list_lock, flags);
+	if (ret == TRUE)
+		queue_work(ntos_wq, &kdpc_work);
+	WORKTRACE("%d", ret);
+	return ret;
+}
+
+BOOLEAN dequeue_kdpc(struct kdpc *kdpc)
+{
+	BOOLEAN ret;
+	unsigned long flags;
+
+	WORKENTER("%p", kdpc);
+	spin_lock_irqsave(&kdpc_list_lock, flags);
+	if (kdpc->queued) {
+		RemoveEntryList(&kdpc->list);
+		kdpc->queued = 0;
+		ret = TRUE;
+	} else
+		ret = FALSE;
+	spin_unlock_irqrestore(&kdpc_list_lock, flags);
+	WORKTRACE("%d", ret);
+	return ret;
+}
+
+wstdcall BOOLEAN WIN_FUNC(KeInsertQueueDpc,3)
+	(struct kdpc *kdpc, void *arg1, void *arg2)
+{
+	WORKENTER("%p, %p, %p", kdpc, arg1, arg2);
+	kdpc->arg1 = arg1;
+	kdpc->arg2 = arg2;
+	return queue_kdpc(kdpc);
+}
+
+wstdcall BOOLEAN WIN_FUNC(KeRemoveQueueDpc,1)
+	(struct kdpc *kdpc)
+{
+	return dequeue_kdpc(kdpc);
+}
+
+wstdcall void WIN_FUNC(KeSetImportanceDpc,2)
+	(struct kdpc *kdpc, enum kdpc_importance importance)
+{
+	kdpc->importance = importance;
+}
+
+static void ntos_work_worker(struct work_struct *dummy)
+{
+	struct ntos_work_item *ntos_work_item;
+	struct nt_list *cur;
+
+	while (1) {
+		spin_lock_bh(&ntos_work_lock);
+		cur = RemoveHeadList(&ntos_work_list);
+		spin_unlock_bh(&ntos_work_lock);
+		if (!cur)
+			break;
+		ntos_work_item = container_of(cur, struct ntos_work_item, list);
+		WORKTRACE("%p: executing %p, %p, %p", current,
+			  ntos_work_item->func, ntos_work_item->arg1,
+			  ntos_work_item->arg2);
+		LIN2WIN2(ntos_work_item->func, ntos_work_item->arg1,
+			 ntos_work_item->arg2);
+		kfree(ntos_work_item);
+	}
+	WORKEXIT(return);
+}
+
+int schedule_ntos_work_item(NTOS_WORK_FUNC func, void *arg1, void *arg2)
+{
+	struct ntos_work_item *ntos_work_item;
+
+	WORKENTER("adding work: %p, %p, %p", func, arg1, arg2);
+	ntos_work_item = kmalloc(sizeof(*ntos_work_item), irql_gfp());
+	if (!ntos_work_item) {
+		ERROR("couldn't allocate memory");
+		return -ENOMEM;
+	}
+	ntos_work_item->func = func;
+	ntos_work_item->arg1 = arg1;
+	ntos_work_item->arg2 = arg2;
+	spin_lock_bh(&ntos_work_lock);
+	InsertTailList(&ntos_work_list, &ntos_work_item->list);
+	spin_unlock_bh(&ntos_work_lock);
+	queue_work(ntos_wq, &ntos_work);
+	WORKEXIT(return 0);
+}
+
+wstdcall void WIN_FUNC(KeInitializeSpinLock,1)
+	(NT_SPIN_LOCK *lock)
+{
+	ENTER6("%p", lock);
+	nt_spin_lock_init(lock);
+}
+
+wstdcall void WIN_FUNC(KeAcquireSpinLock,2)
+	(NT_SPIN_LOCK *lock, KIRQL *irql)
+{
+	ENTER6("%p", lock);
+	*irql = nt_spin_lock_irql(lock, DISPATCH_LEVEL);
+}
+
+wstdcall void WIN_FUNC(KeReleaseSpinLock,2)
+	(NT_SPIN_LOCK *lock, KIRQL oldirql)
+{
+	ENTER6("%p", lock);
+	nt_spin_unlock_irql(lock, oldirql);
+}
+
+wstdcall void WIN_FUNC(KeAcquireSpinLockAtDpcLevel,1)
+	(NT_SPIN_LOCK *lock)
+{
+	ENTER6("%p", lock);
+	nt_spin_lock(lock);
+}
+
+wstdcall void WIN_FUNC(KeReleaseSpinLockFromDpcLevel,1)
+	(NT_SPIN_LOCK *lock)
+{
+	ENTER6("%p", lock);
+	nt_spin_unlock(lock);
+}
+
+wstdcall void WIN_FUNC(KeRaiseIrql,2)
+	(KIRQL newirql, KIRQL *oldirql)
+{
+	ENTER6("%d", newirql);
+	*oldirql = raise_irql(newirql);
+}
+
+wstdcall KIRQL WIN_FUNC(KeRaiseIrqlToDpcLevel,0)
+	(void)
+{
+	return raise_irql(DISPATCH_LEVEL);
+}
+
+wstdcall void WIN_FUNC(KeLowerIrql,1)
+	(KIRQL irql)
+{
+	ENTER6("%d", irql);
+	lower_irql(irql);
+}
+
+wstdcall KIRQL WIN_FUNC(KeAcquireSpinLockRaiseToDpc,1)
+	(NT_SPIN_LOCK *lock)
+{
+	ENTER6("%p", lock);
+	return nt_spin_lock_irql(lock, DISPATCH_LEVEL);
+}
+
+wstdcall void *WIN_FUNC(ExAllocatePoolWithTag,3)
+	(enum pool_type pool_type, SIZE_T size, ULONG tag)
+{
+	void *addr;
+
+	ENTER4("pool_type: %d, size: %zu, tag: 0x%x", pool_type, size, tag);
+	assert_irql(_irql_ <= DISPATCH_LEVEL);
+	if (size < PAGE_SIZE)
+		addr = kmalloc(size, irql_gfp());
+	else {
+		if (irql_gfp() & GFP_ATOMIC) {
+			addr = __vmalloc(size, GFP_ATOMIC | __GFP_HIGHMEM
+					 );
+			TRACE1("%p, %zu", addr, size);
+		} else {
+			addr = vmalloc(size);
+			TRACE1("%p, %zu", addr, size);
+		}
+	}
+	DBG_BLOCK(1) {
+		if (addr)
+			TRACE4("addr: %p, %zu", addr, size);
+		else
+			TRACE1("failed: %zu", size);
+	}
+	return addr;
+}
+WIN_FUNC_DECL(ExAllocatePoolWithTag,3)
+
+wstdcall void WIN_FUNC(ExFreePoolWithTag,2)
+	(void *addr, ULONG tag)
+{
+	TRACE4("%p", addr);
+	if ((unsigned long)addr < VMALLOC_START ||
+	    (unsigned long)addr >= VMALLOC_END)
+		kfree(addr);
+	else
+		vfree(addr);
+
+	EXIT4(return);
+}
+
+wstdcall void WIN_FUNC(ExFreePool,1)
+	(void *addr)
+{
+	ExFreePoolWithTag(addr, 0);
+}
+WIN_FUNC_DECL(ExFreePool,1)
+
+wstdcall void WIN_FUNC(ExInitializeNPagedLookasideList,7)
+	(struct npaged_lookaside_list *lookaside,
+	 LOOKASIDE_ALLOC_FUNC *alloc_func, LOOKASIDE_FREE_FUNC *free_func,
+	 ULONG flags, SIZE_T size, ULONG tag, USHORT depth)
+{
+	ENTER3("lookaside: %p, size: %zu, flags: %u, head: %p, "
+	       "alloc: %p, free: %p", lookaside, size, flags,
+	       lookaside, alloc_func, free_func);
+
+	memset(lookaside, 0, sizeof(*lookaside));
+
+	lookaside->size = size;
+	lookaside->tag = tag;
+	lookaside->depth = 4;
+	lookaside->maxdepth = 256;
+	lookaside->pool_type = NonPagedPool;
+
+	if (alloc_func)
+		lookaside->alloc_func = alloc_func;
+	else
+		lookaside->alloc_func = WIN_FUNC_PTR(ExAllocatePoolWithTag,3);
+	if (free_func)
+		lookaside->free_func = free_func;
+	else
+		lookaside->free_func = WIN_FUNC_PTR(ExFreePool,1);
+
+#ifndef CONFIG_X86_64
+	nt_spin_lock_init(&lookaside->obsolete);
+#endif
+	EXIT3(return);
+}
+
+wstdcall void WIN_FUNC(ExDeleteNPagedLookasideList,1)
+	(struct npaged_lookaside_list *lookaside)
+{
+	struct nt_slist *entry;
+
+	ENTER3("lookaside = %p", lookaside);
+	while ((entry = ExpInterlockedPopEntrySList(&lookaside->head)))
+		LIN2WIN1(lookaside->free_func, entry);
+	EXIT3(return);
+}
+
+wstdcall NTSTATUS WIN_FUNC(ExCreateCallback,4)
+	(struct callback_object **object, struct object_attributes *attributes,
+	 BOOLEAN create, BOOLEAN allow_multiple_callbacks)
+{
+	struct callback_object *obj;
+
+	ENTER2("");
+	spin_lock_bh(&ntoskernel_lock);
+	nt_list_for_each_entry(obj, &callback_objects, callback_funcs) {
+		if (obj->attributes == attributes) {
+			spin_unlock_bh(&ntoskernel_lock);
+			*object = obj;
+			return STATUS_SUCCESS;
+		}
+	}
+	spin_unlock_bh(&ntoskernel_lock);
+	obj = allocate_object(sizeof(struct callback_object),
+			      OBJECT_TYPE_CALLBACK, NULL);
+	if (!obj)
+		EXIT2(return STATUS_INSUFFICIENT_RESOURCES);
+	InitializeListHead(&obj->callback_funcs);
+	nt_spin_lock_init(&obj->lock);
+	obj->allow_multiple_callbacks = allow_multiple_callbacks;
+	obj->attributes = attributes;
+	*object = obj;
+	EXIT2(return STATUS_SUCCESS);
+}
+
+wstdcall void *WIN_FUNC(ExRegisterCallback,3)
+	(struct callback_object *object, PCALLBACK_FUNCTION func, void *context)
+{
+	struct callback_func *callback;
+	KIRQL irql;
+
+	ENTER2("");
+	irql = nt_spin_lock_irql(&object->lock, DISPATCH_LEVEL);
+	if (object->allow_multiple_callbacks == FALSE &&
+	    !IsListEmpty(&object->callback_funcs)) {
+		nt_spin_unlock_irql(&object->lock, irql);
+		EXIT2(return NULL);
+	}
+	nt_spin_unlock_irql(&object->lock, irql);
+	callback = kmalloc(sizeof(*callback), GFP_KERNEL);
+	if (!callback) {
+		ERROR("couldn't allocate memory");
+		return NULL;
+	}
+	callback->func = func;
+	callback->context = context;
+	callback->object = object;
+	irql = nt_spin_lock_irql(&object->lock, DISPATCH_LEVEL);
+	InsertTailList(&object->callback_funcs, &callback->list);
+	nt_spin_unlock_irql(&object->lock, irql);
+	EXIT2(return callback);
+}
+
+wstdcall void WIN_FUNC(ExUnregisterCallback,1)
+	(struct callback_func *callback)
+{
+	struct callback_object *object;
+	KIRQL irql;
+
+	ENTER3("%p", callback);
+	if (!callback)
+		return;
+	object = callback->object;
+	irql = nt_spin_lock_irql(&object->lock, DISPATCH_LEVEL);
+	RemoveEntryList(&callback->list);
+	nt_spin_unlock_irql(&object->lock, irql);
+	kfree(callback);
+	return;
+}
+
+wstdcall void WIN_FUNC(ExNotifyCallback,3)
+	(struct callback_object *object, void *arg1, void *arg2)
+{
+	struct callback_func *callback;
+	KIRQL irql;
+
+	ENTER3("%p", object);
+	irql = nt_spin_lock_irql(&object->lock, DISPATCH_LEVEL);
+	nt_list_for_each_entry(callback, &object->callback_funcs, list) {
+		LIN2WIN3(callback->func, callback->context, arg1, arg2);
+	}
+	nt_spin_unlock_irql(&object->lock, irql);
+	return;
+}
+
+/* check and set signaled state; should be called with dispatcher_lock held */
+/* @grab indicates if the event should be grabbed or checked
+ * - note that a semaphore may stay in signaled state for multiple
+ * 'grabs' if the count is > 1 */
+static int grab_object(struct dispatcher_header *dh,
+		       struct task_struct *thread, int grab)
+{
+	EVENTTRACE("%p, %p, %d, %d", dh, thread, grab, dh->signal_state);
+	if (unlikely(is_mutex_object(dh))) {
+		struct nt_mutex *nt_mutex;
+		nt_mutex = container_of(dh, struct nt_mutex, dh);
+		EVENTTRACE("%p, %p, %d, %p, %d", nt_mutex,
+			   nt_mutex->owner_thread, dh->signal_state,
+			   thread, grab);
+		/* either no thread owns the mutex or this thread owns
+		 * it */
+		assert(dh->signal_state == 1 && nt_mutex->owner_thread == NULL);
+		assert(dh->signal_state < 1 && nt_mutex->owner_thread != NULL);
+		if ((dh->signal_state == 1 && nt_mutex->owner_thread == NULL) ||
+		    nt_mutex->owner_thread == thread) {
+			if (grab) {
+				dh->signal_state--;
+				nt_mutex->owner_thread = thread;
+			}
+			EVENTEXIT(return 1);
+		}
+	} else if (dh->signal_state > 0) {
+		/* to grab, decrement signal_state for synchronization
+		 * or semaphore objects */
+		if (grab && (is_synch_object(dh) || is_semaphore_object(dh)))
+			dh->signal_state--;
+		EVENTEXIT(return 1);
+	}
+	EVENTEXIT(return 0);
+}
+
+/* this function should be called holding dispatcher_lock */
+static void object_signaled(struct dispatcher_header *dh)
+{
+	struct nt_list *cur, *next;
+	struct wait_block *wb;
+
+	EVENTENTER("%p", dh);
+	nt_list_for_each_safe(cur, next, &dh->wait_blocks) {
+		wb = container_of(cur, struct wait_block, list);
+		assert(wb->thread != NULL);
+		assert(wb->object == NULL);
+		if (!grab_object(dh, wb->thread, 1))
+			continue;
+		EVENTTRACE("%p (%p): waking %p", dh, wb, wb->thread);
+		RemoveEntryList(cur);
+		wb->object = dh;
+		*(wb->wait_done) = 1;
+		wake_up_process(wb->thread);
+	}
+	EVENTEXIT(return);
+}
+
+wstdcall NTSTATUS WIN_FUNC(KeWaitForMultipleObjects,8)
+	(ULONG count, void *object[], enum wait_type wait_type,
+	 KWAIT_REASON wait_reason, KPROCESSOR_MODE wait_mode,
+	 BOOLEAN alertable, LARGE_INTEGER *timeout,
+	 struct wait_block *wait_block_array)
+{
+	int i, res = 0, wait_count, wait_done;
+	typeof(jiffies) wait_hz = 0;
+	struct wait_block *wb, wb_array[THREAD_WAIT_OBJECTS];
+	struct dispatcher_header *dh;
+	KIRQL irql = current_irql();
+
+	EVENTENTER("%p, %d, %u, %p", current, count, wait_type, timeout);
+
+	if (count > MAX_WAIT_OBJECTS ||
+	    (count > THREAD_WAIT_OBJECTS && wait_block_array == NULL))
+		EVENTEXIT(return STATUS_INVALID_PARAMETER);
+
+	if (wait_block_array == NULL)
+		wb = wb_array;
+	else
+		wb = wait_block_array;
+
+	/* If *timeout == 0: In the case of WaitAny, if an object can
+	 * be grabbed (object is in signaled state), grab and
+	 * return. In the case of WaitAll, we have to first make sure
+	 * all objects can be grabbed. If any/some of them can't be
+	 * grabbed, either we return STATUS_TIMEOUT or wait for them,
+	 * depending on how to satisfy wait. If all of them can be
+	 * grabbed, we will grab them in the next loop below */
+
+	spin_lock_bh(&dispatcher_lock);
+	for (i = wait_count = 0; i < count; i++) {
+		dh = object[i];
+		EVENTTRACE("%p: event %p (%d)", current, dh, dh->signal_state);
+		/* wait_type == 1 for WaitAny, 0 for WaitAll */
+		if (grab_object(dh, current, wait_type)) {
+			if (wait_type == WaitAny) {
+				spin_unlock_bh(&dispatcher_lock);
+				EVENTEXIT(return STATUS_WAIT_0 + i);
+			}
+		} else {
+			EVENTTRACE("%p: wait for %p", current, dh);
+			wait_count++;
+		}
+	}
+
+	if (timeout && *timeout == 0 && wait_count) {
+		spin_unlock_bh(&dispatcher_lock);
+		EVENTEXIT(return STATUS_TIMEOUT);
+	}
+
+	/* get the list of objects the thread needs to wait on and add
+	 * the thread on the wait list for each such object */
+	/* if *timeout == 0, this step will grab all the objects */
+	wait_done = 0;
+	for (i = 0; i < count; i++) {
+		dh = object[i];
+		EVENTTRACE("%p: event %p (%d)", current, dh, dh->signal_state);
+		wb[i].object = NULL;
+		if (grab_object(dh, current, 1)) {
+			EVENTTRACE("%p: no wait for %p (%d)",
+				   current, dh, dh->signal_state);
+			/* mark that we are not waiting on this object */
+			wb[i].thread = NULL;
+		} else {
+			wb[i].wait_done = &wait_done;
+			wb[i].thread = current;
+			EVENTTRACE("%p: wait for %p", current, dh);
+			InsertTailList(&dh->wait_blocks, &wb[i].list);
+		}
+	}
+	spin_unlock_bh(&dispatcher_lock);
+	if (wait_count == 0)
+		EVENTEXIT(return STATUS_SUCCESS);
+
+	assert(timeout == NULL || *timeout != 0);
+	if (timeout == NULL)
+		wait_hz = 0;
+	else
+		wait_hz = SYSTEM_TIME_TO_HZ(*timeout);
+
+	if (irql >= DISPATCH_LEVEL) {
+		WARNING("attempt to wait with irql %d", irql);
+		EVENTEXIT(return STATUS_INVALID_PARAMETER);
+	}
+	EVENTTRACE("%p: sleep for %ld on %p", current, wait_hz, &wait_done);
+	/* we don't honor 'alertable' - according to description for
+	 * this, even if waiting in non-alertable state, thread may be
+	 * alerted in some circumstances */
+	while (wait_count) {
+		res = wait_condition(wait_done, wait_hz, TASK_INTERRUPTIBLE);
+		spin_lock_bh(&dispatcher_lock);
+		EVENTTRACE("%p woke up: %d, %d", current, res, wait_done);
+		/* the event may have been set by the time
+		 * wrap_wait_event returned and spinlock obtained, so
+		 * don't rely on value of 'res' - check event status */
+		if (!wait_done) {
+			assert(res <= 0);
+			/* timed out or interrupted; remove from wait list */
+			for (i = 0; i < count; i++) {
+				if (!wb[i].thread)
+					continue;
+				EVENTTRACE("%p: timedout, dequeue %p (%p)",
+					   current, object[i], wb[i].object);
+				assert(wb[i].object == NULL);
+				RemoveEntryList(&wb[i].list);
+			}
+			spin_unlock_bh(&dispatcher_lock);
+			if (res < 0)
+				EVENTEXIT(return STATUS_ALERTED);
+			else
+				EVENTEXIT(return STATUS_TIMEOUT);
+		}
+		assert(res > 0);
+		/* woken because object(s) signaled */
+		for (i = 0; wait_count && i < count; i++) {
+			if (!wb[i].thread || !wb[i].object)
+				continue;
+			DBG_BLOCK(1) {
+				if (wb[i].object != object[i]) {
+					EVENTTRACE("oops %p != %p",
+						   wb[i].object, object[i]);
+					continue;
+				}
+			}
+			wait_count--;
+			if (wait_type == WaitAny) {
+				int j;
+				/* done; remove from rest of wait list */
+				for (j = i + 1; j < count; j++) {
+					if (wb[j].thread && !wb[j].object)
+						RemoveEntryList(&wb[j].list);
+				}
+				spin_unlock_bh(&dispatcher_lock);
+				EVENTEXIT(return STATUS_WAIT_0 + i);
+			}
+		}
+		wait_done = 0;
+		spin_unlock_bh(&dispatcher_lock);
+		if (wait_count == 0)
+			EVENTEXIT(return STATUS_SUCCESS);
+
+		/* this thread is still waiting for more objects, so
+		 * let it wait for remaining time and those objects */
+		if (timeout)
+			wait_hz = res;
+		else
+			wait_hz = 0;
+	}
+	/* should never reach here, but compiler wants return value */
+	ERROR("%p: wait_hz: %ld", current, wait_hz);
+	EVENTEXIT(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(KeWaitForSingleObject,5)
+	(void *object, KWAIT_REASON wait_reason, KPROCESSOR_MODE wait_mode,
+	 BOOLEAN alertable, LARGE_INTEGER *timeout)
+{
+	return KeWaitForMultipleObjects(1, &object, WaitAny, wait_reason,
+					wait_mode, alertable, timeout, NULL);
+}
+
+wstdcall void WIN_FUNC(KeInitializeEvent,3)
+	(struct nt_event *nt_event, enum event_type type, BOOLEAN state)
+{
+	EVENTENTER("event = %p, type = %d, state = %d", nt_event, type, state);
+	initialize_object(&nt_event->dh, (enum dh_type)type, state);
+	EVENTEXIT(return);
+}
+
+wstdcall LONG WIN_FUNC(KeSetEvent,3)
+	(struct nt_event *nt_event, KPRIORITY incr, BOOLEAN wait)
+{
+	LONG old_state;
+
+	EVENTENTER("%p, %d", nt_event, nt_event->dh.type);
+	if (wait == TRUE)
+		WARNING("wait = %d, not yet implemented", wait);
+	spin_lock_bh(&dispatcher_lock);
+	old_state = nt_event->dh.signal_state;
+	nt_event->dh.signal_state = 1;
+	if (old_state == 0)
+		object_signaled(&nt_event->dh);
+	spin_unlock_bh(&dispatcher_lock);
+	EVENTEXIT(return old_state);
+}
+
+wstdcall void WIN_FUNC(KeClearEvent,1)
+	(struct nt_event *nt_event)
+{
+	EVENTENTER("%p", nt_event);
+	nt_event->dh.signal_state = 0;
+	EVENTEXIT(return);
+}
+
+wstdcall LONG WIN_FUNC(KeResetEvent,1)
+	(struct nt_event *nt_event)
+{
+	LONG old_state;
+
+	EVENTENTER("%p", nt_event);
+	old_state = xchg(&nt_event->dh.signal_state, 0);
+	EVENTEXIT(return old_state);
+}
+
+wstdcall LONG WIN_FUNC(KeReadStateEvent,1)
+	(struct nt_event *nt_event)
+{
+	LONG state;
+
+	state = nt_event->dh.signal_state;
+	EVENTTRACE("%d", state);
+	return state;
+}
+
+wstdcall void WIN_FUNC(KeInitializeMutex,2)
+	(struct nt_mutex *mutex, ULONG level)
+{
+	EVENTENTER("%p", mutex);
+	initialize_object(&mutex->dh, MutexObject, 1);
+	mutex->dh.size = sizeof(*mutex);
+	InitializeListHead(&mutex->list);
+	mutex->abandoned = FALSE;
+	mutex->apc_disable = 1;
+	mutex->owner_thread = NULL;
+	EVENTEXIT(return);
+}
+
+wstdcall LONG WIN_FUNC(KeReleaseMutex,2)
+	(struct nt_mutex *mutex, BOOLEAN wait)
+{
+	LONG ret;
+	struct task_struct *thread;
+
+	EVENTENTER("%p, %d, %p", mutex, wait, current);
+	if (wait == TRUE)
+		WARNING("wait: %d", wait);
+	thread = current;
+	spin_lock_bh(&dispatcher_lock);
+	EVENTTRACE("%p, %p, %p, %d", mutex, thread, mutex->owner_thread,
+		   mutex->dh.signal_state);
+	if ((mutex->owner_thread == thread) && (mutex->dh.signal_state <= 0)) {
+		ret = mutex->dh.signal_state++;
+		if (ret == 0) {
+			mutex->owner_thread = NULL;
+			object_signaled(&mutex->dh);
+		}
+	} else {
+		ret = STATUS_MUTANT_NOT_OWNED;
+		WARNING("invalid mutex: %p, %p, %p", mutex, mutex->owner_thread,
+			thread);
+	}
+	EVENTTRACE("%p, %p, %p, %d", mutex, thread, mutex->owner_thread,
+		   mutex->dh.signal_state);
+	spin_unlock_bh(&dispatcher_lock);
+	EVENTEXIT(return ret);
+}
+
+wstdcall void WIN_FUNC(KeInitializeSemaphore,3)
+	(struct nt_semaphore *semaphore, LONG count, LONG limit)
+{
+	EVENTENTER("%p: %d", semaphore, count);
+	/* if limit > 1, we need to satisfy as many waits (until count
+	 * becomes 0); so we keep decrementing count every time a wait
+	 * is satisfied */
+	initialize_object(&semaphore->dh, SemaphoreObject, count);
+	semaphore->dh.size = sizeof(*semaphore);
+	semaphore->limit = limit;
+	EVENTEXIT(return);
+}
+
+wstdcall LONG WIN_FUNC(KeReleaseSemaphore,4)
+	(struct nt_semaphore *semaphore, KPRIORITY incr, LONG adjustment,
+	 BOOLEAN wait)
+{
+	LONG ret;
+
+	EVENTENTER("%p", semaphore);
+	spin_lock_bh(&dispatcher_lock);
+	ret = semaphore->dh.signal_state;
+	assert(ret >= 0);
+	if (semaphore->dh.signal_state + adjustment <= semaphore->limit)
+		semaphore->dh.signal_state += adjustment;
+	else {
+		WARNING("releasing %d over limit %d", adjustment,
+			semaphore->limit);
+		semaphore->dh.signal_state = semaphore->limit;
+	}
+	if (semaphore->dh.signal_state > 0)
+		object_signaled(&semaphore->dh);
+	spin_unlock_bh(&dispatcher_lock);
+	EVENTEXIT(return ret);
+}
+
+wstdcall NTSTATUS WIN_FUNC(KeDelayExecutionThread,3)
+	(KPROCESSOR_MODE wait_mode, BOOLEAN alertable, LARGE_INTEGER *interval)
+{
+	int res;
+	long timeout;
+
+	if (wait_mode != 0)
+		ERROR("invalid wait_mode %d", wait_mode);
+
+	timeout = SYSTEM_TIME_TO_HZ(*interval);
+	EVENTTRACE("%p, %lld, %ld", current, *interval, timeout);
+	if (timeout <= 0)
+		EVENTEXIT(return STATUS_SUCCESS);
+
+	if (alertable)
+		set_current_state(TASK_INTERRUPTIBLE);
+	else
+		set_current_state(TASK_UNINTERRUPTIBLE);
+
+	res = schedule_timeout(timeout);
+	EVENTTRACE("%p, %d", current, res);
+	if (res == 0)
+		EVENTEXIT(return STATUS_SUCCESS);
+	else
+		EVENTEXIT(return STATUS_ALERTED);
+}
+
+wstdcall ULONGLONG WIN_FUNC(KeQueryInterruptTime,0)
+	(void)
+{
+	EXIT5(return jiffies * TICKSPERJIFFY);
+}
+
+wstdcall ULONG WIN_FUNC(KeQueryTimeIncrement,0)
+	(void)
+{
+	EXIT5(return TICKSPERSEC / HZ);
+}
+
+wstdcall void WIN_FUNC(KeQuerySystemTime,1)
+	(LARGE_INTEGER *time)
+{
+	*time = ticks_1601();
+	TRACE5("%llu, %lu", *time, jiffies);
+}
+
+wstdcall void WIN_FUNC(KeQueryTickCount,1)
+	(LARGE_INTEGER *count)
+{
+	*count = jiffies;
+}
+
+wstdcall LARGE_INTEGER WIN_FUNC(KeQueryPerformanceCounter,1)
+	(LARGE_INTEGER *counter)
+{
+	if (counter)
+		*counter = HZ;
+	return jiffies;
+}
+
+wstdcall KAFFINITY WIN_FUNC(KeQueryActiveProcessors,0)
+	(void)
+{
+	int i, n;
+	KAFFINITY bits = 0;
+	n = num_online_cpus();
+	for (i = 0; i < n; i++)
+		bits = (bits << 1) | 1;
+	return bits;
+}
+
+struct nt_thread *get_current_nt_thread(void)
+{
+	struct task_struct *task = current;
+	struct nt_thread *thread;
+	struct common_object_header *header;
+
+	TRACE6("task: %p", task);
+	thread = NULL;
+	spin_lock_bh(&ntoskernel_lock);
+	nt_list_for_each_entry(header, &object_list, list) {
+		TRACE6("%p, %d", header, header->type);
+		if (header->type != OBJECT_TYPE_NT_THREAD)
+			break;
+		thread = HEADER_TO_OBJECT(header);
+		TRACE6("%p, %p", thread, thread->task);
+		if (thread->task == task)
+			break;
+		else
+			thread = NULL;
+	}
+	spin_unlock_bh(&ntoskernel_lock);
+	if (thread == NULL)
+		TRACE4("couldn't find thread for task %p, %d", task, task->pid);
+	TRACE6("%p", thread);
+	return thread;
+}
+
+static struct task_struct *get_nt_thread_task(struct nt_thread *thread)
+{
+	struct task_struct *task;
+	struct common_object_header *header;
+
+	TRACE6("%p", thread);
+	task = NULL;
+	spin_lock_bh(&ntoskernel_lock);
+	nt_list_for_each_entry(header, &object_list, list) {
+		TRACE6("%p, %d", header, header->type);
+		if (header->type != OBJECT_TYPE_NT_THREAD)
+			break;
+		if (thread == HEADER_TO_OBJECT(header)) {
+			task = thread->task;
+			break;
+		}
+	}
+	spin_unlock_bh(&ntoskernel_lock);
+	if (task == NULL)
+		TRACE2("%p: couldn't find task for %p", current, thread);
+	return task;
+}
+
+static struct nt_thread *create_nt_thread(struct task_struct *task)
+{
+	struct nt_thread *thread;
+	thread = allocate_object(sizeof(*thread), OBJECT_TYPE_NT_THREAD, NULL);
+	if (!thread) {
+		ERROR("couldn't allocate thread object");
+		EXIT2(return NULL);
+	}
+	thread->task = task;
+	if (task)
+		thread->pid = task->pid;
+	else
+		thread->pid = 0;
+	nt_spin_lock_init(&thread->lock);
+	InitializeListHead(&thread->irps);
+	initialize_object(&thread->dh, ThreadObject, 0);
+	thread->dh.size = sizeof(*thread);
+	thread->prio = LOW_PRIORITY;
+	return thread;
+}
+
+wstdcall struct nt_thread *WIN_FUNC(KeGetCurrentThread,0)
+	(void)
+{
+	struct nt_thread *thread = get_current_nt_thread();
+	TRACE2("%p, %p", thread, current);
+	return thread;
+}
+
+wstdcall KPRIORITY WIN_FUNC(KeQueryPriorityThread,1)
+	(struct nt_thread *thread)
+{
+	KPRIORITY prio;
+	struct task_struct *task;
+
+	TRACE2("%p", thread);
+#ifdef CONFIG_X86_64
+	/* sis163u driver for amd64 passes 0x1f from thread created by
+	 * PsCreateSystemThread - no idea what is 0x1f */
+	if (thread == (void *)0x1f)
+		thread = get_current_nt_thread();
+#endif
+	if (!thread) {
+		TRACE2("invalid thread");
+		EXIT2(return LOW_REALTIME_PRIORITY);
+	}
+	task = get_nt_thread_task(thread);
+	if (!task) {
+		TRACE2("couldn't find task for thread: %p", thread);
+		EXIT2(return LOW_REALTIME_PRIORITY);
+	}
+
+	prio = thread->prio;
+
+	TRACE2("%d", prio);
+	return prio;
+}
+
+wstdcall KPRIORITY WIN_FUNC(KeSetPriorityThread,2)
+	(struct nt_thread *thread, KPRIORITY prio)
+{
+	KPRIORITY old_prio;
+	struct task_struct *task;
+
+	TRACE2("thread: %p, priority = %u", thread, prio);
+#ifdef CONFIG_X86_64
+	if (thread == (void *)0x1f)
+		thread = get_current_nt_thread();
+#endif
+	if (!thread) {
+		TRACE2("invalid thread");
+		EXIT2(return LOW_REALTIME_PRIORITY);
+	}
+	task = get_nt_thread_task(thread);
+	if (!task) {
+		TRACE2("couldn't find task for thread: %p", thread);
+		EXIT2(return LOW_REALTIME_PRIORITY);
+	}
+
+	old_prio = thread->prio;
+	thread->prio = prio;
+
+	TRACE2("%d, %d", old_prio, thread->prio);
+	return old_prio;
+}
+
+struct thread_trampoline {
+	void (*func)(void *) wstdcall;
+	void *ctx;
+	struct nt_thread *thread;
+	struct completion started;
+};
+
+static int ntdriver_thread(void *data)
+{
+	struct thread_trampoline *thread_tramp = data;
+	/* yes, a tramp! */
+	typeof(thread_tramp->func) func = thread_tramp->func;
+	typeof(thread_tramp->ctx) ctx = thread_tramp->ctx;
+
+	thread_tramp->thread->task = current;
+	thread_tramp->thread->pid = current->pid;
+	TRACE2("thread: %p, task: %p (%d)", thread_tramp->thread,
+	       current, current->pid);
+	complete(&thread_tramp->started);
+
+#ifdef PF_NOFREEZE
+	current->flags |= PF_NOFREEZE;
+#endif
+	strncpy(current->comm, "ntdriver", sizeof(current->comm));
+	current->comm[sizeof(current->comm)-1] = 0;
+	LIN2WIN1(func, ctx);
+	ERROR("task: %p", current);
+	return 0;
+}
+
+wstdcall NTSTATUS WIN_FUNC(PsCreateSystemThread,7)
+	(void **handle, ULONG access, void *obj_attr, void *process,
+	 void *client_id, void (*func)(void *) wstdcall, void *ctx)
+{
+	struct thread_trampoline thread_tramp;
+
+	ENTER2("handle = %p, access = %u, obj_attr = %p, process = %p, "
+	       "client_id = %p, func = %p, context = %p", handle, access,
+	       obj_attr, process, client_id, func, ctx);
+
+	thread_tramp.thread = create_nt_thread(NULL);
+	if (!thread_tramp.thread) {
+		ERROR("couldn't allocate thread object");
+		EXIT2(return STATUS_RESOURCES);
+	}
+	TRACE2("thread: %p", thread_tramp.thread);
+	thread_tramp.func = func;
+	thread_tramp.ctx = ctx;
+	init_completion(&thread_tramp.started);
+
+	thread_tramp.thread->task = kthread_run(ntdriver_thread,
+						&thread_tramp, "ntdriver");
+	if (IS_ERR(thread_tramp.thread->task)) {
+		free_object(thread_tramp.thread);
+		EXIT2(return STATUS_FAILURE);
+	}
+	TRACE2("created task: %p", thread_tramp.thread->task);
+
+	wait_for_completion(&thread_tramp.started);
+	*handle = OBJECT_TO_HEADER(thread_tramp.thread);
+	TRACE2("created thread: %p, %p", thread_tramp.thread, *handle);
+	EXIT2(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(PsTerminateSystemThread,1)
+	(NTSTATUS status)
+{
+	struct nt_thread *thread;
+
+	TRACE2("%p, %08X", current, status);
+	thread = get_current_nt_thread();
+	TRACE2("%p", thread);
+	if (thread) {
+		KeSetEvent((struct nt_event *)&thread->dh, 0, FALSE);
+		while (1) {
+			struct nt_list *ent;
+			struct irp *irp;
+			KIRQL irql;
+			irql = nt_spin_lock_irql(&thread->lock, DISPATCH_LEVEL);
+			ent = RemoveHeadList(&thread->irps);
+			nt_spin_unlock_irql(&thread->lock, irql);
+			if (!ent)
+				break;
+			irp = container_of(ent, struct irp, thread_list);
+			IOTRACE("%p", irp);
+			IoCancelIrp(irp);
+		}
+		/* the driver may later query this status with
+		 * ZwQueryInformationThread */
+		thread->status = status;
+	} else
+		ERROR("couldn't find thread for task: %p", current);
+
+	complete_and_exit(NULL, status);
+	ERROR("oops: %p, %d", thread->task, thread->pid);
+	return STATUS_FAILURE;
+}
+
+wstdcall BOOLEAN WIN_FUNC(KeRemoveEntryDeviceQueue,2)
+	(struct kdevice_queue *dev_queue, struct kdevice_queue_entry *entry)
+{
+	struct kdevice_queue_entry *e;
+	KIRQL irql;
+
+	irql = nt_spin_lock_irql(&dev_queue->lock, DISPATCH_LEVEL);
+	nt_list_for_each_entry(e, &dev_queue->list, list) {
+		if (e == entry) {
+			RemoveEntryList(&e->list);
+			nt_spin_unlock_irql(&dev_queue->lock, irql);
+			return TRUE;
+		}
+	}
+	nt_spin_unlock_irql(&dev_queue->lock, irql);
+	return FALSE;
+}
+
+wstdcall BOOLEAN WIN_FUNC(KeSynchronizeExecution,3)
+	(struct kinterrupt *interrupt, PKSYNCHRONIZE_ROUTINE synch_routine,
+	 void *ctx)
+{
+	BOOLEAN ret;
+	unsigned long flags;
+
+	nt_spin_lock_irqsave(interrupt->actual_lock, flags);
+	ret = LIN2WIN1(synch_routine, ctx);
+	nt_spin_unlock_irqrestore(interrupt->actual_lock, flags);
+	TRACE6("%d", ret);
+	return ret;
+}
+
+wstdcall BOOLEAN WIN_FUNC(KeRegisterBugCheckReasonCallback,4)
+	(void *callback_record, void *callback_routine, UINT reason,
+	 char *component)
+{
+	TRACE1("callback_record: %p, callback_routine: %p, reason: %d, "
+	       "component: %s", callback_record, callback_routine, reason,
+	       component);
+	TODO();
+	return FALSE;
+}
+
+wstdcall BOOLEAN WIN_FUNC(KeDeregisterBugCheckReasonCallback,1)
+	(void *callback_record)
+{
+	TRACE1("callback_record: %p", callback_record);
+	TODO();
+	return TRUE;
+}
+
+wstdcall void *WIN_FUNC(MmAllocateContiguousMemorySpecifyCache,5)
+	(SIZE_T size, PHYSICAL_ADDRESS lowest, PHYSICAL_ADDRESS highest,
+	 PHYSICAL_ADDRESS boundary, enum memory_caching_type cache_type)
+{
+	void *addr;
+	gfp_t flags;
+
+	ENTER2("%zu, 0x%llx, 0x%llx, 0x%llx, %d", size, lowest,
+	       highest, boundary, cache_type);
+	flags = irql_gfp();
+	addr = wrap_get_free_pages(flags, size);
+	TRACE2("%p, %zu, 0x%x", addr, size, flags);
+	if (addr && ((virt_to_phys(addr) + size) <= highest))
+		EXIT2(return addr);
+#ifdef CONFIG_X86_64
+	/* GFP_DMA is really only 16MB even on x86-64, but there is no
+	 * other zone available */
+	if (highest <= DMA_BIT_MASK(31))
+		flags |= __GFP_DMA;
+	else if (highest <= DMA_BIT_MASK(32))
+		flags |= __GFP_DMA32;
+#else
+	if (highest <= DMA_BIT_MASK(24))
+		flags |= __GFP_DMA;
+	else if (highest > DMA_BIT_MASK(30))
+		flags |= __GFP_HIGHMEM;
+#endif
+	if (addr)
+		free_pages((unsigned long)addr, get_order(size));
+	addr = wrap_get_free_pages(flags, size);
+	TRACE2("%p, %zu, 0x%x", addr, size, flags);
+	return addr;
+}
+
+wstdcall void WIN_FUNC(MmFreeContiguousMemorySpecifyCache,3)
+	(void *base, SIZE_T size, enum memory_caching_type cache_type)
+{
+	TRACE2("%p, %zu", base, size);
+	free_pages((unsigned long)base, get_order(size));
+}
+
+wstdcall PHYSICAL_ADDRESS WIN_FUNC(MmGetPhysicalAddress,1)
+	(void *base)
+{
+	unsigned long phy = virt_to_phys(base);
+	TRACE2("%p, %p", base, (void *)phy);
+	return phy;
+}
+
+/* Atheros card with pciid 168C:0014 calls this function with 0xf0000
+ * and 0xf6ef0 address, and then check for things that seem to be
+ * related to ACPI: "_SM_" and "_DMI_". This may be the hack they do
+ * to check if this card is installed in IBM thinkpads; we can
+ * probably get this device to work if we create a buffer with the
+ * strings as required by the driver and return virtual address for
+ * that address instead */
+wstdcall void __iomem *WIN_FUNC(MmMapIoSpace,3)
+	(PHYSICAL_ADDRESS phys_addr, SIZE_T size,
+	 enum memory_caching_type cache)
+{
+	void __iomem *virt;
+	ENTER1("cache type: %d", cache);
+	if (cache == MmCached) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
+		virt = ioremap_cache(phys_addr, size);
+#else
+		virt = ioremap(phys_addr, size);
+#endif
+	} else {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
+		virt = ioremap(phys_addr, size);
+#else
+		virt = ioremap_nocache(phys_addr, size);
+#endif
+	}
+	TRACE1("%llx, %zu, %p", phys_addr, size, virt);
+	return virt;
+}
+
+wstdcall void WIN_FUNC(MmUnmapIoSpace,2)
+	(void __iomem *addr, SIZE_T size)
+{
+	ENTER1("%p, %zu", addr, size);
+	iounmap(addr);
+	return;
+}
+
+wstdcall ULONG WIN_FUNC(MmSizeOfMdl,2)
+	(void *base, ULONG length)
+{
+	return sizeof(struct mdl) +
+	       (sizeof(PFN_NUMBER) * SPAN_PAGES(base, length));
+}
+
+struct mdl *allocate_init_mdl(void *virt, ULONG length)
+{
+	struct wrap_mdl *wrap_mdl;
+	struct mdl *mdl;
+	int mdl_size = MmSizeOfMdl(virt, length);
+
+	if (mdl_size <= MDL_CACHE_SIZE) {
+		wrap_mdl = kmem_cache_alloc(mdl_cache, irql_gfp());
+		if (!wrap_mdl)
+			return NULL;
+		spin_lock_bh(&dispatcher_lock);
+		InsertHeadList(&wrap_mdl_list, &wrap_mdl->list);
+		spin_unlock_bh(&dispatcher_lock);
+		mdl = wrap_mdl->mdl;
+		TRACE3("allocated mdl from cache: %p(%p), %p(%d)",
+		       wrap_mdl, mdl, virt, length);
+		memset(mdl, 0, MDL_CACHE_SIZE);
+		MmInitializeMdl(mdl, virt, length);
+		/* mark the MDL as allocated from cache pool so when
+		 * it is freed, we free it back to the pool */
+		mdl->flags = MDL_ALLOCATED_FIXED_SIZE | MDL_CACHE_ALLOCATED;
+	} else {
+		wrap_mdl =
+			kmalloc(sizeof(*wrap_mdl) + mdl_size, irql_gfp());
+		if (!wrap_mdl)
+			return NULL;
+		mdl = wrap_mdl->mdl;
+		TRACE3("allocated mdl from memory: %p(%p), %p(%d)",
+		       wrap_mdl, mdl, virt, length);
+		spin_lock_bh(&dispatcher_lock);
+		InsertHeadList(&wrap_mdl_list, &wrap_mdl->list);
+		spin_unlock_bh(&dispatcher_lock);
+		memset(mdl, 0, mdl_size);
+		MmInitializeMdl(mdl, virt, length);
+		mdl->flags = MDL_ALLOCATED_FIXED_SIZE;
+	}
+	return mdl;
+}
+
+void free_mdl(struct mdl *mdl)
+{
+	/* A driver may allocate Mdl with NdisAllocateBuffer and free
+	 * with IoFreeMdl (e.g., 64-bit Broadcom). Since we need to
+	 * treat buffers allocated with Ndis calls differently, we
+	 * must call NdisFreeBuffer if it is allocated with Ndis
+	 * function. We set 'pool' field in Ndis functions. */
+	if (!mdl)
+		return;
+	if (mdl->pool)
+		NdisFreeBuffer(mdl);
+	else {
+		struct wrap_mdl *wrap_mdl = (struct wrap_mdl *)
+			((char *)mdl - offsetof(struct wrap_mdl, mdl));
+		spin_lock_bh(&dispatcher_lock);
+		RemoveEntryList(&wrap_mdl->list);
+		spin_unlock_bh(&dispatcher_lock);
+
+		if (mdl->flags & MDL_CACHE_ALLOCATED) {
+			TRACE3("freeing mdl cache: %p, %p, %p",
+			       wrap_mdl, mdl, mdl->mappedsystemva);
+			kmem_cache_free(mdl_cache, wrap_mdl);
+		} else {
+			TRACE3("freeing mdl: %p, %p, %p",
+			       wrap_mdl, mdl, mdl->mappedsystemva);
+			kfree(wrap_mdl);
+		}
+	}
+	return;
+}
+
+wstdcall void WIN_FUNC(IoBuildPartialMdl,4)
+	(struct mdl *source, struct mdl *target, void *virt, ULONG length)
+{
+	MmInitializeMdl(target, virt, length);
+	target->flags |= MDL_PARTIAL;
+}
+
+wstdcall void WIN_FUNC(MmBuildMdlForNonPagedPool,1)
+	(struct mdl *mdl)
+{
+	PFN_NUMBER *mdl_pages;
+	int i, n;
+
+	ENTER4("%p", mdl);
+	/* already mapped */
+//	mdl->mappedsystemva = MmGetMdlVirtualAddress(mdl);
+	mdl->flags |= MDL_SOURCE_IS_NONPAGED_POOL;
+	TRACE4("%p, %p, %p, %d, %d", mdl, mdl->mappedsystemva, mdl->startva,
+	       mdl->byteoffset, mdl->bytecount);
+	n = SPAN_PAGES(MmGetSystemAddressForMdl(mdl), MmGetMdlByteCount(mdl));
+	if (n > MDL_CACHE_PAGES)
+		WARNING("%p, %d, %d", MmGetSystemAddressForMdl(mdl),
+			MmGetMdlByteCount(mdl), n);
+	mdl_pages = MmGetMdlPfnArray(mdl);
+	for (i = 0; i < n; i++)
+		mdl_pages[i] = (ULONG_PTR)mdl->startva + (i * PAGE_SIZE);
+	EXIT4(return);
+}
+
+wstdcall void *WIN_FUNC(MmMapLockedPages,2)
+	(struct mdl *mdl, KPROCESSOR_MODE access_mode)
+{
+	/* already mapped */
+//	mdl->mappedsystemva = MmGetMdlVirtualAddress(mdl);
+	mdl->flags |= MDL_MAPPED_TO_SYSTEM_VA;
+	/* what is the need for MDL_PARTIAL_HAS_BEEN_MAPPED? */
+	if (mdl->flags & MDL_PARTIAL)
+		mdl->flags |= MDL_PARTIAL_HAS_BEEN_MAPPED;
+	return mdl->mappedsystemva;
+}
+
+wstdcall void *WIN_FUNC(MmMapLockedPagesSpecifyCache,6)
+	(struct mdl *mdl, KPROCESSOR_MODE access_mode,
+	 enum memory_caching_type cache_type, void *base_address,
+	 ULONG bug_check, enum mm_page_priority priority)
+{
+	return MmMapLockedPages(mdl, access_mode);
+}
+
+wstdcall void WIN_FUNC(MmUnmapLockedPages,2)
+	(void *base, struct mdl *mdl)
+{
+	mdl->flags &= ~MDL_MAPPED_TO_SYSTEM_VA;
+	return;
+}
+
+wstdcall void WIN_FUNC(MmProbeAndLockPages,3)
+	(struct mdl *mdl, KPROCESSOR_MODE access_mode,
+	 enum lock_operation operation)
+{
+	/* already locked */
+	mdl->flags |= MDL_PAGES_LOCKED;
+	return;
+}
+
+wstdcall void WIN_FUNC(MmUnlockPages,1)
+	(struct mdl *mdl)
+{
+	mdl->flags &= ~MDL_PAGES_LOCKED;
+	return;
+}
+
+wstdcall BOOLEAN WIN_FUNC(MmIsAddressValid,1)
+	(void *virt_addr)
+{
+	if (virt_addr_valid(virt_addr))
+		return TRUE;
+	else
+		return FALSE;
+}
+
+wstdcall void *WIN_FUNC(MmLockPagableDataSection,1)
+	(void *address)
+{
+	return address;
+}
+
+wstdcall void WIN_FUNC(MmUnlockPagableImageSection,1)
+	(void *handle)
+{
+	return;
+}
+
+wstdcall NTSTATUS WIN_FUNC(ObReferenceObjectByHandle,6)
+	(void *handle, ACCESS_MASK desired_access, void *obj_type,
+	 KPROCESSOR_MODE access_mode, void **object, void *handle_info)
+{
+	struct common_object_header *hdr;
+
+	TRACE2("%p", handle);
+	hdr = HANDLE_TO_HEADER(handle);
+	atomic_inc_var(hdr->ref_count);
+	*object = HEADER_TO_OBJECT(hdr);
+	TRACE2("%p, %p, %d, %p", hdr, object, hdr->ref_count, *object);
+	return STATUS_SUCCESS;
+}
+
+/* DDK doesn't say if return value should be before incrementing or
+ * after incrementing reference count, but according to #reactos
+ * developers, it should be return value after incrementing */
+wfastcall LONG WIN_FUNC(ObfReferenceObject,1)
+	(void *object)
+{
+	struct common_object_header *hdr;
+	LONG ret;
+
+	hdr = OBJECT_TO_HEADER(object);
+	ret = post_atomic_add(hdr->ref_count, 1);
+	TRACE2("%p, %d, %p", hdr, hdr->ref_count, object);
+	return ret;
+}
+
+static int dereference_object(void *object)
+{
+	struct common_object_header *hdr;
+	int ref_count;
+
+	ENTER2("object: %p", object);
+	hdr = OBJECT_TO_HEADER(object);
+	TRACE2("hdr: %p", hdr);
+	ref_count = post_atomic_add(hdr->ref_count, -1);
+	TRACE2("object: %p, %d", object, ref_count);
+	if (ref_count < 0)
+		ERROR("invalid object: %p (%d)", object, ref_count);
+	if (ref_count <= 0) {
+		free_object(object);
+		return 1;
+	} else
+		return 0;
+}
+
+wfastcall void WIN_FUNC(ObfDereferenceObject,1)
+	(void *object)
+{
+	TRACE2("%p", object);
+	dereference_object(object);
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwCreateFile,11)
+	(void **handle, ACCESS_MASK access_mask,
+	 struct object_attributes *obj_attr, struct io_status_block *iosb,
+	 LARGE_INTEGER *size, ULONG file_attr, ULONG share_access,
+	 ULONG create_disposition, ULONG create_options, void *ea_buffer,
+	 ULONG ea_length)
+{
+	struct common_object_header *coh;
+	struct file_object *fo;
+	struct ansi_string ansi;
+	struct wrap_bin_file *bin_file;
+	char *file_basename;
+	NTSTATUS status;
+
+	spin_lock_bh(&ntoskernel_lock);
+	nt_list_for_each_entry(coh, &object_list, list) {
+		if (coh->type != OBJECT_TYPE_FILE)
+			continue;
+		/* TODO: check if file is opened in shared mode */
+		if (!RtlCompareUnicodeString(&coh->name, obj_attr->name, TRUE)) {
+			fo = HEADER_TO_OBJECT(coh);
+			bin_file = fo->wrap_bin_file;
+			*handle = coh;
+			spin_unlock_bh(&ntoskernel_lock);
+			ObReferenceObject(fo);
+			iosb->status = FILE_OPENED;
+			iosb->info = bin_file->size;
+			EXIT2(return STATUS_SUCCESS);
+		}
+	}
+	spin_unlock_bh(&ntoskernel_lock);
+
+	if (RtlUnicodeStringToAnsiString(&ansi, obj_attr->name, TRUE) !=
+	    STATUS_SUCCESS)
+		EXIT2(return STATUS_INSUFFICIENT_RESOURCES);
+
+	file_basename = strrchr(ansi.buf, '\\');
+	if (file_basename)
+		file_basename++;
+	else
+		file_basename = ansi.buf;
+	TRACE2("file: '%s', '%s'", ansi.buf, file_basename);
+
+	fo = allocate_object(sizeof(struct file_object), OBJECT_TYPE_FILE,
+			     obj_attr->name);
+	if (!fo) {
+		RtlFreeAnsiString(&ansi);
+		iosb->status = STATUS_INSUFFICIENT_RESOURCES;
+		iosb->info = 0;
+		EXIT2(return STATUS_FAILURE);
+	}
+	coh = OBJECT_TO_HEADER(fo);
+	bin_file = get_bin_file(file_basename);
+	if (bin_file) {
+		TRACE2("%s, %s", bin_file->name, file_basename);
+		fo->flags = FILE_OPENED;
+	} else if (access_mask & FILE_WRITE_DATA) {
+		bin_file = kzalloc(sizeof(*bin_file), GFP_KERNEL);
+		if (bin_file) {
+			strncpy(bin_file->name, file_basename,
+				sizeof(bin_file->name));
+			bin_file->name[sizeof(bin_file->name)-1] = 0;
+			bin_file->data = vmalloc(*size);
+			if (bin_file->data) {
+				memset(bin_file->data, 0, *size);
+				bin_file->size = *size;
+				fo->flags = FILE_CREATED;
+			} else {
+				kfree(bin_file);
+				bin_file = NULL;
+			}
+		}
+	} else
+		bin_file = NULL;
+
+	RtlFreeAnsiString(&ansi);
+	if (!bin_file) {
+		iosb->status = FILE_DOES_NOT_EXIST;
+		iosb->info = 0;
+		free_object(fo);
+		EXIT2(return STATUS_FAILURE);
+	}
+
+	fo->wrap_bin_file = bin_file;
+	fo->current_byte_offset = 0;
+	if (access_mask & FILE_READ_DATA)
+		fo->read_access = TRUE;
+	if (access_mask & FILE_WRITE_DATA)
+		fo->write_access = TRUE;
+	iosb->status = FILE_OPENED;
+	iosb->info = bin_file->size;
+	*handle = coh;
+	TRACE2("handle: %p", *handle);
+	status = STATUS_SUCCESS;
+	EXIT2(return status);
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwOpenFile,6)
+	(void **handle, ACCESS_MASK access_mask,
+	 struct object_attributes *obj_attr, struct io_status_block *iosb,
+	 ULONG share_access, ULONG open_options)
+{
+	LARGE_INTEGER size;
+	return ZwCreateFile(handle, access_mask, obj_attr, iosb, &size, 0,
+			    share_access, 0, open_options, NULL, 0);
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwReadFile,9)
+	(void *handle, struct nt_event *event, void *apc_routine,
+	 void *apc_context, struct io_status_block *iosb, void *buffer,
+	 ULONG length, LARGE_INTEGER *byte_offset, ULONG *key)
+{
+	struct file_object *fo;
+	struct common_object_header *coh;
+	ULONG count;
+	size_t offset;
+	struct wrap_bin_file *file;
+
+	TRACE2("%p", handle);
+	coh = handle;
+	if (coh->type != OBJECT_TYPE_FILE) {
+		ERROR("handle %p is invalid: %d", handle, coh->type);
+		EXIT2(return STATUS_FAILURE);
+	}
+	fo = HANDLE_TO_OBJECT(coh);
+	file = fo->wrap_bin_file;
+	TRACE2("file: %s (%zu)", file->name, file->size);
+	spin_lock_bh(&ntoskernel_lock);
+	if (byte_offset)
+		offset = *byte_offset;
+	else
+		offset = fo->current_byte_offset;
+	count = min((size_t)length, file->size - offset);
+	TRACE2("count: %u, offset: %zu, length: %u", count, offset, length);
+	memcpy(buffer, ((void *)file->data) + offset, count);
+	fo->current_byte_offset = offset + count;
+	spin_unlock_bh(&ntoskernel_lock);
+	iosb->status = STATUS_SUCCESS;
+	iosb->info = count;
+	EXIT2(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwWriteFile,9)
+	(void *handle, struct nt_event *event, void *apc_routine,
+	 void *apc_context, struct io_status_block *iosb, void *buffer,
+	 ULONG length, LARGE_INTEGER *byte_offset, ULONG *key)
+{
+	struct file_object *fo;
+	struct common_object_header *coh;
+	struct wrap_bin_file *file;
+	unsigned long offset;
+
+	TRACE2("%p", handle);
+	coh = handle;
+	if (coh->type != OBJECT_TYPE_FILE) {
+		ERROR("handle %p is invalid: %d", handle, coh->type);
+		EXIT2(return STATUS_FAILURE);
+	}
+	fo = HANDLE_TO_OBJECT(coh);
+	file = fo->wrap_bin_file;
+	TRACE2("file: %zu, %u", file->size, length);
+	spin_lock_bh(&ntoskernel_lock);
+	if (byte_offset)
+		offset = *byte_offset;
+	else
+		offset = fo->current_byte_offset;
+	if (length + offset > file->size) {
+		WARNING("%lu, %zu", length + offset, file->size);
+		/* TODO: implement writing past end of current size */
+		iosb->status = STATUS_FAILURE;
+		iosb->info = 0;
+	} else {
+		memcpy(file->data + offset, buffer, length);
+		iosb->status = STATUS_SUCCESS;
+		iosb->info = length;
+		fo->current_byte_offset = offset + length;
+	}
+	spin_unlock_bh(&ntoskernel_lock);
+	EXIT2(return iosb->status);
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwClose,1)
+	(void *handle)
+{
+	struct common_object_header *coh;
+
+	TRACE2("%p", handle);
+	if (handle == NULL) {
+		TRACE1("");
+		EXIT2(return STATUS_SUCCESS);
+	}
+	coh = handle;
+	if (coh->type == OBJECT_TYPE_FILE) {
+		struct file_object *fo;
+		struct wrap_bin_file *bin_file;
+		typeof(fo->flags) flags;
+
+		fo = HANDLE_TO_OBJECT(handle);
+		flags = fo->flags;
+		bin_file = fo->wrap_bin_file;
+		if (dereference_object(fo)) {
+			if (flags == FILE_CREATED) {
+				vfree(bin_file->data);
+				kfree(bin_file);
+			} else
+				free_bin_file(bin_file);
+		}
+	} else if (coh->type == OBJECT_TYPE_NT_THREAD) {
+		struct nt_thread *thread = HANDLE_TO_OBJECT(handle);
+		TRACE2("thread: %p (%p)", thread, handle);
+		ObDereferenceObject(thread);
+	} else {
+		/* TODO: can we just dereference object here? */
+		WARNING("closing handle 0x%x not implemented", coh->type);
+	}
+	EXIT2(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwQueryInformationFile,5)
+	(void *handle, struct io_status_block *iosb, void *info,
+	 ULONG length, enum file_info_class class)
+{
+	struct file_object *fo;
+	struct file_name_info *fni;
+	struct file_std_info *fsi;
+	struct wrap_bin_file *file;
+	struct common_object_header *coh;
+
+	ENTER2("%p", handle);
+	coh = handle;
+	if (coh->type != OBJECT_TYPE_FILE) {
+		ERROR("handle %p is invalid: %d", coh, coh->type);
+		EXIT2(return STATUS_FAILURE);
+	}
+	fo = HANDLE_TO_OBJECT(handle);
+	TRACE2("fo: %p, %d", fo, class);
+	switch (class) {
+	case FileNameInformation:
+		fni = info;
+		fni->length = min(length, (typeof(length))coh->name.length);
+		memcpy(fni->name, coh->name.buf, fni->length);
+		iosb->status = STATUS_SUCCESS;
+		iosb->info = fni->length;
+		break;
+	case FileStandardInformation:
+		fsi = info;
+		file = fo->wrap_bin_file;
+		fsi->alloc_size = file->size;
+		fsi->eof = file->size;
+		fsi->num_links = 1;
+		fsi->delete_pending = FALSE;
+		fsi->dir = FALSE;
+		iosb->status = STATUS_SUCCESS;
+		iosb->info = 0;
+		break;
+	default:
+		WARNING("type %d not implemented yet", class);
+		iosb->status = STATUS_FAILURE;
+		iosb->info = 0;
+		break;
+	}
+	EXIT2(return iosb->status);
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwOpenSection,3)
+	(void **handle, ACCESS_MASK access, struct object_attributes *obj_attrs)
+{
+	INFO("%p, 0x%x, %d", obj_attrs, obj_attrs->attributes, access);
+	TODO();
+	*handle = obj_attrs;
+	return STATUS_SUCCESS;
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwMapViewOfSection,10)
+	(void *secn_handle, void *process_handle, void **base_address,
+	 ULONG zero_bits, SIZE_T commit_size, LARGE_INTEGER *secn_offset,
+	 SIZE_T *view_size, enum section_inherit inherit, ULONG alloc_type,
+	 ULONG protect)
+{
+	INFO("%p, %p, %p", secn_handle, process_handle, base_address);
+	TODO();
+	*base_address = (void *)0xdeadbeef;
+	return STATUS_SUCCESS;
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwUnmapViewOfSection,2)
+	(void *process_handle, void *base_address)
+{
+	INFO("%p, %p", process_handle, base_address);
+	TODO();
+	return STATUS_SUCCESS;
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwCreateKey,7)
+	(void **handle, ACCESS_MASK desired_access,
+	 struct object_attributes *attr, ULONG title_index,
+	 struct unicode_string *class, ULONG create_options,
+	 ULONG *disposition)
+{
+	struct ansi_string ansi;
+	if (RtlUnicodeStringToAnsiString(&ansi, attr->name, TRUE) ==
+	    STATUS_SUCCESS) {
+		TRACE1("key: %s", ansi.buf);
+		RtlFreeAnsiString(&ansi);
+	}
+	*handle = NULL;
+	return STATUS_SUCCESS;
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwOpenKey,3)
+	(void **handle, ACCESS_MASK desired_access,
+	 struct object_attributes *attr)
+{
+	struct ansi_string ansi;
+	if (RtlUnicodeStringToAnsiString(&ansi, attr->name, TRUE) ==
+	    STATUS_SUCCESS) {
+		TRACE1("key: %s", ansi.buf);
+		RtlFreeAnsiString(&ansi);
+	}
+	*handle = NULL;
+	return STATUS_SUCCESS;
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwSetValueKey,6)
+	(void *handle, struct unicode_string *name, ULONG title_index,
+	 ULONG type, void *data, ULONG data_size)
+{
+	struct ansi_string ansi;
+	if (RtlUnicodeStringToAnsiString(&ansi, name, TRUE) ==
+	    STATUS_SUCCESS) {
+		TRACE1("key: %s", ansi.buf);
+		RtlFreeAnsiString(&ansi);
+	}
+	return STATUS_SUCCESS;
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwQueryValueKey,6)
+	(void *handle, struct unicode_string *name,
+	 enum key_value_information_class class, void *info,
+	 ULONG length, ULONG *res_length)
+{
+	struct ansi_string ansi;
+	if (RtlUnicodeStringToAnsiString(&ansi, name, TRUE) == STATUS_SUCCESS) {
+		TRACE1("key: %s", ansi.buf);
+		RtlFreeAnsiString(&ansi);
+	}
+	TODO();
+	return STATUS_INVALID_PARAMETER;
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwDeleteKey,1)
+	(void *handle)
+{
+	ENTER2("%p", handle);
+	return STATUS_SUCCESS;
+}
+
+wstdcall NTSTATUS WIN_FUNC(ZwPowerInformation,5)
+	(INT info_level, void *in_buf, ULONG in_buf_len, void *out_buf,
+	 ULONG out_buf_len)
+{
+	INFO("%d, %u, %u", info_level, in_buf_len, out_buf_len);
+	TODO();
+	return STATUS_ACCESS_DENIED;
+}
+
+wstdcall NTSTATUS WIN_FUNC(WmiSystemControl,4)
+	(struct wmilib_context *info, struct device_object *dev_obj,
+	 struct irp *irp, void *irp_disposition)
+{
+	TODO();
+	return STATUS_SUCCESS;
+}
+
+wstdcall NTSTATUS WIN_FUNC(WmiCompleteRequest,5)
+	(struct device_object *dev_obj, struct irp *irp, NTSTATUS status,
+	 ULONG buffer_used, CCHAR priority_boost)
+{
+	TODO();
+	return STATUS_SUCCESS;
+}
+
+noregparm NTSTATUS WIN_FUNC(WmiTraceMessage,12)
+	(void *tracehandle, ULONG message_flags,
+	 void *message_guid, USHORT message_no, ...)
+{
+	TODO();
+	EXIT2(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(WmiQueryTraceInformation,4)
+	(enum trace_information_class trace_info_class, void *trace_info,
+	 ULONG *req_length, void *buf)
+{
+	TODO();
+	EXIT2(return STATUS_SUCCESS);
+}
+
+/* this function can't be wstdcall as it takes variable number of args */
+__attribute__((format(printf, 1, 2)))
+noregparm ULONG WIN_FUNC(DbgPrint,12)
+	(char *format, ...)
+{
+#if DEBUG >= 1
+	va_list args;
+	static char buf[100];
+
+	va_start(args, format);
+	vsnprintf(buf, sizeof(buf), format, args);
+	printk(KERN_DEBUG "%s (%s): %s", DRIVER_NAME, __func__, buf);
+	va_end(args);
+#endif
+	return STATUS_SUCCESS;
+}
+
+__attribute__((format(printf, 3, 4)))
+noregparm ULONG WIN_FUNC(DbgPrintEx,12)
+	(ULONG component_id, ULONG severity, char *format, ...)
+{
+#if DEBUG >= 1
+	va_list args;
+	static char buf[100];
+
+	va_start(args, format);
+	vsnprintf(buf, sizeof(buf), format, args);
+	TRACE1("component_id: %d, severity: %d\n", component_id, severity);
+	printk(KERN_DEBUG "%s (%s): %s", DRIVER_NAME, __func__, buf);
+	va_end(args);
+#endif
+	return STATUS_SUCCESS;
+}
+
+wstdcall void WIN_FUNC(KeBugCheck,1)
+	(ULONG code)
+{
+	ERROR("Unrecoverable error reported by the driver");
+	ERROR("code: 0x%x\n", code);
+	dump_stack();
+	return;
+}
+
+wstdcall void WIN_FUNC(KeBugCheckEx,5)
+	(ULONG code, ULONG_PTR param1, ULONG_PTR param2,
+	 ULONG_PTR param3, ULONG_PTR param4)
+{
+	ERROR("Unrecoverable error reported by the driver");
+	ERROR("code: 0x%x, params: 0x%lx 0x%lx 0x%lx 0x%lx\n", code, param1,
+	      param2, param3, param4);
+	dump_stack();
+	return;
+}
+
+wstdcall void WIN_FUNC(ExSystemTimeToLocalTime,2)
+	(LARGE_INTEGER *system_time, LARGE_INTEGER *local_time)
+{
+	*local_time = *system_time;
+}
+
+wstdcall ULONG WIN_FUNC(ExSetTimerResolution,2)
+	(ULONG time, BOOLEAN set)
+{
+	/* why a driver should change system wide timer resolution is
+	 * beyond me */
+	return time;
+}
+
+wstdcall void WIN_FUNC(DbgBreakPoint,0)
+	(void)
+{
+	TODO();
+}
+
+wstdcall void WIN_FUNC(_except_handler3,0)
+	(void)
+{
+	TODO();
+}
+
+wstdcall void WIN_FUNC(__C_specific_handler,0)
+	(void)
+{
+	TODO();
+}
+
+wstdcall void WIN_FUNC(_purecall,0)
+	(void)
+{
+	TODO();
+}
+
+struct worker_init_struct {
+	struct work_struct work;
+	struct completion completion;
+	struct nt_thread *nt_thread;
+};
+
+int ntoskernel_init(void)
+{
+	spin_lock_init(&dispatcher_lock);
+	spin_lock_init(&ntoskernel_lock);
+	spin_lock_init(&ntos_work_lock);
+	spin_lock_init(&kdpc_list_lock);
+	spin_lock_init(&irp_cancel_lock);
+	InitializeListHead(&wrap_mdl_list);
+	InitializeListHead(&kdpc_list);
+	InitializeListHead(&callback_objects);
+	InitializeListHead(&bus_driver_list);
+	InitializeListHead(&object_list);
+	InitializeListHead(&ntos_work_list);
+
+	nt_spin_lock_init(&nt_list_lock);
+
+	INIT_WORK(&kdpc_work, kdpc_worker);
+	INIT_WORK(&ntos_work, ntos_work_worker);
+	wrap_timer_slist.next = NULL;
+
+    wrap_ticks_to_boot = TICKS_1601_TO_1970;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0)
+    do {
+        u64 now;
+        now = ktime_get_real_ns();
+        wrap_ticks_to_boot += now * 10;
+    } while (0);
+#else
+    do {
+        struct timeval now;
+
+        do_gettimeofday(&now);
+        wrap_ticks_to_boot += (u64)now.tv_sec * TICKSPERSEC;
+        wrap_ticks_to_boot += now.tv_usec * 10;
+    } while (0);
+#endif
+	TRACE2("%llu", wrap_ticks_to_boot);
+	wrap_ticks_to_boot -= jiffies * TICKSPERJIFFY;
+	TRACE2("%llu", wrap_ticks_to_boot);
+
+	cpu_count = num_online_cpus();
+
+#ifdef WRAP_PREEMPT
+	do {
+		int cpu;
+		for_each_possible_cpu(cpu) {
+			struct irql_info *info;
+			info = &per_cpu(irql_info, cpu);
+			mutex_init(&(info->lock));
+			info->task = NULL;
+			info->count = 0;
+#ifdef CONFIG_SMP
+			cpumask_setall(tsk_cpus_allowed(info));
+#endif
+		}
+	} while (0);
+#endif
+
+	ntos_wq = create_singlethread_workqueue("ntos_wq");
+	if (!ntos_wq) {
+		WARNING("couldn't create ntos_wq thread");
+		return -ENOMEM;
+	}
+	TRACE1("ntos_wq: %p", ntos_wq);
+
+	if (add_bus_driver("PCI")
+#ifdef ENABLE_USB
+	    || add_bus_driver("USB")
+#endif
+		) {
+		ntoskernel_exit();
+		return -ENOMEM;
+	}
+	mdl_cache =
+		wrap_kmem_cache_create(DRIVER_NAME "_mdl",
+				       sizeof(struct wrap_mdl) + MDL_CACHE_SIZE,
+				       0, 0);
+	TRACE2("%p", mdl_cache);
+	if (!mdl_cache) {
+		ERROR("couldn't allocate MDL cache");
+		ntoskernel_exit();
+		return -ENOMEM;
+	}
+
+#if defined(CONFIG_X86_64)
+	memset(&kuser_shared_data, 0, sizeof(kuser_shared_data));
+	*((ULONG64 *)&kuser_shared_data.system_time) = ticks_1601();
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,15,0)
+	init_timer(&shared_data_timer);
+	shared_data_timer.function = update_user_shared_data_proc;
+	shared_data_timer.data = 0;
+#else
+	timer_setup(&shared_data_timer, update_user_shared_data_proc, 0);
+#endif
+#endif
+	return 0;
+}
+
+int ntoskernel_init_device(struct wrap_device *wd)
+{
+#if defined(CONFIG_X86_64)
+	if (kuser_shared_data.reserved1)
+		mod_timer(&shared_data_timer, jiffies + MSEC_TO_HZ(30));
+#endif
+	return 0;
+}
+
+void ntoskernel_exit_device(struct wrap_device *wd)
+{
+	ENTER2("");
+
+	KeFlushQueuedDpcs();
+	EXIT2(return);
+}
+
+void ntoskernel_exit(void)
+{
+	struct nt_list *cur;
+
+	ENTER2("");
+
+	/* free kernel (Ke) timers */
+	TRACE2("freeing timers");
+	while (1) {
+		struct wrap_timer *wrap_timer;
+		struct nt_slist *slist;
+
+		spin_lock_bh(&ntoskernel_lock);
+		if ((slist = wrap_timer_slist.next))
+			wrap_timer_slist.next = slist->next;
+		spin_unlock_bh(&ntoskernel_lock);
+		TIMERTRACE("%p", slist);
+		if (!slist)
+			break;
+		wrap_timer = container_of(slist, struct wrap_timer, slist);
+		if (del_timer_sync(&wrap_timer->timer))
+			WARNING("Buggy Windows driver left timer %p running",
+				wrap_timer->nt_timer);
+		memset(wrap_timer, 0, sizeof(*wrap_timer));
+		slack_kfree(wrap_timer);
+	}
+
+	TRACE2("freeing MDLs");
+	if (mdl_cache) {
+		spin_lock_bh(&ntoskernel_lock);
+		if (!IsListEmpty(&wrap_mdl_list))
+			ERROR("Windows driver didn't free all MDLs; "
+			      "freeing them now");
+		while ((cur = RemoveHeadList(&wrap_mdl_list))) {
+			struct wrap_mdl *wrap_mdl;
+			wrap_mdl = container_of(cur, struct wrap_mdl, list);
+			if (wrap_mdl->mdl->flags & MDL_CACHE_ALLOCATED)
+				kmem_cache_free(mdl_cache, wrap_mdl);
+			else
+				kfree(wrap_mdl);
+		}
+		spin_unlock_bh(&ntoskernel_lock);
+		kmem_cache_destroy(mdl_cache);
+		mdl_cache = NULL;
+	}
+
+	TRACE2("freeing callbacks");
+	spin_lock_bh(&ntoskernel_lock);
+	while ((cur = RemoveHeadList(&callback_objects))) {
+		struct callback_object *object;
+		struct nt_list *ent;
+		object = container_of(cur, struct callback_object, list);
+		while ((ent = RemoveHeadList(&object->callback_funcs))) {
+			struct callback_func *f;
+			f = container_of(ent, struct callback_func, list);
+			kfree(f);
+		}
+		kfree(object);
+	}
+	spin_unlock_bh(&ntoskernel_lock);
+
+	spin_lock_bh(&ntoskernel_lock);
+	while ((cur = RemoveHeadList(&bus_driver_list))) {
+		struct bus_driver *bus_driver;
+		bus_driver = container_of(cur, struct bus_driver, list);
+		/* TODO: make sure all all drivers are shutdown/removed */
+		kfree(bus_driver);
+	}
+	spin_unlock_bh(&ntoskernel_lock);
+
+#if defined(CONFIG_X86_64)
+	del_timer_sync(&shared_data_timer);
+#endif
+	if (ntos_wq)
+		destroy_workqueue(ntos_wq);
+	ENTER2("freeing objects");
+	spin_lock_bh(&ntoskernel_lock);
+	while ((cur = RemoveHeadList(&object_list))) {
+		struct common_object_header *hdr;
+		hdr = container_of(cur, struct common_object_header, list);
+		if (hdr->type == OBJECT_TYPE_NT_THREAD)
+			TRACE1("object %p(%d) was not freed, freeing it now",
+			       HEADER_TO_OBJECT(hdr), hdr->type);
+		else
+			WARNING("object %p(%d) was not freed, freeing it now",
+				HEADER_TO_OBJECT(hdr), hdr->type);
+		ExFreePool(hdr);
+	}
+	spin_unlock_bh(&ntoskernel_lock);
+
+	EXIT2(return);
+}
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/ntoskernel.h linux-5.6.11-ndis/3rdparty/ndiswrapper/ntoskernel.h
--- linux-5.6.11/3rdparty/ndiswrapper/ntoskernel.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/ntoskernel.h	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,1117 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#ifndef _NTOSKERNEL_H_
+#define _NTOSKERNEL_H_
+
+#include <linux/types.h>
+#include <linux/timer.h>
+#include <linux/time.h>
+#include <linux/module.h>
+#include <linux/kmod.h>
+
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <linux/pci.h>
+#include <linux/wait.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/random.h>
+#include <linux/ctype.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/usb.h>
+#include <linux/spinlock.h>
+#include <asm/mman.h>
+#include <linux/version.h>
+#include <linux/etherdevice.h>
+#include <net/iw_handler.h>
+#include <linux/ethtool.h>
+#include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
+#include <linux/highmem.h>
+#include <linux/percpu.h>
+#include <linux/kthread.h>
+#include <linux/workqueue.h>
+#include <linux/vmalloc.h>
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(4,11,0)
+#include <linux/sched/signal.h>
+#endif
+
+#if !defined(CONFIG_X86) && !defined(CONFIG_X86_64)
+#error "this module is for x86 or x86_64 architectures only"
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
+#define gfp_t unsigned int __nocast
+
+static inline void *_kzalloc(size_t size, gfp_t flags)
+{
+	void *p = kmalloc(size, flags);
+	if (likely(p != NULL))
+		memset(p, 0, size);
+	return p;
+}
+
+#define kzalloc(size, flags) _kzalloc(size, flags)
+#endif
+
+/* Interrupt backwards compatibility stuff */
+#include <linux/interrupt.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
+#ifndef IRQ_HANDLED
+#define IRQ_HANDLED
+#define IRQ_NONE
+#define irqreturn_t void
+#endif
+#endif /* Linux < 2.6.29 */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
+#ifndef mutex_init
+#define mutex semaphore
+#define mutex_init(m) sema_init(m, 1)
+#define mutex_lock(m) down(m)
+#define mutex_trylock(m) (!down_trylock(m))
+#define mutex_unlock(m) up(m)
+#define mutex_is_locked(m) (atomic_read(m.count) == 0)
+#endif
+#endif /* Linux < 2.6.16 */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+#define set_cpus_allowed_ptr(task, mask) set_cpus_allowed(task, *mask)
+#endif /* Linux < 2.6.26 */
+
+#ifdef CONFIG_SMP
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+#define cpumask_copy(dst, src) do { *dst = *src; } while (0)
+#define cpumask_equal(mask1, mask2) cpus_equal(*mask1, *mask2)
+#define cpumask_setall(mask) cpus_setall(*mask)
+static cpumask_t cpumasks[NR_CPUS];
+#define cpumask_of(cpu) 			\
+({						\
+	cpumasks[cpu] = cpumask_of_cpu(cpu);	\
+	&cpumasks[cpu];				\
+})
+#endif /* Linux < 2.6.28 */
+#endif /* CONFIG_SMP */
+
+#ifndef tsk_cpus_allowed
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)
+#define tsk_cpus_allowed(tsk) (&(tsk)->cpus_mask)
+#else
+#define tsk_cpus_allowed(tsk) (&(tsk)->cpus_allowed)
+#endif
+#endif
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+/* pci functions in 2.6 kernels have problems allocating dma buffers,
+ * but seem to work fine with dma functions
+ */
+#include <asm/dma-mapping.h>
+
+#ifndef ___GFP_RETRY_MAYFAIL
+#define ___GFP_RETRY_MAYFAIL __GFP_REPEAT
+#endif
+
+#define PCI_DMA_ALLOC_COHERENT(pci_dev,size,dma_handle)			\
+	dma_alloc_coherent(&pci_dev->dev,size,dma_handle,		\
+			   GFP_KERNEL | ___GFP_RETRY_MAYFAIL)
+#define PCI_DMA_FREE_COHERENT(pci_dev,size,cpu_addr,dma_handle)		\
+	dma_free_coherent(&pci_dev->dev,size,cpu_addr,dma_handle)
+#define PCI_DMA_MAP_SINGLE(pci_dev,addr,size,direction)		\
+	dma_map_single(&pci_dev->dev,addr,size,direction)
+#define PCI_DMA_UNMAP_SINGLE(pci_dev,dma_handle,size,direction)		\
+	dma_unmap_single(&pci_dev->dev,dma_handle,size,direction)
+#define MAP_SG(pci_dev, sglist, nents, direction)		\
+	dma_map_sg(&pci_dev->dev, sglist, nents, direction)
+#define UNMAP_SG(pci_dev, sglist, nents, direction)		\
+	dma_unmap_sg(&pci_dev->dev, sglist, nents, direction)
+#define PCI_DMA_MAP_ERROR(dma_addr) dma_mapping_error(dma_addr)
+
+
+#if defined(CONFIG_NET_RADIO) && !defined(CONFIG_WIRELESS_EXT)
+#define CONFIG_WIRELESS_EXT
+#endif
+
+#define prepare_wait_condition(task, var, value)	\
+do {							\
+	var = value;					\
+	task = current;					\
+	barrier();					\
+} while (0)
+
+/* Wait in wait_state (e.g., TASK_INTERRUPTIBLE) for condition to
+ * become true; timeout is either jiffies (> 0) to wait or 0 to wait
+ * forever.
+ * When timeout == 0, return value is
+ *    > 0 if condition becomes true, or
+ *    < 0 if signal is pending on the thread.
+ * When timeout > 0, return value is
+ *    > 0 if condition becomes true before timeout,
+ *    < 0 if signal is pending on the thread before timeout, or
+ *    0 if timedout (condition may have become true at the same time)
+ */
+
+#define wait_condition(condition, timeout, wait_state)		\
+({								\
+	long ret = timeout ? timeout : 1;			\
+	while (1) {						\
+		if (signal_pending(current)) {			\
+			ret = -ERESTARTSYS;			\
+			break;					\
+		}						\
+		set_current_state(wait_state);			\
+		if (condition) {				\
+			__set_current_state(TASK_RUNNING);	\
+			break;					\
+		}						\
+		if (timeout) {					\
+			ret = schedule_timeout(ret);		\
+			if (!ret)				\
+				break;				\
+		} else						\
+			schedule();				\
+	}							\
+	ret;							\
+})
+
+#ifdef WRAP_WQ
+
+struct wrap_workqueue_struct;
+
+struct wrap_work_struct {
+	struct list_head list;
+	void (*func)(struct wrap_work_struct *data);
+	void *data;
+	/* whether/on which thread scheduled */
+	struct workqueue_thread *thread;
+};
+
+#define work_struct wrap_work_struct
+#define workqueue_struct wrap_workqueue_struct
+
+#undef INIT_WORK
+#define INIT_WORK(work, pfunc)					\
+	do {							\
+		(work)->func = (pfunc);				\
+		(work)->data = (work);				\
+		(work)->thread = NULL;				\
+	} while (0)
+
+#undef create_singlethread_workqueue
+#define create_singlethread_workqueue(wq) wrap_create_wq(wq, 1, 0)
+#undef create_workqueue
+#define create_workqueue(wq) wrap_create_wq(wq, 0, 0)
+#undef destroy_workqueue
+#define destroy_workqueue(wq) wrap_destroy_wq(wq)
+#undef queue_work
+#define queue_work(wq, work) wrap_queue_work(wq, work)
+#undef flush_workqueue
+#define flush_workqueue(wq) wrap_flush_wq(wq)
+
+struct workqueue_struct *wrap_create_wq(const char *name, u8 singlethread,
+					u8 freeze);
+void wrap_destroy_wq(struct workqueue_struct *workq);
+int wrap_queue_work(struct workqueue_struct *workq, struct work_struct *work);
+void wrap_cancel_work(struct work_struct *work);
+void wrap_flush_wq(struct workqueue_struct *workq);
+
+#else // WRAP_WQ
+
+/* Compatibility for Linux before 2.6.20 where INIT_WORK takes 3 arguments */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) && \
+    !defined(INIT_WORK_NAR) && \
+    !defined(INIT_DELAYED_WORK_DEFERRABLE)
+typedef void (*compat_work_func_t)(void *work);
+typedef void (*work_func_t)(struct work_struct *work);
+static inline void (INIT_WORK)(struct work_struct *work, work_func_t func)
+{
+	INIT_WORK(work, (compat_work_func_t)func, work);
+}
+#undef INIT_WORK
+#endif
+
+#endif // WRAP_WQ
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
+#define ISR_PT_REGS_PARAM_DECL
+#else
+#define ISR_PT_REGS_PARAM_DECL , struct pt_regs *regs
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)
+#define for_each_possible_cpu(_cpu) for_each_cpu(_cpu)
+#endif
+
+#ifndef CHECKSUM_PARTIAL
+#define CHECKSUM_PARTIAL CHECKSUM_HW
+#endif
+
+#ifndef IRQF_SHARED
+#define IRQF_SHARED SA_SHIRQ
+#endif
+
+#ifndef UMH_WAIT_PROC
+#define UMH_WAIT_PROC 1
+#endif
+
+#define memcpy_skb(skb, from, length)			\
+	memcpy(skb_put(skb, length), from, length)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+#ifndef DMA_BIT_MASK
+#define DMA_BIT_MASK(n)	(((n) == 64) ? ~0ULL : ((1ULL<<(n))-1))
+#endif
+#endif
+
+#ifndef __GFP_DMA32
+#define __GFP_DMA32 GFP_DMA
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)
+#define wrap_kmem_cache_create(name, size, align, flags)	\
+	kmem_cache_create(name, size, align, flags, NULL, NULL)
+#else
+#define wrap_kmem_cache_create(name, size, align, flags)	\
+	kmem_cache_create(name, size, align, flags, NULL)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)
+#define netdev_mc_count(dev) ((dev)->mc_count)
+#define usb_alloc_coherent(dev, size, mem_flags, dma) (usb_buffer_alloc((dev), (size), (mem_flags), (dma)))
+#define usb_free_coherent(dev, size, addr, dma) (usb_buffer_free((dev), (size), (addr), (dma)))
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
+#define daemonize(name, ...) do {} while (0)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
+#define add_taint(flag, lockdep_ok) add_taint(flag)
+#endif
+
+#include "winnt_types.h"
+#include "ndiswrapper.h"
+#include "pe_linker.h"
+#include "wrapmem.h"
+#include "lin2win.h"
+#include "loader.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+static inline void netif_tx_lock(struct net_device *dev)
+{
+	spin_lock(&dev->xmit_lock);
+}
+static inline void netif_tx_unlock(struct net_device *dev)
+{
+	spin_unlock(&dev->xmit_lock);
+}
+static inline void netif_tx_lock_bh(struct net_device *dev)
+{
+	spin_lock_bh(&dev->xmit_lock);
+}
+static inline void netif_tx_unlock_bh(struct net_device *dev)
+{
+	spin_unlock_bh(&dev->xmit_lock);
+}
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+static inline void netif_poll_enable(struct net_device *dev)
+{
+}
+static inline void netif_poll_disable(struct net_device *dev)
+{
+}
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+#define proc_net_root init_net.proc_net
+#else
+#define proc_net_root proc_net
+#endif
+
+#if ((LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)) && \
+     (LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0))) || \
+    (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,42))
+#ifndef skb_frag_page
+#define skb_frag_page(frag) ((frag)->page)
+#endif
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
+#define netdev_notifier_info_to_dev(x) ((struct net_device *)(x))
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)
+static inline void reinit_completion(struct completion *x)
+{
+	INIT_COMPLETION(*x);
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
+#define prandom_seed(seed) net_srandom(seed)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+static int strncasecmp(const char *s1, const char *s2, size_t n)
+{
+	strnicmp(s1, s2, n);
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+static inline void netif_trans_update(struct net_device *dev)
+{
+	dev->trans_start = jiffies;
+}
+#endif
+
+/* TICK is 100ns */
+#define TICKSPERSEC		10000000
+#define TICKSPERMSEC		10000
+#define SECSPERDAY		86400
+#define TICKSPERJIFFY		((TICKSPERSEC + HZ - 1) / HZ)
+
+#define int_div_round(x, y) (((x) + (y - 1)) / (y))
+
+/* 1601 to 1970 is 369 years plus 89 leap days */
+#define SECS_1601_TO_1970	((369 * 365 + 89) * (u64)SECSPERDAY)
+#define TICKS_1601_TO_1970	(SECS_1601_TO_1970 * TICKSPERSEC)
+
+/* 100ns units to HZ; if sys_time is negative, relative to current
+ * clock, otherwise from year 1601 */
+#define SYSTEM_TIME_TO_HZ(sys_time)					\
+	(((sys_time) <= 0) ? \
+	 int_div_round(((u64)HZ * (-(sys_time))), TICKSPERSEC) :	\
+	 int_div_round(((s64)HZ * ((sys_time) - ticks_1601())), TICKSPERSEC))
+
+#define MSEC_TO_HZ(ms) int_div_round((ms * HZ), 1000)
+#define USEC_TO_HZ(us) int_div_round((us * HZ), 1000000)
+
+extern u64 wrap_ticks_to_boot;
+
+static inline u64 ticks_1601(void)
+{
+	return wrap_ticks_to_boot + (u64)jiffies * TICKSPERJIFFY;
+}
+
+typedef void (*generic_func)(void);
+
+struct wrap_export {
+	const char *name;
+	generic_func func;
+};
+
+#ifdef CONFIG_X86_64
+
+#define WIN_SYMBOL(name, argc)					\
+	{#name, (generic_func) win2lin_ ## name ## _ ## argc}
+#define WIN_WIN_SYMBOL(name, argc)					\
+	{#name, (generic_func) win2lin__win_ ## name ## _ ## argc}
+#define WIN_FUNC_DECL(name, argc)			\
+	extern typeof(name) win2lin_ ## name ## _ ## argc;
+#define WIN_FUNC_PTR(name, argc) win2lin_ ## name ## _ ## argc
+
+#else
+
+#define WIN_SYMBOL(name, argc) {#name, (generic_func)name}
+#define WIN_WIN_SYMBOL(name, argc) {#name, (generic_func)_win_ ## name}
+#define WIN_FUNC_DECL(name, argc)
+#define WIN_FUNC_PTR(name, argc) name
+
+#endif
+
+#define WIN_FUNC(name, argc) (name)
+/* map name s to f - if f is different from s */
+#define WIN_SYMBOL_MAP(s, f)
+
+#define POOL_TAG(A, B, C, D)					\
+	((ULONG)((A) + ((B) << 8) + ((C) << 16) + ((D) << 24)))
+
+struct pe_image {
+	char name[MAX_DRIVER_NAME_LEN];
+	UINT (*entry)(struct driver_object *, struct unicode_string *) wstdcall;
+	void *image;
+	int size;
+	int type;
+
+	IMAGE_NT_HEADERS *nt_hdr;
+	IMAGE_OPTIONAL_HEADER *opt_hdr;
+};
+
+struct ndis_mp_block;
+
+struct wrap_timer {
+	struct nt_slist slist;
+	struct timer_list timer;
+	struct nt_timer *nt_timer;
+	long repeat;
+#ifdef TIMER_DEBUG
+	unsigned long wrap_timer_magic;
+#endif
+};
+
+struct ntos_work_item {
+	struct nt_list list;
+	void *arg1;
+	void *arg2;
+	NTOS_WORK_FUNC func;
+};
+
+struct wrap_device_setting {
+	struct nt_list list;
+	char name[MAX_SETTING_NAME_LEN];
+	char value[MAX_SETTING_VALUE_LEN];
+	void *encoded;
+};
+
+struct wrap_bin_file {
+	char name[MAX_DRIVER_NAME_LEN];
+	size_t size;
+	void *data;
+};
+
+#define WRAP_DRIVER_CLIENT_ID 1
+
+struct wrap_driver {
+	struct nt_list list;
+	struct driver_object *drv_obj;
+	char name[MAX_DRIVER_NAME_LEN];
+	char version[MAX_SETTING_VALUE_LEN];
+	unsigned short num_pe_images;
+	struct pe_image pe_images[MAX_DRIVER_PE_IMAGES];
+	unsigned short num_bin_files;
+	struct wrap_bin_file *bin_files;
+	struct nt_list settings;
+	int dev_type;
+	struct ndis_driver *ndis_driver;
+};
+
+enum hw_status {
+	HW_INITIALIZED = 1, HW_SUSPENDED, HW_HALTED, HW_DISABLED,
+};
+
+struct wrap_device {
+	/* first part is (de)initialized once by loader */
+	struct nt_list list;
+	int dev_bus;
+	int vendor;
+	int device;
+	int subvendor;
+	int subdevice;
+	char conf_file_name[MAX_DRIVER_NAME_LEN];
+	char driver_name[MAX_DRIVER_NAME_LEN];
+	struct wrap_driver *driver;
+	struct nt_list settings;
+
+	/* rest should be (de)initialized when a device is
+	 * (un)plugged */
+	struct cm_resource_list *resource_list;
+	unsigned long hw_status;
+	struct device_object *pdo;
+	union {
+		struct {
+			struct pci_dev *pdev;
+			enum device_power_state wake_state;
+		} pci;
+		struct {
+			struct usb_device *udev;
+			struct usb_interface *intf;
+			int num_alloc_urbs;
+			struct nt_list wrap_urb_list;
+		} usb;
+	};
+};
+
+#define wrap_is_pci_bus(dev_bus)			\
+	(WRAP_BUS(dev_bus) == WRAP_PCI_BUS ||		\
+	 WRAP_BUS(dev_bus) == WRAP_PCMCIA_BUS)
+#ifdef ENABLE_USB
+/* earlier versions of ndiswrapper used 0 as USB_BUS */
+#define wrap_is_usb_bus(dev_bus)			\
+	(WRAP_BUS(dev_bus) == WRAP_USB_BUS ||		\
+	 WRAP_BUS(dev_bus) == WRAP_INTERNAL_BUS)
+#else
+#define wrap_is_usb_bus(dev_bus) 0
+#endif
+#define wrap_is_bluetooth_device(dev_bus)			\
+	(WRAP_DEVICE(dev_bus) == WRAP_BLUETOOTH_DEVICE1 ||	\
+	 WRAP_DEVICE(dev_bus) == WRAP_BLUETOOTH_DEVICE2)
+
+extern struct workqueue_struct *ntos_wq;
+extern struct workqueue_struct *ndis_wq;
+extern struct workqueue_struct *wrapndis_wq;
+
+#define atomic_unary_op(var, size, oper)				\
+do {									\
+	if (size == 1)							\
+		__asm__ __volatile__(					\
+			LOCK_PREFIX oper "b %b0\n\t" : "+m" (var));	\
+	else if (size == 2)						\
+		__asm__ __volatile__(					\
+			LOCK_PREFIX oper "w %w0\n\t" : "+m" (var));	\
+	else if (size == 4)						\
+		__asm__ __volatile__(					\
+			LOCK_PREFIX oper "l %0\n\t" : "+m" (var));	\
+	else if (size == 8)						\
+		__asm__ __volatile__(					\
+			LOCK_PREFIX oper "q %q0\n\t" : "+m" (var));	\
+	else {								\
+		extern void _invalid_op_size_(void);			\
+		_invalid_op_size_();					\
+	}								\
+} while (0)
+
+#define atomic_inc_var_size(var, size) atomic_unary_op(var, size, "inc")
+
+#define atomic_inc_var(var) atomic_inc_var_size(var, sizeof(var))
+
+#define atomic_dec_var_size(var, size) atomic_unary_op(var, size, "dec")
+
+#define atomic_dec_var(var) atomic_dec_var_size(var, sizeof(var))
+
+#define pre_atomic_add(var, i)					\
+({								\
+	typeof(var) pre;					\
+	__asm__ __volatile__(					\
+		LOCK_PREFIX "xadd %0, %1\n\t"			\
+		: "=r"(pre), "+m"(var)				\
+		: "0"(i));					\
+	pre;							\
+})
+
+#define post_atomic_add(var, i) (pre_atomic_add(var, i) + i)
+
+//#define DEBUG_IRQL 1
+
+#ifdef DEBUG_IRQL
+#define assert_irql(cond)						\
+do {									\
+	KIRQL _irql_ = current_irql();					\
+	if (!(cond)) {							\
+		WARNING("assertion '%s' failed: %d", #cond, _irql_);	\
+		DBG_BLOCK(4) {						\
+			dump_stack();					\
+		}							\
+	}								\
+} while (0)
+#else
+#define assert_irql(cond) do { } while (0)
+#endif
+
+/* When preempt is enabled, we should preempt_disable to raise IRQL to
+ * DISPATCH_LEVEL, to be consistent with the semantics. However, using
+ * a mutex instead, so that only ndiswrapper threads run one at a time
+ * on a processor when at DISPATCH_LEVEL seems to be enough. So that
+ * is what we will use until we learn otherwise. If
+ * preempt_(en|dis)able is required for some reason, comment out
+ * following #define. */
+
+#define WRAP_PREEMPT 1
+
+#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PREEMPT_RT)
+#ifndef WRAP_PREEMPT
+#define WRAP_PREEMPT 1
+#endif
+#endif
+
+//#undef WRAP_PREEMPT
+
+#ifdef WRAP_PREEMPT
+
+struct irql_info {
+	int count;
+	struct mutex lock;
+#ifdef CONFIG_SMP
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0)
+	cpumask_t cpus_mask;
+#else
+	cpumask_t cpus_allowed;
+#endif
+#endif
+	struct task_struct *task;
+};
+
+DECLARE_PER_CPU(struct irql_info, irql_info);
+
+static inline KIRQL raise_irql(KIRQL newirql)
+{
+	struct irql_info *info;
+
+	assert(newirql == DISPATCH_LEVEL);
+	info = &get_cpu_var(irql_info);
+	if (info->task == current) {
+		assert(info->count > 0);
+		assert(mutex_is_locked(&info->lock));
+#if defined(CONFIG_SMP) && DEBUG >= 1
+		assert(cpumask_equal(tsk_cpus_allowed(current),
+				     cpumask_of(smp_processor_id())));
+#endif
+		info->count++;
+		put_cpu_var(irql_info);
+		return DISPATCH_LEVEL;
+	}
+	/* TODO: is this enough to pin down to current cpu? */
+#ifdef CONFIG_SMP
+	assert(task_cpu(current) == smp_processor_id());
+	cpumask_copy(tsk_cpus_allowed(info), tsk_cpus_allowed(current));
+	set_cpus_allowed_ptr(current, cpumask_of(smp_processor_id()));
+#endif
+	put_cpu_var(irql_info);
+	mutex_lock(&info->lock);
+	assert(info->count == 0);
+	assert(info->task == NULL);
+	info->count = 1;
+	info->task = current;
+	return PASSIVE_LEVEL;
+}
+
+static inline void lower_irql(KIRQL oldirql)
+{
+	struct irql_info *info;
+
+	assert(oldirql <= DISPATCH_LEVEL);
+	info = &get_cpu_var(irql_info);
+	assert(info->task == current);
+	assert(mutex_is_locked(&info->lock));
+	assert(info->count > 0);
+	if (--info->count == 0) {
+		info->task = NULL;
+#ifdef CONFIG_SMP
+		set_cpus_allowed_ptr(current, tsk_cpus_allowed(info));
+#endif
+		mutex_unlock(&info->lock);
+	}
+	put_cpu_var(irql_info);
+}
+
+static inline KIRQL current_irql(void)
+{
+	int count;
+	if (in_irq() || irqs_disabled())
+		EXIT4(return DIRQL);
+	if (in_atomic() || in_interrupt())
+		EXIT4(return SOFT_IRQL);
+	count = get_cpu_var(irql_info).count;
+	put_cpu_var(irql_info);
+	if (count)
+		EXIT6(return DISPATCH_LEVEL);
+	else
+		EXIT6(return PASSIVE_LEVEL);
+}
+
+#else
+
+static inline KIRQL current_irql(void)
+{
+	if (in_irq() || irqs_disabled())
+		EXIT4(return DIRQL);
+	if (in_interrupt())
+		EXIT4(return SOFT_IRQL);
+	if (in_atomic())
+		EXIT6(return DISPATCH_LEVEL);
+	else
+		EXIT6(return PASSIVE_LEVEL);
+}
+
+static inline KIRQL raise_irql(KIRQL newirql)
+{
+	KIRQL ret = in_atomic() ? DISPATCH_LEVEL : PASSIVE_LEVEL;
+	assert(newirql == DISPATCH_LEVEL);
+	assert(current_irql() <= DISPATCH_LEVEL);
+	preempt_disable();
+	return ret;
+}
+
+static inline void lower_irql(KIRQL oldirql)
+{
+	assert(current_irql() == DISPATCH_LEVEL);
+	preempt_enable();
+}
+
+#endif
+
+#define irql_gfp() (in_atomic() ? GFP_ATOMIC : GFP_KERNEL)
+
+/* Windows spinlocks are of type ULONG_PTR which is not big enough to
+ * store Linux spinlocks; so we implement Windows spinlocks using
+ * ULONG_PTR space with our own functions/macros */
+
+/* Windows seems to use 0 for unlocked state of spinlock - if Linux
+ * convention of 1 for unlocked state is used, at least prism54 driver
+ * crashes */
+
+#define NT_SPIN_LOCK_UNLOCKED 0
+#define NT_SPIN_LOCK_LOCKED 1
+
+static inline void nt_spin_lock_init(NT_SPIN_LOCK *lock)
+{
+	*lock = NT_SPIN_LOCK_UNLOCKED;
+}
+
+#ifdef CONFIG_SMP
+
+static inline void nt_spin_lock(NT_SPIN_LOCK *lock)
+{
+	while (1) {
+		unsigned long lockval = xchg(lock, NT_SPIN_LOCK_LOCKED);
+
+		if (likely(lockval == NT_SPIN_LOCK_UNLOCKED))
+			break;
+		if (unlikely(lockval > NT_SPIN_LOCK_LOCKED)) {
+			ERROR("bad spinlock: 0x%lx at %p", lockval, lock);
+			return;
+		}
+		/* "rep; nop" doesn't change cx register, it's a "pause" */
+		__asm__ __volatile__("rep; nop");
+	}
+}
+
+static inline void nt_spin_unlock(NT_SPIN_LOCK *lock)
+{
+	unsigned long lockval = xchg(lock, NT_SPIN_LOCK_UNLOCKED);
+
+	if (likely(lockval == NT_SPIN_LOCK_LOCKED))
+		return;
+	WARNING("unlocking unlocked spinlock: 0x%lx at %p", lockval, lock);
+}
+
+#else // CONFIG_SMP
+
+#define nt_spin_lock(lock) do { } while (0)
+
+#define nt_spin_unlock(lock) do { } while (0)
+
+#endif // CONFIG_SMP
+
+/* When kernel would've disabled preempt (e.g., in interrupt
+ * handlers), we need to fake preempt so driver thinks it is running
+ * at right IRQL */
+
+/* raise IRQL to given (higher) IRQL if necessary before locking */
+static inline KIRQL nt_spin_lock_irql(NT_SPIN_LOCK *lock, KIRQL newirql)
+{
+	KIRQL oldirql = raise_irql(newirql);
+	nt_spin_lock(lock);
+	return oldirql;
+}
+
+/* lower IRQL to given (lower) IRQL if necessary after unlocking */
+static inline void nt_spin_unlock_irql(NT_SPIN_LOCK *lock, KIRQL oldirql)
+{
+	nt_spin_unlock(lock);
+	lower_irql(oldirql);
+}
+
+#define nt_spin_lock_irqsave(lock, flags)				\
+do {									\
+	local_irq_save(flags);						\
+	preempt_disable();						\
+	nt_spin_lock(lock);						\
+} while (0)
+
+#define nt_spin_unlock_irqrestore(lock, flags)				\
+do {									\
+	nt_spin_unlock(lock);						\
+	preempt_enable();						\
+	local_irq_restore(flags);					\
+} while (0)
+
+static inline ULONG SPAN_PAGES(void *ptr, SIZE_T length)
+{
+	return PAGE_ALIGN(((unsigned long)ptr & (PAGE_SIZE - 1)) + length)
+		>> PAGE_SHIFT;
+}
+
+#ifdef CONFIG_X86_64
+
+/* TODO: can these be implemented without using spinlock? */
+
+static inline struct nt_slist *PushEntrySList(nt_slist_header *head,
+					      struct nt_slist *entry,
+					      NT_SPIN_LOCK *lock)
+{
+	KIRQL irql = nt_spin_lock_irql(lock, DISPATCH_LEVEL);
+	entry->next = head->next;
+	head->next = entry;
+	head->depth++;
+	nt_spin_unlock_irql(lock, irql);
+	TRACE4("%p, %p, %p", head, entry, entry->next);
+	return entry->next;
+}
+
+static inline struct nt_slist *PopEntrySList(nt_slist_header *head,
+					     NT_SPIN_LOCK *lock)
+{
+	struct nt_slist *entry;
+	KIRQL irql = nt_spin_lock_irql(lock, DISPATCH_LEVEL);
+	entry = head->next;
+	if (entry) {
+		head->next = entry->next;
+		head->depth--;
+	}
+	nt_spin_unlock_irql(lock, irql);
+	TRACE4("%p, %p", head, entry);
+	return entry;
+}
+
+#else
+
+#define u64_low_32(x) ((u32)x)
+#define u64_high_32(x) ((u32)(x >> 32))
+
+static inline u64 nt_cmpxchg8b(volatile u64 *ptr, u64 old, u64 new)
+{
+	u64 prev;
+
+	__asm__ __volatile__(
+		"\n"
+		LOCK_PREFIX "cmpxchg8b %0\n"
+		: "+m" (*ptr), "=A" (prev)
+		: "A" (old), "b" (u64_low_32(new)), "c" (u64_high_32(new)));
+	return prev;
+}
+
+/* slist routines below update slist atomically - no need for
+ * spinlocks */
+
+static inline struct nt_slist *PushEntrySList(nt_slist_header *head,
+					      struct nt_slist *entry,
+					      NT_SPIN_LOCK *lock)
+{
+	nt_slist_header old, new;
+	do {
+		old.align = head->align;
+		entry->next = old.next;
+		new.next = entry;
+		new.depth = old.depth + 1;
+	} while (nt_cmpxchg8b(&head->align, old.align, new.align) != old.align);
+	TRACE4("%p, %p, %p", head, entry, old.next);
+	return old.next;
+}
+
+static inline struct nt_slist *PopEntrySList(nt_slist_header *head,
+					     NT_SPIN_LOCK *lock)
+{
+	struct nt_slist *entry;
+	nt_slist_header old, new;
+	do {
+		old.align = head->align;
+		entry = old.next;
+		if (!entry)
+			break;
+		new.next = entry->next;
+		new.depth = old.depth - 1;
+	} while (nt_cmpxchg8b(&head->align, old.align, new.align) != old.align);
+	TRACE4("%p, %p", head, entry);
+	return entry;
+}
+
+#endif
+
+#define sleep_hz(n)					\
+do {							\
+	set_current_state(TASK_INTERRUPTIBLE);		\
+	schedule_timeout(n);				\
+} while (0)
+
+int ntoskernel_init(void);
+void ntoskernel_exit(void);
+int ntoskernel_init_device(struct wrap_device *wd);
+void ntoskernel_exit_device(struct wrap_device *wd);
+void *allocate_object(ULONG size, enum common_object_type type,
+		      struct unicode_string *name);
+
+#ifdef ENABLE_USB
+int usb_init(void);
+void usb_exit(void);
+#else
+static inline int usb_init(void) { return 0; }
+static inline void usb_exit(void) {}
+#endif
+int usb_init_device(struct wrap_device *wd);
+void usb_exit_device(struct wrap_device *wd);
+
+int wrap_procfs_init(void);
+void wrap_procfs_remove(void);
+
+int link_pe_images(struct pe_image *pe_image, unsigned short n);
+
+int stricmp(const char *s1, const char *s2);
+void dump_bytes(const char *name, const u8 *from, int len);
+struct mdl *allocate_init_mdl(void *virt, ULONG length);
+void free_mdl(struct mdl *mdl);
+struct driver_object *find_bus_driver(const char *name);
+void free_custom_extensions(struct driver_extension *drv_obj_ext);
+struct nt_thread *get_current_nt_thread(void);
+u64 ticks_1601(void);
+int schedule_ntos_work_item(NTOS_WORK_FUNC func, void *arg1, void *arg2);
+void wrap_init_timer(struct nt_timer *nt_timer, enum timer_type type,
+		     struct ndis_mp_block *nmb);
+BOOLEAN wrap_set_timer(struct nt_timer *nt_timer, unsigned long expires_hz,
+		       unsigned long repeat_hz, struct kdpc *kdpc);
+
+LONG InterlockedDecrement(LONG volatile *val) wfastcall;
+LONG InterlockedIncrement(LONG volatile *val) wfastcall;
+struct nt_list *ExInterlockedInsertHeadList
+	(struct nt_list *head, struct nt_list *entry,
+	 NT_SPIN_LOCK *lock) wfastcall;
+struct nt_list *ExInterlockedInsertTailList
+	(struct nt_list *head, struct nt_list *entry,
+	 NT_SPIN_LOCK *lock) wfastcall;
+struct nt_list *ExInterlockedRemoveHeadList
+	(struct nt_list *head, NT_SPIN_LOCK *lock) wfastcall;
+NTSTATUS IofCallDriver(struct device_object *dev_obj, struct irp *irp) wfastcall;
+KIRQL KfRaiseIrql(KIRQL newirql) wfastcall;
+void KfLowerIrql(KIRQL oldirql) wfastcall;
+KIRQL KfAcquireSpinLock(NT_SPIN_LOCK *lock) wfastcall;
+void KfReleaseSpinLock(NT_SPIN_LOCK *lock, KIRQL oldirql) wfastcall;
+void IofCompleteRequest(struct irp *irp, CHAR prio_boost) wfastcall;
+void KefReleaseSpinLockFromDpcLevel(NT_SPIN_LOCK *lock) wfastcall;
+
+LONG ObfReferenceObject(void *object) wfastcall;
+void ObfDereferenceObject(void *object) wfastcall;
+
+#define ObReferenceObject(object) ObfReferenceObject(object)
+#define ObDereferenceObject(object) ObfDereferenceObject(object)
+
+/* prevent expansion of ExAllocatePoolWithTag macro */
+void *(ExAllocatePoolWithTag)(enum pool_type pool_type, SIZE_T size,
+			      ULONG tag) wstdcall;
+
+void ExFreePool(void *p) wstdcall;
+ULONG MmSizeOfMdl(void *base, ULONG length) wstdcall;
+void __iomem *MmMapIoSpace(PHYSICAL_ADDRESS phys_addr, SIZE_T size,
+		   enum memory_caching_type cache) wstdcall;
+void MmUnmapIoSpace(void __iomem *addr, SIZE_T size) wstdcall;
+void MmProbeAndLockPages(struct mdl *mdl, KPROCESSOR_MODE access_mode,
+			 enum lock_operation operation) wstdcall;
+void MmUnlockPages(struct mdl *mdl) wstdcall;
+void KeInitializeEvent(struct nt_event *nt_event,
+		       enum event_type type, BOOLEAN state) wstdcall;
+LONG KeSetEvent(struct nt_event *nt_event, KPRIORITY incr,
+		BOOLEAN wait) wstdcall;
+LONG KeResetEvent(struct nt_event *nt_event) wstdcall;
+BOOLEAN queue_kdpc(struct kdpc *kdpc);
+BOOLEAN dequeue_kdpc(struct kdpc *kdpc);
+
+NTSTATUS IoConnectInterrupt(struct kinterrupt **kinterrupt,
+			    PKSERVICE_ROUTINE service_routine,
+			    void *service_context, NT_SPIN_LOCK *lock,
+			    ULONG vector, KIRQL irql, KIRQL synch_irql,
+			    enum kinterrupt_mode interrupt_mode,
+			    BOOLEAN shareable, KAFFINITY processor_enable_mask,
+			    BOOLEAN floating_save) wstdcall;
+void IoDisconnectInterrupt(struct kinterrupt *interrupt) wstdcall;
+BOOLEAN KeSynchronizeExecution(struct kinterrupt *interrupt,
+			       PKSYNCHRONIZE_ROUTINE synch_routine,
+			       void *ctx) wstdcall;
+
+NTSTATUS KeWaitForSingleObject(void *object, KWAIT_REASON reason,
+			       KPROCESSOR_MODE waitmode, BOOLEAN alertable,
+			       LARGE_INTEGER *timeout) wstdcall;
+void MmBuildMdlForNonPagedPool(struct mdl *mdl) wstdcall;
+NTSTATUS IoCreateDevice(struct driver_object *driver, ULONG dev_ext_length,
+			struct unicode_string *dev_name, DEVICE_TYPE dev_type,
+			ULONG dev_chars, BOOLEAN exclusive,
+			struct device_object **dev_obj) wstdcall;
+NTSTATUS IoCreateSymbolicLink(struct unicode_string *link,
+			      struct unicode_string *dev_name) wstdcall;
+void IoDeleteDevice(struct device_object *dev) wstdcall;
+void IoDetachDevice(struct device_object *topdev) wstdcall;
+struct device_object *IoGetAttachedDevice(struct device_object *dev) wstdcall;
+struct device_object *IoGetAttachedDeviceReference
+	(struct device_object *dev) wstdcall;
+NTSTATUS IoAllocateDriverObjectExtension
+	(struct driver_object *drv_obj, void *client_id, ULONG extlen,
+	 void **ext) wstdcall;
+void *IoGetDriverObjectExtension(struct driver_object *drv,
+				 void *client_id) wstdcall;
+struct device_object *IoAttachDeviceToDeviceStack
+	(struct device_object *src, struct device_object *dst) wstdcall;
+BOOLEAN IoCancelIrp(struct irp *irp) wstdcall;
+struct irp *IoBuildSynchronousFsdRequest
+	(ULONG major_func, struct device_object *dev_obj, void *buf,
+	 ULONG length, LARGE_INTEGER *offset, struct nt_event *event,
+	 struct io_status_block *status) wstdcall;
+
+NTSTATUS IoPassIrpDown(struct device_object *dev_obj, struct irp *irp) wstdcall;
+WIN_FUNC_DECL(IoPassIrpDown,2);
+NTSTATUS IoSyncForwardIrp(struct device_object *dev_obj,
+			  struct irp *irp) wstdcall;
+NTSTATUS IoAsyncForwardIrp(struct device_object *dev_obj,
+			   struct irp *irp) wstdcall;
+NTSTATUS IoInvalidDeviceRequest(struct device_object *dev_obj,
+				struct irp *irp) wstdcall;
+
+void KeInitializeSpinLock(NT_SPIN_LOCK *lock) wstdcall;
+void IoAcquireCancelSpinLock(KIRQL *irql) wstdcall;
+void IoReleaseCancelSpinLock(KIRQL irql) wstdcall;
+
+NTSTATUS RtlUnicodeStringToAnsiString
+	(struct ansi_string *dst, const struct unicode_string *src,
+	 BOOLEAN dup) wstdcall;
+NTSTATUS RtlAnsiStringToUnicodeString
+	(struct unicode_string *dst, const struct ansi_string *src,
+	 BOOLEAN dup) wstdcall;
+void RtlInitAnsiString(struct ansi_string *dst, const char *src) wstdcall;
+void RtlInitUnicodeString(struct unicode_string *dest,
+			  const wchar_t *src) wstdcall;
+void RtlFreeUnicodeString(struct unicode_string *string) wstdcall;
+void RtlFreeAnsiString(struct ansi_string *string) wstdcall;
+LONG RtlCompareUnicodeString(const struct unicode_string *s1,
+			     const struct unicode_string *s2,
+			     BOOLEAN case_insensitive) wstdcall;
+NTSTATUS RtlUpcaseUnicodeString(struct unicode_string *dst,
+				struct unicode_string *src,
+				BOOLEAN alloc) wstdcall;
+BOOLEAN KeCancelTimer(struct nt_timer *nt_timer) wstdcall;
+void KeInitializeDpc(struct kdpc *kdpc, void *func, void *ctx) wstdcall;
+
+extern spinlock_t ntoskernel_lock;
+extern spinlock_t irp_cancel_lock;
+extern struct nt_list object_list;
+extern CCHAR cpu_count;
+#ifdef CONFIG_X86_64
+extern struct kuser_shared_data kuser_shared_data;
+#endif
+
+#define IoCompleteRequest(irp, prio) IofCompleteRequest(irp, prio)
+#define IoCallDriver(dev, irp) IofCallDriver(dev, irp)
+
+#if defined(IO_DEBUG)
+#define DUMP_IRP(_irp)							\
+do {									\
+	struct io_stack_location *_irp_sl;				\
+	_irp_sl = IoGetCurrentIrpStackLocation(_irp);			\
+	IOTRACE("irp: %p, stack size: %d, cl: %d, sl: %p, dev_obj: %p, " \
+		"mj_fn: %d, minor_fn: %d, nt_urb: %p, event: %p",	\
+		_irp, _irp->stack_count, (_irp)->current_location,	\
+		_irp_sl, _irp_sl->dev_obj, _irp_sl->major_fn,		\
+		_irp_sl->minor_fn, IRP_URB(_irp),			\
+		(_irp)->user_event);					\
+} while (0)
+#else
+#define DUMP_IRP(_irp) do { } while (0)
+#endif
+
+#endif // _NTOSKERNEL_H_
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/ntoskernel_io.c linux-5.6.11-ndis/3rdparty/ndiswrapper/ntoskernel_io.c
--- linux-5.6.11/3rdparty/ndiswrapper/ntoskernel_io.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/ntoskernel_io.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,1161 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include "ntoskernel.h"
+#include "ndis.h"
+#include "wrapndis.h"
+#include "usb.h"
+#include "loader.h"
+#include "ntoskernel_io_exports.h"
+
+wstdcall void WIN_FUNC(IoAcquireCancelSpinLock,1)
+	(KIRQL *irql) __acquires(irql)
+{
+	spin_lock_bh(&irp_cancel_lock);
+	*irql = 0;
+}
+
+wstdcall void WIN_FUNC(IoReleaseCancelSpinLock,1)
+	(KIRQL irql) __releases(irql)
+{
+	spin_unlock_bh(&irp_cancel_lock);
+}
+
+wstdcall int WIN_FUNC(IoIsWdmVersionAvailable,2)
+	(UCHAR major, UCHAR minor)
+{
+	IOENTER("%d, %x", major, minor);
+	if (major == 1 &&
+	    (minor == 0x30 || // Windows 2003
+	     minor == 0x20 || // Windows XP
+	     minor == 0x10)) // Windows 2000
+		IOEXIT(return TRUE);
+	IOEXIT(return FALSE);
+}
+
+wstdcall BOOLEAN WIN_FUNC(IoIs32bitProcess,1)
+	(struct irp *irp)
+{
+#ifdef CONFIG_X86_64
+	return FALSE;
+#else
+	return TRUE;
+#endif
+}
+
+wstdcall void WIN_FUNC(IoInitializeIrp,3)
+	(struct irp *irp, USHORT size, CCHAR stack_count)
+{
+	IOENTER("irp: %p, %d, %d", irp, size, stack_count);
+
+	memset(irp, 0, size);
+	irp->size = size;
+	irp->stack_count = stack_count;
+	irp->current_location = stack_count;
+	IoGetCurrentIrpStackLocation(irp) = IRP_SL(irp, stack_count);
+	IOEXIT(return);
+}
+
+wstdcall void WIN_FUNC(IoReuseIrp,2)
+	(struct irp *irp, NTSTATUS status)
+{
+	IOENTER("%p, %d", irp, status);
+	if (irp) {
+		UCHAR alloc_flags;
+
+		alloc_flags = irp->alloc_flags;
+		IoInitializeIrp(irp, irp->size, irp->stack_count);
+		irp->alloc_flags = alloc_flags;
+		irp->io_status.status = status;
+	}
+	IOEXIT(return);
+}
+
+wstdcall struct irp *WIN_FUNC(IoAllocateIrp,2)
+	(char stack_count, BOOLEAN charge_quota)
+{
+	struct irp *irp;
+	int irp_size;
+
+	IOENTER("count: %d", stack_count);
+	stack_count++;
+	irp_size = IoSizeOfIrp(stack_count);
+	irp = kmalloc(irp_size, irql_gfp());
+	if (irp)
+		IoInitializeIrp(irp, irp_size, stack_count);
+	IOTRACE("irp %p", irp);
+	IOEXIT(return irp);
+}
+
+wstdcall BOOLEAN WIN_FUNC(IoCancelIrp,1)
+	(struct irp *irp)
+{
+	typeof(irp->cancel_routine) cancel_routine;
+
+	/* NB: this function may be called at DISPATCH_LEVEL */
+	IOTRACE("irp: %p", irp);
+	if (!irp)
+		return FALSE;
+	DUMP_IRP(irp);
+	IoAcquireCancelSpinLock(&irp->cancel_irql);
+	cancel_routine = xchg(&irp->cancel_routine, NULL);
+	IOTRACE("%p", cancel_routine);
+	irp->cancel = TRUE;
+	if (cancel_routine) {
+		struct device_object *dev_obj;
+
+		if (irp->current_location >= 0 &&
+		    irp->current_location < irp->stack_count)
+			dev_obj = IoGetCurrentIrpStackLocation(irp)->dev_obj;
+		else
+			dev_obj = NULL;
+		IOTRACE("current_location: %d, dev_obj: %p",
+			irp->current_location, dev_obj);
+		/* cancel_routine will release the spin lock */
+		__release(irp->cancel_irql);
+		LIN2WIN2(cancel_routine, dev_obj, irp);
+		/* in usb's cancel, irp->cancel is set to indicate
+		 * status of cancel */
+		IOEXIT(return xchg(&irp->cancel, TRUE));
+	} else {
+		IOTRACE("irp %p already canceled", irp);
+		IoReleaseCancelSpinLock(irp->cancel_irql);
+		IOEXIT(return FALSE);
+	}
+}
+
+wstdcall void IoQueueThreadIrp(struct irp *irp)
+{
+	struct nt_thread *thread;
+	KIRQL irql;
+
+	thread = get_current_nt_thread();
+	if (thread) {
+		IOTRACE("thread: %p, task: %p", thread, thread->task);
+		irp->flags |= IRP_SYNCHRONOUS_API;
+		irql = nt_spin_lock_irql(&thread->lock, DISPATCH_LEVEL);
+		InsertTailList(&thread->irps, &irp->thread_list);
+		IoIrpThread(irp) = thread;
+		nt_spin_unlock_irql(&thread->lock, irql);
+	} else
+		IoIrpThread(irp) = NULL;
+}
+
+wstdcall void IoDequeueThreadIrp(struct irp *irp)
+{
+	struct nt_thread *thread;
+	KIRQL irql;
+
+	thread = IoIrpThread(irp);
+	if (thread) {
+		irql = nt_spin_lock_irql(&thread->lock, DISPATCH_LEVEL);
+		RemoveEntryList(&irp->thread_list);
+		nt_spin_unlock_irql(&thread->lock, irql);
+	}
+}
+
+wstdcall void WIN_FUNC(IoFreeIrp,1)
+	(struct irp *irp)
+{
+	IOENTER("irp = %p", irp);
+	if (!irp) {
+		WARNING("irp is NULL");
+		return;
+	}
+
+	if (irp->flags & IRP_SYNCHRONOUS_API)
+		IoDequeueThreadIrp(irp);
+	IoCancelIrp(irp);
+	kfree(irp);
+
+	IOEXIT(return);
+}
+
+wstdcall struct irp *WIN_FUNC(IoBuildAsynchronousFsdRequest,6)
+	(ULONG major_fn, struct device_object *dev_obj, void *buffer,
+	 ULONG length, LARGE_INTEGER *offset,
+	 struct io_status_block *user_status)
+{
+	struct irp *irp;
+	struct io_stack_location *irp_sl;
+
+	IOENTER("%p", dev_obj);
+	if (!dev_obj)
+		IOEXIT(return NULL);
+	irp = IoAllocateIrp(dev_obj->stack_count, FALSE);
+	if (irp == NULL) {
+		WARNING("couldn't allocate irp");
+		IOEXIT(return NULL);
+	}
+
+	irp_sl = IoGetNextIrpStackLocation(irp);
+	irp_sl->major_fn = major_fn;
+	IOTRACE("major_fn: %d", major_fn);
+	irp_sl->minor_fn = 0;
+	irp_sl->flags = 0;
+	irp_sl->control = 0;
+	irp_sl->dev_obj = dev_obj;
+	irp_sl->file_obj = NULL;
+	irp_sl->completion_routine = NULL;
+
+	if (dev_obj->flags & DO_DIRECT_IO) {
+		irp->mdl = IoAllocateMdl(buffer, length, FALSE, FALSE, irp);
+		if (irp->mdl == NULL) {
+			IoFreeIrp(irp);
+			return NULL;
+		}
+		MmProbeAndLockPages(irp->mdl, KernelMode,
+				    major_fn == IRP_MJ_WRITE ?
+				    IoReadAccess : IoWriteAccess);
+		IOTRACE("mdl: %p", irp->mdl);
+	} else if (dev_obj->flags & DO_BUFFERED_IO) {
+		irp->associated_irp.system_buffer = buffer;
+		irp->flags = IRP_BUFFERED_IO;
+		irp->mdl = NULL;
+		IOTRACE("buffer: %p", buffer);
+	}
+	if (major_fn == IRP_MJ_READ) {
+		irp_sl->params.read.length = length;
+		irp_sl->params.read.byte_offset = *offset;
+	} else if (major_fn == IRP_MJ_WRITE) {
+		irp_sl->params.write.length = length;
+		irp_sl->params.write.byte_offset = *offset;
+	}
+	irp->user_status = user_status;
+	IOTRACE("irp: %p", irp);
+	return irp;
+}
+
+wstdcall struct irp *WIN_FUNC(IoBuildSynchronousFsdRequest,7)
+	(ULONG major_fn, struct device_object *dev_obj, void *buf,
+	 ULONG length, LARGE_INTEGER *offset, struct nt_event *event,
+	 struct io_status_block *user_status)
+{
+	struct irp *irp;
+
+	irp = IoBuildAsynchronousFsdRequest(major_fn, dev_obj, buf, length,
+					    offset, user_status);
+	if (irp == NULL)
+		return NULL;
+	irp->user_event = event;
+	IoQueueThreadIrp(irp);
+	return irp;
+}
+
+wstdcall struct irp *WIN_FUNC(IoBuildDeviceIoControlRequest,9)
+	(ULONG ioctl, struct device_object *dev_obj,
+	 void *input_buf, ULONG input_buf_len, void *output_buf,
+	 ULONG output_buf_len, BOOLEAN internal_ioctl,
+	 struct nt_event *event, struct io_status_block *io_status)
+{
+	struct irp *irp;
+	struct io_stack_location *irp_sl;
+	ULONG buf_len;
+
+	IOENTER("%p, 0x%08x, %d", dev_obj, ioctl, internal_ioctl);
+	if (!dev_obj)
+		IOEXIT(return NULL);
+	irp = IoAllocateIrp(dev_obj->stack_count, FALSE);
+	if (irp == NULL) {
+		WARNING("couldn't allocate irp");
+		return NULL;
+	}
+	irp_sl = IoGetNextIrpStackLocation(irp);
+	irp_sl->params.dev_ioctl.code = ioctl;
+	irp_sl->params.dev_ioctl.input_buf_len = input_buf_len;
+	irp_sl->params.dev_ioctl.output_buf_len = output_buf_len;
+	irp_sl->major_fn = (internal_ioctl) ?
+		IRP_MJ_INTERNAL_DEVICE_CONTROL : IRP_MJ_DEVICE_CONTROL;
+	IOTRACE("%d", IO_METHOD_FROM_CTL_CODE(ioctl));
+
+	switch (IO_METHOD_FROM_CTL_CODE(ioctl)) {
+	case METHOD_BUFFERED:
+		buf_len = max(input_buf_len, output_buf_len);
+		if (buf_len) {
+			irp->associated_irp.system_buffer =
+				ExAllocatePoolWithTag(NonPagedPool, buf_len, 0);
+			if (!irp->associated_irp.system_buffer) {
+				IoFreeIrp(irp);
+				IOEXIT(return NULL);
+			}
+			irp->associated_irp.system_buffer = input_buf;
+			if (input_buf)
+				memcpy(irp->associated_irp.system_buffer,
+				       input_buf, input_buf_len);
+			irp->flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
+			if (output_buf)
+				irp->flags = IRP_INPUT_OPERATION;
+			irp->user_buf = output_buf;
+		} else
+			irp->user_buf = NULL;
+		break;
+	case METHOD_IN_DIRECT:
+	case METHOD_OUT_DIRECT:
+		if (input_buf) {
+			irp->associated_irp.system_buffer =
+				ExAllocatePoolWithTag(NonPagedPool,
+						      input_buf_len, 0);
+			if (!irp->associated_irp.system_buffer) {
+				IoFreeIrp(irp);
+				IOEXIT(return NULL);
+			}
+			memcpy(irp->associated_irp.system_buffer,
+			       input_buf, input_buf_len);
+			irp->flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
+		}
+		/* TODO: we are supposed to setup MDL, but USB layer
+		 * doesn't use MDLs. Moreover, USB layer mirrors
+		 * non-DMAable buffers, so no need to allocate
+		 * DMAable buffer here */
+		if (output_buf) {
+			irp->associated_irp.system_buffer =
+				ExAllocatePoolWithTag(NonPagedPool,
+						      output_buf_len, 0);
+			if (!irp->associated_irp.system_buffer) {
+				IoFreeIrp(irp);
+				IOEXIT(return NULL);
+			}
+			irp->flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
+		}
+		break;
+	case METHOD_NEITHER:
+		irp->user_buf = output_buf;
+		irp_sl->params.dev_ioctl.type3_input_buf = input_buf;
+		break;
+	}
+
+	irp->user_status = io_status;
+	irp->user_event = event;
+	IoQueueThreadIrp(irp);
+
+	IOTRACE("irp: %p", irp);
+	IOEXIT(return irp);
+}
+
+wfastcall NTSTATUS WIN_FUNC(IofCallDriver,2)
+	(struct device_object *dev_obj, struct irp *irp)
+{
+	struct io_stack_location *irp_sl;
+	NTSTATUS status;
+	driver_dispatch_t *major_func;
+	struct driver_object *drv_obj;
+
+	if (irp->current_location <= 0) {
+		ERROR("invalid irp: %p, %d", irp, irp->current_location);
+		return STATUS_INVALID_PARAMETER;
+	}
+	IOTRACE("%p, %p, %p, %d, %d, %p", dev_obj, irp, dev_obj->drv_obj,
+		irp->current_location, irp->stack_count,
+		IoGetCurrentIrpStackLocation(irp));
+	IoSetNextIrpStackLocation(irp);
+	DUMP_IRP(irp);
+	irp_sl = IoGetCurrentIrpStackLocation(irp);
+	drv_obj = dev_obj->drv_obj;
+	irp_sl->dev_obj = dev_obj;
+	major_func = drv_obj->major_func[irp_sl->major_fn];
+	IOTRACE("major_func: %p, dev_obj: %p", major_func, dev_obj);
+	if (major_func)
+		status = LIN2WIN2(major_func, dev_obj, irp);
+	else {
+		ERROR("major_function %d is not implemented",
+		      irp_sl->major_fn);
+		status = STATUS_NOT_SUPPORTED;
+	}
+	IOEXIT(return status);
+}
+
+wfastcall void WIN_FUNC(IofCompleteRequest,2)
+	(struct irp *irp, CHAR prio_boost)
+{
+	struct io_stack_location *irp_sl;
+
+#ifdef IO_DEBUG
+	DUMP_IRP(irp);
+	if (irp->io_status.status == STATUS_PENDING) {
+		ERROR("invalid irp: %p, STATUS_PENDING", irp);
+		return;
+	}
+	if (irp->current_location < 0 ||
+	    irp->current_location >= irp->stack_count) {
+		ERROR("invalid irp: %p, %d", irp, irp->current_location);
+		return;
+	}
+#endif
+	for (irp_sl = IoGetCurrentIrpStackLocation(irp);
+	     irp->current_location < irp->stack_count; irp_sl++) {
+		struct device_object *dev_obj;
+		NTSTATUS status;
+
+		DUMP_IRP(irp);
+		if (irp_sl->control & SL_PENDING_RETURNED)
+			irp->pending_returned = TRUE;
+
+		/* current_location and dev_obj must be same as when
+		 * driver called IoSetCompletionRoutine, which sets
+		 * completion routine at next (lower) location, which
+		 * is what we are going to call below; so we set
+		 * current_location and dev_obj for the previous
+		 * (higher) location */
+		IoSkipCurrentIrpStackLocation(irp);
+		if (irp->current_location < irp->stack_count)
+			dev_obj = IoGetCurrentIrpStackLocation(irp)->dev_obj;
+		else
+			dev_obj = NULL;
+
+		IOTRACE("%d, %d, %p", irp->current_location, irp->stack_count,
+			dev_obj);
+		if (irp_sl->completion_routine &&
+		    ((irp->io_status.status == STATUS_SUCCESS &&
+		      irp_sl->control & SL_INVOKE_ON_SUCCESS) ||
+		     (irp->io_status.status != STATUS_SUCCESS &&
+		      irp_sl->control & SL_INVOKE_ON_ERROR) ||
+		     (irp->cancel == TRUE &&
+		      irp_sl->control & SL_INVOKE_ON_CANCEL))) {
+			IOTRACE("calling completion_routine at: %p, %p",
+				irp_sl->completion_routine, irp_sl->context);
+			status = LIN2WIN3(irp_sl->completion_routine,
+					  dev_obj, irp, irp_sl->context);
+			IOTRACE("status: %08X", status);
+			if (status == STATUS_MORE_PROCESSING_REQUIRED)
+				IOEXIT(return);
+		} else {
+			/* propagate pending status to next irp_sl */
+			if (irp->pending_returned &&
+			    irp->current_location < irp->stack_count)
+				IoMarkIrpPending(irp);
+		}
+	}
+
+	if (irp->user_status) {
+		irp->user_status->status = irp->io_status.status;
+		irp->user_status->info = irp->io_status.info;
+	}
+
+	if (irp->user_event) {
+		IOTRACE("setting event %p", irp->user_event);
+		KeSetEvent(irp->user_event, prio_boost, FALSE);
+	}
+
+	if (irp->associated_irp.system_buffer &&
+	    (irp->flags & IRP_DEALLOCATE_BUFFER))
+		ExFreePool(irp->associated_irp.system_buffer);
+	else {
+		struct mdl *mdl;
+		while ((mdl = irp->mdl)) {
+			irp->mdl = mdl->next;
+			MmUnlockPages(mdl);
+			IoFreeMdl(mdl);
+		}
+	}
+	IOTRACE("freeing irp %p", irp);
+	IoFreeIrp(irp);
+	IOEXIT(return);
+}
+
+wstdcall NTSTATUS IoPassIrpDown(struct device_object *dev_obj, struct irp *irp)
+{
+	IoSkipCurrentIrpStackLocation(irp);
+	IOEXIT(return IoCallDriver(dev_obj, irp));
+}
+
+wstdcall NTSTATUS IoIrpSyncComplete(struct device_object *dev_obj,
+				    struct irp *irp, void *context)
+{
+	if (irp->pending_returned == TRUE)
+		KeSetEvent(context, IO_NO_INCREMENT, FALSE);
+	IOEXIT(return STATUS_MORE_PROCESSING_REQUIRED);
+}
+WIN_FUNC_DECL(IoIrpSyncComplete,3)
+
+wstdcall NTSTATUS IoSyncForwardIrp(struct device_object *dev_obj,
+				   struct irp *irp)
+{
+	struct nt_event event;
+	NTSTATUS status;
+
+	IoCopyCurrentIrpStackLocationToNext(irp);
+	KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+	/* completion function is called as Windows function */
+	IoSetCompletionRoutine(irp, WIN_FUNC_PTR(IoIrpSyncComplete,3), &event,
+			       TRUE, TRUE, TRUE);
+	status = IoCallDriver(dev_obj, irp);
+	IOTRACE("%08X", status);
+	if (status == STATUS_PENDING) {
+		KeWaitForSingleObject(&event, Executive, KernelMode, FALSE,
+				      NULL);
+		status = irp->io_status.status;
+	}
+	IOTRACE("%08X", status);
+	IOEXIT(return status);
+}
+WIN_FUNC_DECL(IoSyncForwardIrp,2)
+
+wstdcall NTSTATUS IoAsyncForwardIrp(struct device_object *dev_obj,
+				    struct irp *irp)
+{
+	NTSTATUS status;
+
+	IoCopyCurrentIrpStackLocationToNext(irp);
+	status = IoCallDriver(dev_obj, irp);
+	IOEXIT(return status);
+}
+WIN_FUNC_DECL(IoAsyncForwardIrp,2)
+
+wstdcall NTSTATUS IoInvalidDeviceRequest(struct device_object *dev_obj,
+					 struct irp *irp)
+{
+	struct io_stack_location *irp_sl;
+	NTSTATUS status;
+
+	irp_sl = IoGetCurrentIrpStackLocation(irp);
+	WARNING("%d:%d not implemented", irp_sl->major_fn, irp_sl->minor_fn);
+	irp->io_status.status = STATUS_SUCCESS;
+	irp->io_status.info = 0;
+	status = irp->io_status.status;
+	IoCompleteRequest(irp, IO_NO_INCREMENT);
+	IOEXIT(return status);
+}
+WIN_FUNC_DECL(IoInvalidDeviceRequest,2)
+
+static irqreturn_t io_irq_isr(int irq, void *data ISR_PT_REGS_PARAM_DECL)
+{
+	struct kinterrupt *interrupt = data;
+	BOOLEAN ret;
+
+#ifdef CONFIG_DEBUG_SHIRQ
+	if (!interrupt->u.enabled)
+		EXIT1(return IRQ_NONE);
+#endif
+	TRACE6("%p", interrupt);
+	nt_spin_lock(interrupt->actual_lock);
+	ret = LIN2WIN2(interrupt->isr, interrupt, interrupt->isr_ctx);
+	nt_spin_unlock(interrupt->actual_lock);
+	if (ret == TRUE)
+		EXIT6(return IRQ_HANDLED);
+	else
+		EXIT6(return IRQ_NONE);
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoConnectInterrupt,11)
+	(struct kinterrupt **kinterrupt, PKSERVICE_ROUTINE isr, void *isr_ctx,
+	 NT_SPIN_LOCK *lock, ULONG vector, KIRQL irql, KIRQL synch_irql,
+	 enum kinterrupt_mode mode, BOOLEAN shared, KAFFINITY cpu_mask,
+	 BOOLEAN save_fp)
+{
+	struct kinterrupt *interrupt;
+	IOENTER("");
+	interrupt = kzalloc(sizeof(*interrupt), GFP_KERNEL);
+	if (!interrupt)
+		IOEXIT(return STATUS_INSUFFICIENT_RESOURCES);
+	interrupt->vector = vector;
+	interrupt->cpu_mask = cpu_mask;
+	nt_spin_lock_init(&interrupt->lock);
+	if (lock)
+		interrupt->actual_lock = lock;
+	else
+		interrupt->actual_lock = &interrupt->lock;
+	interrupt->shared = shared;
+	interrupt->save_fp = save_fp;
+	interrupt->isr = isr;
+	interrupt->isr_ctx = isr_ctx;
+	InitializeListHead(&interrupt->list);
+	interrupt->irql = irql;
+	interrupt->synch_irql = synch_irql;
+	interrupt->mode = mode;
+	if (request_irq(vector, io_irq_isr, shared ? IRQF_SHARED : 0,
+			DRIVER_NAME, interrupt)) {
+		WARNING("request for irq %d failed", vector);
+		kfree(interrupt);
+		IOEXIT(return STATUS_INSUFFICIENT_RESOURCES);
+	}
+	*kinterrupt = interrupt;
+#ifdef CONFIG_DEBUG_SHIRQ
+	interrupt->u.enabled = 1;
+#endif
+	IOEXIT(return STATUS_SUCCESS);
+}
+
+wstdcall void WIN_FUNC(IoDisconnectInterrupt,1)
+	(struct kinterrupt *interrupt)
+{
+#ifdef CONFIG_DEBUG_SHIRQ
+	interrupt->u.enabled = 0;
+#endif
+	free_irq(interrupt->vector, interrupt);
+	kfree(interrupt);
+}
+
+wstdcall struct mdl *WIN_FUNC(IoAllocateMdl,5)
+	(void *virt, ULONG length, BOOLEAN second_buf, BOOLEAN charge_quota,
+	 struct irp *irp)
+{
+	struct mdl *mdl;
+	mdl = allocate_init_mdl(virt, length);
+	if (!mdl)
+		return NULL;
+	if (irp) {
+		if (second_buf == TRUE) {
+			struct mdl *last;
+
+			last = irp->mdl;
+			while (last->next)
+				last = last->next;
+			last->next = mdl;
+		} else
+			irp->mdl = mdl;
+	}
+	IOTRACE("%p", mdl);
+	return mdl;
+}
+
+wstdcall void WIN_FUNC(IoFreeMdl,1)
+	(struct mdl *mdl)
+{
+	IOTRACE("%p", mdl);
+	free_mdl(mdl);
+}
+
+wstdcall struct io_workitem *WIN_FUNC(IoAllocateWorkItem,1)
+	(struct device_object *dev_obj)
+{
+	struct io_workitem *io_workitem;
+
+	IOENTER("%p", dev_obj);
+	io_workitem = kmalloc(sizeof(*io_workitem), irql_gfp());
+	if (!io_workitem)
+		IOEXIT(return NULL);
+	io_workitem->dev_obj = dev_obj;
+	IOEXIT(return io_workitem);
+}
+
+wstdcall void WIN_FUNC(IoFreeWorkItem,1)
+	(struct io_workitem *io_workitem)
+{
+	kfree(io_workitem);
+	IOEXIT(return);
+}
+
+wstdcall void WIN_FUNC(IoQueueWorkItem,4)
+	(struct io_workitem *io_workitem, void *func,
+	 enum work_queue_type queue_type, void *context)
+{
+	IOENTER("%p, %p", io_workitem, io_workitem->dev_obj);
+	io_workitem->worker_routine = func;
+	io_workitem->context = context;
+	schedule_ntos_work_item(func, io_workitem->dev_obj, context);
+	IOEXIT(return);
+}
+
+wstdcall void WIN_FUNC(ExQueueWorkItem,2)
+	(struct io_workitem *io_workitem, enum work_queue_type queue_type)
+{
+	IOENTER("%p", io_workitem);
+	schedule_ntos_work_item(io_workitem->worker_routine,
+				io_workitem->dev_obj, io_workitem->context);
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoAllocateDriverObjectExtension,4)
+	(struct driver_object *drv_obj, void *client_id, ULONG extlen,
+	 void **ext)
+{
+	struct custom_ext *ce;
+
+	IOENTER("%p, %p", drv_obj, client_id);
+	ce = kmalloc(sizeof(*ce) + extlen, irql_gfp());
+	if (ce == NULL)
+		return STATUS_INSUFFICIENT_RESOURCES;
+
+	IOTRACE("custom_ext: %p", ce);
+	ce->client_id = client_id;
+	spin_lock_bh(&ntoskernel_lock);
+	InsertTailList(&drv_obj->drv_ext->custom_ext, &ce->list);
+	spin_unlock_bh(&ntoskernel_lock);
+
+	*ext = (void *)ce + sizeof(*ce);
+	IOTRACE("ext: %p", *ext);
+	IOEXIT(return STATUS_SUCCESS);
+}
+
+wstdcall void *WIN_FUNC(IoGetDriverObjectExtension,2)
+	(struct driver_object *drv_obj, void *client_id)
+{
+	struct custom_ext *ce;
+	void *ret;
+
+	IOENTER("drv_obj: %p, client_id: %p", drv_obj, client_id);
+	ret = NULL;
+	spin_lock_bh(&ntoskernel_lock);
+	nt_list_for_each_entry(ce, &drv_obj->drv_ext->custom_ext, list) {
+		if (ce->client_id == client_id) {
+			ret = (void *)ce + sizeof(*ce);
+			break;
+		}
+	}
+	spin_unlock_bh(&ntoskernel_lock);
+	IOTRACE("ret: %p", ret);
+	return ret;
+}
+
+void free_custom_extensions(struct driver_extension *drv_ext)
+{
+	struct nt_list *ent;
+
+	IOENTER("%p", drv_ext);
+	spin_lock_bh(&ntoskernel_lock);
+	while ((ent = RemoveHeadList(&drv_ext->custom_ext)))
+		kfree(ent);
+	spin_unlock_bh(&ntoskernel_lock);
+	IOEXIT(return);
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoCreateDevice,7)
+	(struct driver_object *drv_obj, ULONG dev_ext_length,
+	 struct unicode_string *dev_name, DEVICE_TYPE dev_type,
+	 ULONG dev_chars, BOOLEAN exclusive, struct device_object **newdev)
+{
+	struct device_object *dev;
+	struct dev_obj_ext *dev_obj_ext;
+	int size;
+
+	IOENTER("%p, %u, %p", drv_obj, dev_ext_length, dev_name);
+
+	size = sizeof(*dev) + dev_ext_length + sizeof(*dev_obj_ext);
+	dev = allocate_object(size, OBJECT_TYPE_DEVICE, dev_name);
+	if (!dev)
+		IOEXIT(return STATUS_INSUFFICIENT_RESOURCES);
+	if (dev_ext_length)
+		dev->dev_ext = dev + 1;
+	else
+		dev->dev_ext = NULL;
+
+	dev_obj_ext = ((void *)(dev + 1)) + dev_ext_length;
+	dev_obj_ext->dev_obj = dev;
+	dev_obj_ext->size = 0;
+	dev_obj_ext->type = IO_TYPE_DEVICE;
+	dev->dev_obj_ext = dev_obj_ext;
+
+	dev->type = dev_type;
+	dev->flags = 0;
+	dev->size = sizeof(*dev) + dev_ext_length;
+	dev->ref_count = 1;
+	dev->attached = NULL;
+	dev->stack_count = 1;
+
+	dev->drv_obj = drv_obj;
+	dev->next = drv_obj->dev_obj;
+	drv_obj->dev_obj = dev;
+
+	dev->align_req = 1;
+	dev->characteristics = dev_chars;
+	dev->io_timer = NULL;
+	KeInitializeEvent(&dev->lock, SynchronizationEvent, TRUE);
+	dev->vpb = NULL;
+
+	IOTRACE("dev: %p, ext: %p", dev, dev->dev_ext);
+	*newdev = dev;
+	IOEXIT(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoCreateUnprotectedSymbolicLink,2)
+	(struct unicode_string *link, struct unicode_string *dev_name)
+{
+	struct ansi_string ansi;
+
+	IOENTER("%p, %p", dev_name, link);
+	if (dev_name && (RtlUnicodeStringToAnsiString(&ansi, dev_name, TRUE) ==
+			 STATUS_SUCCESS)) {
+		IOTRACE("dev_name: %s", ansi.buf);
+		RtlFreeAnsiString(&ansi);
+	}
+	if (link && (RtlUnicodeStringToAnsiString(&ansi, link, TRUE) ==
+		     STATUS_SUCCESS)) {
+		IOTRACE("link: %s", ansi.buf);
+		RtlFreeAnsiString(&ansi);
+	}
+//	TODO();
+	IOEXIT(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoCreateSymbolicLink,2)
+	(struct unicode_string *link, struct unicode_string *dev_name)
+{
+	IOEXIT(return IoCreateUnprotectedSymbolicLink(link, dev_name));
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoDeleteSymbolicLink,1)
+	(struct unicode_string *link)
+{
+	struct ansi_string ansi;
+
+	IOENTER("%p", link);
+	if (link && (RtlUnicodeStringToAnsiString(&ansi, link, TRUE) ==
+		     STATUS_SUCCESS)) {
+		IOTRACE("dev_name: %s", ansi.buf);
+		RtlFreeAnsiString(&ansi);
+	}
+	IOEXIT(return STATUS_SUCCESS);
+}
+
+wstdcall void WIN_FUNC(IoDeleteDevice,1)
+	(struct device_object *dev)
+{
+	IOENTER("%p", dev);
+	if (dev == NULL)
+		IOEXIT(return);
+	IOTRACE("drv_obj: %p", dev->drv_obj);
+	if (dev->drv_obj) {
+		struct device_object *prev;
+
+		prev = dev->drv_obj->dev_obj;
+		IOTRACE("dev_obj: %p", prev);
+		if (prev == dev)
+			dev->drv_obj->dev_obj = dev->next;
+		else if (prev) {
+			while (prev->next != dev)
+				prev = prev->next;
+			prev->next = dev->next;
+		}
+	}
+	ObDereferenceObject(dev);
+	IOEXIT(return);
+}
+
+wstdcall void WIN_FUNC(IoDetachDevice,1)
+	(struct device_object *tgt)
+{
+	struct device_object *tail;
+
+	IOENTER("%p", tgt);
+	if (!tgt)
+		IOEXIT(return);
+	tail = tgt->attached;
+	if (!tail)
+		IOEXIT(return);
+	IOTRACE("tail: %p", tail);
+
+	spin_lock_bh(&ntoskernel_lock);
+	tgt->attached = tail->attached;
+	IOTRACE("attached:%p", tgt->attached);
+	for ( ; tail; tail = tail->attached) {
+		IOTRACE("tail:%p", tail);
+		tail->stack_count--;
+	}
+	spin_unlock_bh(&ntoskernel_lock);
+	IOEXIT(return);
+}
+
+wstdcall struct device_object *WIN_FUNC(IoGetAttachedDevice,1)
+	(struct device_object *dev)
+{
+	IOENTER("%p", dev);
+	if (!dev)
+		IOEXIT(return NULL);
+	spin_lock_bh(&ntoskernel_lock);
+	while (dev->attached)
+		dev = dev->attached;
+	spin_unlock_bh(&ntoskernel_lock);
+	IOEXIT(return dev);
+}
+
+wstdcall struct device_object *WIN_FUNC(IoGetAttachedDeviceReference,1)
+	(struct device_object *dev)
+{
+	IOENTER("%p", dev);
+	if (!dev)
+		IOEXIT(return NULL);
+	dev = IoGetAttachedDevice(dev);
+	ObReferenceObject(dev);
+	IOEXIT(return dev);
+}
+
+wstdcall struct device_object *WIN_FUNC(IoAttachDeviceToDeviceStack,2)
+	(struct device_object *src, struct device_object *tgt)
+{
+	struct device_object *attached;
+	struct dev_obj_ext *src_dev_ext;
+
+	IOENTER("%p, %p", src, tgt);
+	attached = IoGetAttachedDevice(tgt);
+	IOTRACE("%p", attached);
+	src_dev_ext = src->dev_obj_ext;
+	spin_lock_bh(&ntoskernel_lock);
+	if (attached)
+		attached->attached = src;
+	src->attached = NULL;
+	src->stack_count = attached->stack_count + 1;
+	src_dev_ext->attached_to = attached;
+	spin_unlock_bh(&ntoskernel_lock);
+	IOTRACE("stack_count: %d -> %d", attached->stack_count,
+		src->stack_count);
+	IOEXIT(return attached);
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoGetDeviceProperty,5)
+	(struct device_object *pdo, enum device_registry_property dev_property,
+	 ULONG buffer_len, void *buffer, ULONG *result_len)
+{
+	struct ansi_string ansi;
+	struct unicode_string unicode;
+	struct wrap_device *wd;
+	ULONG need;
+
+	IOENTER("dev_obj = %p, dev_property = %d, buffer_len = %u, "
+		"buffer = %p, result_len = %p", pdo, dev_property,
+		buffer_len, buffer, result_len);
+
+	wd = pdo->reserved;
+	switch (dev_property) {
+	case DevicePropertyDeviceDescription:
+	case DevicePropertyFriendlyName:
+	case DevicePropertyDriverKeyName:
+		if (wrap_is_pci_bus(wd->dev_bus))
+			RtlInitAnsiString(&ansi, "PCI");
+		else // if (wrap_is_usb_bus(wd->dev_bus))
+			RtlInitAnsiString(&ansi, "USB");
+		need = sizeof(wchar_t) * (ansi.max_length + 1);
+		if (buffer_len < need) {
+			*result_len = need;
+			IOEXIT(return STATUS_BUFFER_TOO_SMALL);
+		}
+		unicode.max_length = buffer_len;
+		unicode.buf = buffer;
+		if (RtlAnsiStringToUnicodeString(&unicode, &ansi,
+						 FALSE) != STATUS_SUCCESS) {
+			*result_len = unicode.length;
+			IOEXIT(return STATUS_BUFFER_TOO_SMALL);
+		}
+		IOEXIT(return STATUS_SUCCESS);
+	default:
+		WARNING("%d not implemented", dev_property);
+		IOEXIT(return STATUS_INVALID_PARAMETER_2);
+	}
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoGetDeviceObjectPointer,4)
+	(struct unicode_string *name, ACCESS_MASK desired_access,
+	 struct file_object *file_obj, struct device_object *dev_obj)
+{
+	struct common_object_header *coh;
+
+	dev_obj = NULL;
+	/* TODO: access is not checked and file_obj is filled with zeroes */
+	spin_lock_bh(&ntoskernel_lock);
+	nt_list_for_each_entry(coh, &object_list, list) {
+		TRACE5("header: %p, type: %d", coh, coh->type);
+		if (coh->type != OBJECT_TYPE_DEVICE)
+			continue;
+		if (!RtlCompareUnicodeString(&coh->name, name, TRUE)) {
+			dev_obj = HEADER_TO_OBJECT(coh);
+			TRACE5("dev_obj: %p", dev_obj);
+			break;
+		}
+	}
+	spin_unlock_bh(&ntoskernel_lock);
+	if (dev_obj) {
+		memset(file_obj, 0, sizeof(*file_obj));
+		WARNING("file_obj filled with zeroes");
+		IOEXIT(return STATUS_SUCCESS);
+	} else
+		IOEXIT(return STATUS_OBJECT_NAME_INVALID);
+}
+
+/* NOTE: Make sure to compile with -freg-struct-return, so gcc will
+ * return union in register, like Windows */
+wstdcall union power_state WIN_FUNC(PoSetPowerState,3)
+	(struct device_object *dev_obj, enum power_state_type type,
+	 union power_state state)
+{
+	IOEXIT(return state);
+}
+
+wstdcall NTSTATUS WIN_FUNC(PoCallDriver,2)
+	(struct device_object *dev_obj, struct irp *irp)
+{
+	return IoCallDriver(dev_obj, irp);
+}
+
+wstdcall NTSTATUS WIN_FUNC(PoRequestPowerIrp,6)
+	(struct device_object *dev_obj, UCHAR minor_fn,
+	 union power_state power_state, void *completion_func,
+	 void *context, struct irp **pirp)
+{
+	struct irp *irp;
+	struct io_stack_location *irp_sl;
+
+	TRACE1("%p, %d, %p", dev_obj, dev_obj->stack_count, dev_obj->drv_obj);
+	irp = IoAllocateIrp(dev_obj->stack_count, FALSE);
+	if (!irp)
+		return STATUS_INSUFFICIENT_RESOURCES;
+	irp_sl = IoGetNextIrpStackLocation(irp);
+	irp_sl->major_fn = IRP_MJ_POWER;
+	irp_sl->minor_fn = minor_fn;
+	if (minor_fn == IRP_MN_WAIT_WAKE)
+		irp_sl->params.power.type = SystemPowerState;
+	else
+		irp_sl->params.power.type = DevicePowerState;
+	irp_sl->params.power.state = power_state;
+	irp_sl->completion_routine = completion_func;
+	irp->io_status.status = STATUS_NOT_SUPPORTED;
+	*pirp = irp;
+	return PoCallDriver(dev_obj, irp);
+}
+
+wstdcall void WIN_FUNC(PoStartNextPowerIrp,1)
+	(struct irp *irp)
+{
+	IOENTER("irp = %p", irp);
+	IOEXIT(return);
+}
+
+wstdcall void WIN_FUNC(IoInitializeRemoveLockEx,5)
+	(struct io_remove_lock *lock, ULONG alloc_tag, ULONG max_locked_min,
+	 ULONG high_mark, ULONG lock_size)
+{
+	TODO();
+}
+
+wstdcall void *WIN_FUNC(IoAllocateErrorLogEntry,2)
+	(void *io_object, UCHAR entry_size)
+{
+	/* not implemented fully */
+	void *ret = kmalloc(sizeof(struct io_error_log_packet) + entry_size,
+			    irql_gfp());
+	TRACE2("%p", ret);
+	if (ret)
+		return ret + sizeof(struct io_error_log_packet);
+	else
+		return NULL;
+}
+
+wstdcall void WIN_FUNC(IoWriteErrorLogEntry,1)
+	(void *entry)
+{
+	/* TODO: log error with codes and message */
+	ERROR("");
+}
+
+wstdcall void WIN_FUNC(IoFreeErrorLogEntry,1)
+	(void *entry)
+{
+	TRACE2("%p", entry);
+	kfree(entry - sizeof(struct io_error_log_packet));
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoAcquireRemoveLockEx,5)
+	(struct io_remove_lock *lock, void *tag, char *file, ULONG line,
+	 ULONG lock_size)
+{
+	TODO();
+	IOEXIT(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoReleaseRemoveLockEx,3)
+	(struct io_remove_lock *lock, void *tag, ULONG lock_size)
+{
+	TODO();
+	IOEXIT(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoRegisterDeviceInterface,4)
+	(struct device_object *pdo, struct guid *guid_class,
+	 struct unicode_string *reference, struct unicode_string *link)
+{
+	struct ansi_string ansi;
+
+	/* TODO: check if pdo is valid */
+	RtlInitAnsiString(&ansi, "ndis");
+	ENTER1("pdo: %p, ref: %p, link: %p, %x, %x, %x", pdo, reference, link,
+	       guid_class->data1, guid_class->data2, guid_class->data3);
+	return RtlAnsiStringToUnicodeString(link, &ansi, TRUE);
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoSetDeviceInterfaceState,2)
+	(struct unicode_string *link, BOOLEAN enable)
+{
+	ENTER1("link: %p, enable: %d", link, enable);
+	return STATUS_SUCCESS;
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoOpenDeviceRegistryKey,4)
+	(struct device_object *dev_obj, ULONG type, ACCESS_MASK mask,
+	 void **handle)
+{
+	ENTER1("dev_obj: %p", dev_obj);
+	*handle = dev_obj;
+	return STATUS_SUCCESS;
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoWMIRegistrationControl,2)
+	(struct device_object *dev_obj, ULONG action)
+{
+	ENTER2("%p, %d", dev_obj, action);
+	EXIT2(return STATUS_SUCCESS);
+}
+
+wstdcall void WIN_FUNC(IoInvalidateDeviceRelations,2)
+	(struct device_object *dev_obj, enum device_relation_type type)
+{
+	INFO("%p, %d", dev_obj, type);
+	TODO();
+}
+
+wstdcall void WIN_FUNC(IoInvalidateDeviceState,1)
+	(struct device_object *pdo)
+{
+	INFO("%p", pdo);
+	TODO();
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoRegisterPlugPlayNotification,7)
+	(UINT category, ULONG flags, void *data, struct driver_object *object,
+	 void *callback, void *context, void **notification_entry)
+{
+	TRACE2("category: %d, flags 0x%x, data: %p, object: %p, callback: %p, "
+	       "context: %p, notification_entry: %p", category, flags, data,
+	       object, callback, context, notification_entry);
+	TODO();
+	IOEXIT(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoUnregisterPlugPlayNotification,1)
+	(void *notification_entry)
+{
+	TRACE2("%p", notification_entry);
+	TODO();
+	IOEXIT(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoWMIOpenBlock,3)
+	(struct guid *guid, ULONG access, void *object)
+{
+	TODO();
+	IOEXIT(return STATUS_NOT_IMPLEMENTED);
+}
+
+wstdcall NTSTATUS WIN_FUNC(IoWMIQueryAllData,3)
+	(void *data_block_object, UINT *buffer_size, void *buffer)
+{
+	TODO();
+	IOEXIT(return STATUS_NOT_IMPLEMENTED);
+}
+
+wstdcall void WIN_FUNC(IoGetStackLimits,2)
+	(ULONG_PTR *LowLimit, ULONG_PTR *HighLimit)
+{
+	*LowLimit = (ULONG_PTR)&LowLimit & ~(THREAD_SIZE - 1);
+	*HighLimit = *LowLimit + THREAD_SIZE;
+	IOTRACE("LowLimit: 0x%lx, HighLimit: 0x%lx", *LowLimit, *HighLimit);
+	IOEXIT(return);
+}
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/pe_linker.c linux-5.6.11-ndis/3rdparty/ndiswrapper/pe_linker.c
--- linux-5.6.11/3rdparty/ndiswrapper/pe_linker.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/pe_linker.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,600 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#ifdef TEST_LOADER
+
+#include "usr_linker.h"
+
+#else
+
+#include <linux/types.h>
+#include <asm/errno.h>
+
+//#define DEBUGLINKER 2
+
+#include "ntoskernel.h"
+
+#endif
+
+struct pe_exports {
+	char *dll;
+	char *name;
+	generic_func addr;
+};
+
+static struct pe_exports pe_exports[40];
+static int num_pe_exports;
+
+#define RVA2VA(image, rva, type) (type)(ULONG_PTR)((void *)image + rva)
+#define CHECK_SZ(a,b) { if (sizeof(a) != b) {				\
+			ERROR("%s is bad, got %zd, expected %d",	\
+			      #a , sizeof(a), (b)); return -EINVAL; } }
+
+#if defined(DEBUGLINKER) && DEBUGLINKER > 0
+#define DBGLINKER(fmt, ...) printk(KERN_INFO "%s (%s:%d): " fmt "\n",	\
+				   DRIVER_NAME, __func__,		\
+				   __LINE__ , ## __VA_ARGS__);
+static const char *image_directory_name[] = {
+	"EXPORT",
+	"IMPORT",
+	"RESOURCE",
+	"EXCEPTION",
+	"SECURITY",
+	"BASERELOC",
+	"DEBUG",
+	"COPYRIGHT",
+	"GLOBALPTR",
+	"TLS",
+	"LOAD_CONFIG",
+	"BOUND_IMPORT",
+	"IAT",
+	"DELAY_IMPORT",
+	"COM_DESCRIPTOR" };
+#else
+#define DBGLINKER(fmt, ...) do { } while (0)
+#endif
+
+#ifndef TEST_LOADER
+extern struct wrap_export ntoskernel_exports[], ntoskernel_io_exports[],
+	ndis_exports[], crt_exports[], hal_exports[], rtl_exports[];
+#ifdef ENABLE_USB
+extern struct wrap_export usb_exports[];
+#endif
+
+static int get_export(char *name, generic_func *func)
+{
+	int i, j;
+
+	struct wrap_export *exports[] = {
+		ntoskernel_exports,
+		ntoskernel_io_exports,
+		ndis_exports,
+		crt_exports,
+		hal_exports,
+		rtl_exports,
+#ifdef ENABLE_USB
+		usb_exports,
+#endif
+	};
+
+	for (j = 0; j < ARRAY_SIZE(exports); j++)
+		for (i = 0; exports[j][i].name != NULL; i++)
+			if (strcmp(exports[j][i].name, name) == 0) {
+				*func = exports[j][i].func;
+				return 0;
+			}
+
+	for (i = 0; i < num_pe_exports; i++)
+		if (strcmp(pe_exports[i].name, name) == 0) {
+			*func = pe_exports[i].addr;
+			return 0;
+		}
+
+	return -1;
+}
+#endif // TEST_LOADER
+
+static void *get_dll_init(char *name)
+{
+	int i;
+	for (i = 0; i < num_pe_exports; i++)
+		if ((strcmp(pe_exports[i].dll, name) == 0) &&
+		    (strcmp(pe_exports[i].name, "DllInitialize") == 0))
+			return (void *)pe_exports[i].addr;
+	return NULL;
+}
+
+/*
+ * Find and validate the coff header
+ *
+ */
+static int check_nt_hdr(IMAGE_NT_HEADERS *nt_hdr)
+{
+	int i;
+	WORD attr;
+	PIMAGE_OPTIONAL_HEADER opt_hdr;
+
+	/* Validate the "PE\0\0" signature */
+	if (nt_hdr->Signature != IMAGE_NT_SIGNATURE) {
+		ERROR("is this driver file? bad signature %08x",
+		      nt_hdr->Signature);
+		return -EINVAL;
+	}
+
+	opt_hdr = &nt_hdr->OptionalHeader;
+	/* Make sure Image is PE32 or PE32+ */
+#ifdef CONFIG_X86_64
+	if (opt_hdr->Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+		ERROR("kernel is 64-bit, but Windows driver is not 64-bit;"
+		      "bad magic: %04X", opt_hdr->Magic);
+		return -EINVAL;
+	}
+#else
+	if (opt_hdr->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+		ERROR("kernel is 32-bit, but Windows driver is not 32-bit;"
+		      "bad magic: %04X", opt_hdr->Magic);
+		return -EINVAL;
+	}
+#endif
+
+	/* Validate the image for the current architecture. */
+#ifdef CONFIG_X86_64
+	if (nt_hdr->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) {
+		ERROR("kernel is 64-bit, but Windows driver is not 64-bit;"
+		      " (PE signature is %04X)", nt_hdr->FileHeader.Machine);
+		return -EINVAL;
+	}
+#else
+	if (nt_hdr->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) {
+		ERROR("kernel is 32-bit, but Windows driver is not 32-bit;"
+		      " (PE signature is %04X)", nt_hdr->FileHeader.Machine);
+		return -EINVAL;
+	}
+#endif
+
+	/* Must have attributes */
+#ifdef CONFIG_X86_64
+	attr = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_LARGE_ADDRESS_AWARE;
+#else
+	attr = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_32BIT_MACHINE;
+#endif
+	if ((nt_hdr->FileHeader.Characteristics & attr) != attr)
+		return -EINVAL;
+
+	/* Must be relocatable */
+	attr = IMAGE_FILE_RELOCS_STRIPPED;
+	if ((nt_hdr->FileHeader.Characteristics & attr))
+		return -EINVAL;
+
+	/* Make sure we have at least one section */
+	if (nt_hdr->FileHeader.NumberOfSections == 0)
+		return -EINVAL;
+
+	if (opt_hdr->SectionAlignment < opt_hdr->FileAlignment) {
+		ERROR("alignment mismatch: section: 0x%x, file: 0x%x",
+		      opt_hdr->SectionAlignment, opt_hdr->FileAlignment);
+		return -EINVAL;
+	}
+
+	DBGLINKER("number of datadictionary entries %d",
+		  opt_hdr->NumberOfRvaAndSizes);
+	for (i = 0; i < opt_hdr->NumberOfRvaAndSizes; i++) {
+		DBGLINKER("datadirectory %s RVA:%X Size:%d",
+			  (i <= IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR) ?
+			  image_directory_name[i] : "unknown",
+			  opt_hdr->DataDirectory[i].VirtualAddress,
+			  opt_hdr->DataDirectory[i].Size);
+	}
+
+	if ((nt_hdr->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE))
+		return IMAGE_FILE_EXECUTABLE_IMAGE;
+	if ((nt_hdr->FileHeader.Characteristics & IMAGE_FILE_DLL))
+		return IMAGE_FILE_DLL;
+	return -EINVAL;
+}
+
+static int import(void *image, IMAGE_IMPORT_DESCRIPTOR *dirent, char *dll)
+{
+	ULONG_PTR *lookup_tbl, *address_tbl;
+	char *symname = NULL;
+	int i;
+	int ret = 0;
+	generic_func adr;
+
+	lookup_tbl = RVA2VA(image, dirent->u.OriginalFirstThunk, ULONG_PTR *);
+	address_tbl = RVA2VA(image, dirent->FirstThunk, ULONG_PTR *);
+
+	for (i = 0; lookup_tbl[i]; i++) {
+		if (IMAGE_SNAP_BY_ORDINAL(lookup_tbl[i])) {
+			ERROR("ordinal import not supported: %llu",
+			      (uint64_t)lookup_tbl[i]);
+			return -1;
+		}
+		else {
+			symname = RVA2VA(image,
+					 ((lookup_tbl[i] &
+					   ~IMAGE_ORDINAL_FLAG) + 2), char *);
+		}
+
+		if (get_export(symname, &adr) < 0) {
+			ERROR("unknown symbol: %s:'%s'", dll, symname);
+			ret = -1;
+		} else {
+			DBGLINKER("found symbol: %s:%s: addr: %p, rva = %llu",
+				  dll, symname, adr, (uint64_t)address_tbl[i]);
+			address_tbl[i] = (ULONG_PTR)adr;
+		}
+	}
+	return ret;
+}
+
+static int read_exports(struct pe_image *pe)
+{
+	IMAGE_EXPORT_DIRECTORY *export_dir_table;
+	uint32_t *export_addr_table;
+	int i;
+	uint32_t *name_table;
+	PIMAGE_OPTIONAL_HEADER opt_hdr;
+	IMAGE_DATA_DIRECTORY *export_data_dir;
+
+	opt_hdr = &pe->nt_hdr->OptionalHeader;
+	export_data_dir =
+		&opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
+
+	if (export_data_dir->Size == 0) {
+		DBGLINKER("no exports");
+		return 0;
+	}
+
+	export_dir_table =
+		RVA2VA(pe->image, export_data_dir->VirtualAddress,
+		       IMAGE_EXPORT_DIRECTORY *);
+
+	name_table = (unsigned int *)(pe->image +
+				      export_dir_table->AddressOfNames);
+	export_addr_table = (uint32_t *)
+		(pe->image + export_dir_table->AddressOfFunctions);
+
+	for (i = 0; i < export_dir_table->NumberOfNames; i++) {
+
+		if (export_data_dir->VirtualAddress <= *export_addr_table ||
+		    *export_addr_table >= (export_data_dir->VirtualAddress +
+					   export_data_dir->Size))
+			DBGLINKER("forwarder rva");
+
+		DBGLINKER("export symbol: %s, at %p",
+			  (char *)(pe->image + *name_table),
+			  pe->image + *export_addr_table);
+
+		pe_exports[num_pe_exports].dll = pe->name;
+		pe_exports[num_pe_exports].name = pe->image + *name_table;
+		pe_exports[num_pe_exports].addr =
+			pe->image + *export_addr_table;
+
+		num_pe_exports++;
+		name_table++;
+		export_addr_table++;
+	}
+	return 0;
+}
+
+static int fixup_imports(void *image, IMAGE_NT_HEADERS *nt_hdr)
+{
+	int i;
+	char *name;
+	int ret = 0;
+	IMAGE_IMPORT_DESCRIPTOR *dirent;
+	IMAGE_DATA_DIRECTORY *import_data_dir;
+	PIMAGE_OPTIONAL_HEADER opt_hdr;
+
+	opt_hdr = &nt_hdr->OptionalHeader;
+	import_data_dir =
+		&opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
+	dirent = RVA2VA(image, import_data_dir->VirtualAddress,
+			IMAGE_IMPORT_DESCRIPTOR *);
+
+	for (i = 0; dirent[i].Name; i++) {
+		name = RVA2VA(image, dirent[i].Name, char*);
+
+		DBGLINKER("imports from dll: %s", name);
+		ret += import(image, &dirent[i], name);
+	}
+	return ret;
+}
+
+static int fixup_reloc(void *image, IMAGE_NT_HEADERS *nt_hdr)
+{
+	ULONG_PTR base;
+	ULONG_PTR size;
+	IMAGE_BASE_RELOCATION *fixup_block;
+	IMAGE_DATA_DIRECTORY *base_reloc_data_dir;
+	PIMAGE_OPTIONAL_HEADER opt_hdr;
+
+	opt_hdr = &nt_hdr->OptionalHeader;
+	base = opt_hdr->ImageBase;
+	base_reloc_data_dir =
+		&opt_hdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
+	if (base_reloc_data_dir->Size == 0)
+		return 0;
+
+	fixup_block = RVA2VA(image, base_reloc_data_dir->VirtualAddress,
+			     IMAGE_BASE_RELOCATION *);
+	DBGLINKER("fixup_block=%p, image=%p", fixup_block, image);
+	DBGLINKER("fixup_block info: %x %d",
+		  fixup_block->VirtualAddress, fixup_block->SizeOfBlock);
+
+	while (fixup_block->SizeOfBlock) {
+		int i;
+		WORD fixup, offset;
+
+		size = (fixup_block->SizeOfBlock -
+			sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
+		DBGLINKER("found %llu relocations in this block",
+			  (uint64_t)size);
+
+		for (i = 0; i < size; i++) {
+			fixup = fixup_block->TypeOffset[i];
+			offset = fixup & 0xfff;
+			switch ((fixup >> 12) & 0x0f) {
+			case IMAGE_REL_BASED_ABSOLUTE:
+				break;
+
+			case IMAGE_REL_BASED_HIGHLOW: {
+				uint32_t addr;
+				uint32_t *loc =
+					RVA2VA(image,
+					       fixup_block->VirtualAddress +
+					       offset, uint32_t *);
+				addr = RVA2VA(image, (*loc - base), uint32_t);
+				DBGLINKER("relocation: *%p (Val:%X)= %X",
+					  loc, *loc, addr);
+				*loc = addr;
+			}
+				break;
+
+			case IMAGE_REL_BASED_DIR64: {
+				uint64_t addr;
+				uint64_t *loc =
+					RVA2VA(image,
+					       fixup_block->VirtualAddress +
+					       offset, uint64_t *);
+				addr = RVA2VA(image, (*loc - base), uint64_t);
+				DBGLINKER("relocation: *%p (Val:%llX)= %llx",
+					  loc, *loc, addr);
+				*loc = addr;
+			}
+				break;
+
+			default:
+				ERROR("unknown fixup: %08X",
+				      (fixup >> 12) & 0x0f);
+				return -EOPNOTSUPP;
+				break;
+			}
+		}
+		DBGLINKER("finished relocating block");
+
+		fixup_block = (IMAGE_BASE_RELOCATION *)
+			((void *)fixup_block + fixup_block->SizeOfBlock);
+	};
+	DBGLINKER("done relocating all");
+
+	return 0;
+}
+
+/* Expand the image in memory if necessary. The image on disk does not
+ * necessarily maps the image of the driver in memory, so we have to
+ * re-write it in order to fulfill the sections alignments. The
+ * advantage to do that is that rva_to_va becomes a simple
+ * addition. */
+static int fix_pe_image(struct pe_image *pe)
+{
+	void *image;
+	IMAGE_SECTION_HEADER *sect_hdr;
+	int i, sections;
+	int image_size;
+
+	if (pe->size == pe->opt_hdr->SizeOfImage) {
+		/* Nothing to do */
+		return 0;
+	}
+
+	image_size = pe->opt_hdr->SizeOfImage;
+#ifdef CONFIG_X86_64
+#ifdef PAGE_KERNEL_EXECUTABLE
+	image = __vmalloc(image_size, GFP_KERNEL | __GFP_HIGHMEM
+			  );
+#elif defined PAGE_KERNEL_EXEC
+	image = __vmalloc(image_size, GFP_KERNEL | __GFP_HIGHMEM
+			  );
+#else
+#error x86_64 should have either PAGE_KERNEL_EXECUTABLE or PAGE_KERNEL_EXEC
+#endif
+#else
+#ifdef cpu_has_nx
+	/* hate to play with kernel macros, but PAGE_KERNEL_EXEC is
+	 * not available to modules! */
+	if (cpu_has_nx)
+		image = __vmalloc(image_size, GFP_KERNEL | __GFP_HIGHMEM
+				  );
+	else
+		image = vmalloc(image_size);
+#else
+		image = vmalloc(image_size);
+#endif
+#endif
+	if (image == NULL) {
+		ERROR("failed to allocate enough space for new image:"
+		      " %d bytes", image_size);
+		return -ENOMEM;
+	}
+	memset(image, 0, image_size);
+
+	/* Copy all the headers, ie everything before the first section. */
+
+	sections = pe->nt_hdr->FileHeader.NumberOfSections;
+	sect_hdr = IMAGE_FIRST_SECTION(pe->nt_hdr);
+
+	DBGLINKER("copying headers: %u bytes", sect_hdr->PointerToRawData);
+
+	memcpy(image, pe->image, sect_hdr->PointerToRawData);
+
+	/* Copy all the sections */
+	for (i = 0; i < sections; i++) {
+		DBGLINKER("Copy section %s from %x to %x",
+			  sect_hdr->Name, sect_hdr->PointerToRawData,
+			  sect_hdr->VirtualAddress);
+		if (sect_hdr->VirtualAddress+sect_hdr->SizeOfRawData >
+		    image_size) {
+			ERROR("Invalid section %s in driver", sect_hdr->Name);
+			vfree(image);
+			return -EINVAL;
+		}
+
+		memcpy(image+sect_hdr->VirtualAddress,
+		       pe->image + sect_hdr->PointerToRawData,
+		       sect_hdr->SizeOfRawData);
+		sect_hdr++;
+	}
+
+	vfree(pe->image);
+	pe->image = image;
+	pe->size = image_size;
+
+	/* Update our internal pointers */
+	pe->nt_hdr = (IMAGE_NT_HEADERS *)
+		(pe->image + ((IMAGE_DOS_HEADER *)pe->image)->e_lfanew);
+	pe->opt_hdr = &pe->nt_hdr->OptionalHeader;
+
+	DBGLINKER("set nt headers: nt_hdr=%p, opt_hdr=%p, image=%p",
+		  pe->nt_hdr, pe->opt_hdr, pe->image);
+
+	return 0;
+}
+
+#if defined(CONFIG_X86_64)
+static void fix_user_shared_data_addr(char *driver, unsigned long length)
+{
+	unsigned long i, n, max_addr, *addr;
+
+	TRACE1("fixing KI_USER_SHARED_DATA address in the driver");
+	n = length - sizeof(unsigned long);
+	max_addr = KI_USER_SHARED_DATA + sizeof(kuser_shared_data);
+	for (i = 0; i < n; i++) {
+		addr = (unsigned long *)(driver + i);
+		if (*addr >= KI_USER_SHARED_DATA && *addr < max_addr) {
+			*addr -= KI_USER_SHARED_DATA;
+			*addr += (unsigned long)&kuser_shared_data;
+			kuser_shared_data.reserved1 = 1;
+		}
+	}
+}
+#endif
+
+int link_pe_images(struct pe_image *pe_image, unsigned short n)
+{
+	int i;
+	struct pe_image *pe;
+
+#if DEBUG >= 1
+	/* Sanity checks */
+	CHECK_SZ(IMAGE_SECTION_HEADER, IMAGE_SIZEOF_SECTION_HEADER);
+	CHECK_SZ(IMAGE_FILE_HEADER, IMAGE_SIZEOF_FILE_HEADER);
+	CHECK_SZ(IMAGE_OPTIONAL_HEADER, IMAGE_SIZEOF_NT_OPTIONAL_HEADER);
+	CHECK_SZ(IMAGE_NT_HEADERS, 4 + IMAGE_SIZEOF_FILE_HEADER +
+		 IMAGE_SIZEOF_NT_OPTIONAL_HEADER);
+	CHECK_SZ(IMAGE_DOS_HEADER, 0x40);
+	CHECK_SZ(IMAGE_EXPORT_DIRECTORY, 40);
+	CHECK_SZ(IMAGE_BASE_RELOCATION, 8);
+	CHECK_SZ(IMAGE_IMPORT_DESCRIPTOR, 20);
+#endif
+
+	for (i = 0; i < n; i++) {
+		IMAGE_DOS_HEADER *dos_hdr;
+		pe = &pe_image[i];
+		dos_hdr = pe->image;
+
+		if (pe->size < sizeof(IMAGE_DOS_HEADER)) {
+			TRACE1("image too small: %d", pe->size);
+			return -EINVAL;
+		}
+
+		pe->nt_hdr =
+			(IMAGE_NT_HEADERS *)(pe->image + dos_hdr->e_lfanew);
+		pe->opt_hdr = &pe->nt_hdr->OptionalHeader;
+
+		pe->type = check_nt_hdr(pe->nt_hdr);
+		if (pe->type <= 0) {
+			TRACE1("type <= 0");
+			return -EINVAL;
+		}
+
+		if (fix_pe_image(pe)) {
+			TRACE1("bad PE image");
+			return -EINVAL;
+		}
+
+		if (read_exports(pe)) {
+			TRACE1("read exports failed");
+			return -EINVAL;
+		}
+	}
+
+	for (i = 0; i < n; i++) {
+		pe = &pe_image[i];
+
+		if (fixup_reloc(pe->image, pe->nt_hdr)) {
+			TRACE1("fixup reloc failed");
+			return -EINVAL;
+		}
+		if (fixup_imports(pe->image, pe->nt_hdr)) {
+			TRACE1("fixup imports failed");
+			return -EINVAL;
+		}
+#if defined(CONFIG_X86_64)
+		fix_user_shared_data_addr(pe_image[i].image, pe_image[i].size);
+#endif
+		flush_icache_range((unsigned long)pe->image, pe->size);
+
+		pe->entry =
+			RVA2VA(pe->image,
+			       pe->opt_hdr->AddressOfEntryPoint, void *);
+		TRACE1("entry is at %p, rva at %08X", pe->entry,
+		       pe->opt_hdr->AddressOfEntryPoint);
+	}
+
+	for (i = 0; i < n; i++) {
+		pe = &pe_image[i];
+
+		if (pe->type == IMAGE_FILE_DLL) {
+			struct unicode_string ustring;
+			char *buf = "0/0t0m0p00";
+			int (*dll_entry)(struct unicode_string *ustring)
+				wstdcall;
+
+			memset(&ustring, 0, sizeof(ustring));
+			ustring.buf = (wchar_t *)buf;
+			dll_entry = (void *)get_dll_init(pe->name);
+
+			TRACE1("calling dll_init at %p", dll_entry);
+			if (!dll_entry || dll_entry(&ustring))
+				ERROR("DLL initialize failed for %s",
+				      pe->name);
+		}
+		else if (pe->type != IMAGE_FILE_EXECUTABLE_IMAGE)
+			ERROR("illegal image type: %d", pe->type);
+	}
+	return 0;
+}
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/pe_linker.h linux-5.6.11-ndis/3rdparty/ndiswrapper/pe_linker.h
--- linux-5.6.11/3rdparty/ndiswrapper/pe_linker.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/pe_linker.h	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,992 @@
+/*
+ * This file is an excerpt of winnt.h from WINE, which bears the
+ * following copyright:
+ *
+ * Win32 definitions for Windows NT
+ *
+ * Copyright 1996 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+/*
+ * File formats definitions
+ */
+typedef struct _IMAGE_DOS_HEADER {
+    WORD  e_magic;      /* 00: MZ Header signature */
+    WORD  e_cblp;       /* 02: Bytes on last page of file */
+    WORD  e_cp;         /* 04: Pages in file */
+    WORD  e_crlc;       /* 06: Relocations */
+    WORD  e_cparhdr;    /* 08: Size of header in paragraphs */
+    WORD  e_minalloc;   /* 0a: Minimum extra paragraphs needed */
+    WORD  e_maxalloc;   /* 0c: Maximum extra paragraphs needed */
+    WORD  e_ss;         /* 0e: Initial (relative) SS value */
+    WORD  e_sp;         /* 10: Initial SP value */
+    WORD  e_csum;       /* 12: Checksum */
+    WORD  e_ip;         /* 14: Initial IP value */
+    WORD  e_cs;         /* 16: Initial (relative) CS value */
+    WORD  e_lfarlc;     /* 18: File address of relocation table */
+    WORD  e_ovno;       /* 1a: Overlay number */
+    WORD  e_res[4];     /* 1c: Reserved words */
+    WORD  e_oemid;      /* 24: OEM identifier (for e_oeminfo) */
+    WORD  e_oeminfo;    /* 26: OEM information; e_oemid specific */
+    WORD  e_res2[10];   /* 28: Reserved words */
+    DWORD e_lfanew;     /* 3c: Offset to extended header */
+} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
+
+#define IMAGE_DOS_SIGNATURE    0x5A4D     /* MZ   */
+#define IMAGE_OS2_SIGNATURE    0x454E     /* NE   */
+#define IMAGE_OS2_SIGNATURE_LE 0x454C     /* LE   */
+#define IMAGE_OS2_SIGNATURE_LX 0x584C     /* LX */
+#define IMAGE_VXD_SIGNATURE    0x454C     /* LE   */
+#define IMAGE_NT_SIGNATURE     0x00004550 /* PE00 */
+
+/*
+ * This is the Windows executable (NE) header.
+ * the name IMAGE_OS2_HEADER is misleading, but in the SDK this way.
+ */
+typedef struct
+{
+    WORD  ne_magic;             /* 00 NE signature 'NE' */
+    BYTE  ne_ver;               /* 02 Linker version number */
+    BYTE  ne_rev;               /* 03 Linker revision number */
+    WORD  ne_enttab;            /* 04 Offset to entry table relative to NE */
+    WORD  ne_cbenttab;          /* 06 Length of entry table in bytes */
+    LONG  ne_crc;               /* 08 Checksum */
+    WORD  ne_flags;             /* 0c Flags about segments in this file */
+    WORD  ne_autodata;          /* 0e Automatic data segment number */
+    WORD  ne_heap;              /* 10 Initial size of local heap */
+    WORD  ne_stack;             /* 12 Initial size of stack */
+    DWORD ne_csip;              /* 14 Initial CS:IP */
+    DWORD ne_sssp;              /* 18 Initial SS:SP */
+    WORD  ne_cseg;              /* 1c # of entries in segment table */
+    WORD  ne_cmod;              /* 1e # of entries in module reference tab. */
+    WORD  ne_cbnrestab;         /* 20 Length of nonresident-name table */
+    WORD  ne_segtab;            /* 22 Offset to segment table */
+    WORD  ne_rsrctab;           /* 24 Offset to resource table */
+    WORD  ne_restab;            /* 26 Offset to resident-name table */
+    WORD  ne_modtab;            /* 28 Offset to module reference table */
+    WORD  ne_imptab;            /* 2a Offset to imported name table */
+    DWORD ne_nrestab;           /* 2c Offset to nonresident-name table */
+    WORD  ne_cmovent;           /* 30 # of movable entry points */
+    WORD  ne_align;             /* 32 Logical sector alignment shift count */
+    WORD  ne_cres;              /* 34 # of resource segments */
+    BYTE  ne_exetyp;            /* 36 Flags indicating target OS */
+    BYTE  ne_flagsothers;       /* 37 Additional information flags */
+    WORD  ne_pretthunks;        /* 38 Offset to return thunks */
+    WORD  ne_psegrefbytes;      /* 3a Offset to segment ref. bytes */
+    WORD  ne_swaparea;          /* 3c Reserved by Microsoft */
+    WORD  ne_expver;            /* 3e Expected Windows version number */
+} IMAGE_OS2_HEADER, *PIMAGE_OS2_HEADER;
+
+typedef struct _IMAGE_VXD_HEADER {
+  WORD  e32_magic;
+  BYTE  e32_border;
+  BYTE  e32_worder;
+  DWORD e32_level;
+  WORD  e32_cpu;
+  WORD  e32_os;
+  DWORD e32_ver;
+  DWORD e32_mflags;
+  DWORD e32_mpages;
+  DWORD e32_startobj;
+  DWORD e32_eip;
+  DWORD e32_stackobj;
+  DWORD e32_esp;
+  DWORD e32_pagesize;
+  DWORD e32_lastpagesize;
+  DWORD e32_fixupsize;
+  DWORD e32_fixupsum;
+  DWORD e32_ldrsize;
+  DWORD e32_ldrsum;
+  DWORD e32_objtab;
+  DWORD e32_objcnt;
+  DWORD e32_objmap;
+  DWORD e32_itermap;
+  DWORD e32_rsrctab;
+  DWORD e32_rsrccnt;
+  DWORD e32_restab;
+  DWORD e32_enttab;
+  DWORD e32_dirtab;
+  DWORD e32_dircnt;
+  DWORD e32_fpagetab;
+  DWORD e32_frectab;
+  DWORD e32_impmod;
+  DWORD e32_impmodcnt;
+  DWORD e32_impproc;
+  DWORD e32_pagesum;
+  DWORD e32_datapage;
+  DWORD e32_preload;
+  DWORD e32_nrestab;
+  DWORD e32_cbnrestab;
+  DWORD e32_nressum;
+  DWORD e32_autodata;
+  DWORD e32_debuginfo;
+  DWORD e32_debuglen;
+  DWORD e32_instpreload;
+  DWORD e32_instdemand;
+  DWORD e32_heapsize;
+  BYTE  e32_res3[12];
+  DWORD e32_winresoff;
+  DWORD e32_winreslen;
+  WORD  e32_devid;
+  WORD  e32_ddkver;
+} IMAGE_VXD_HEADER, *PIMAGE_VXD_HEADER;
+
+/* These defines describe the meanings of the bits in the
+   Characteristics field */
+
+#define IMAGE_FILE_RELOCS_STRIPPED	0x0001 /* No relocation info */
+#define IMAGE_FILE_EXECUTABLE_IMAGE	0x0002
+#define IMAGE_FILE_LINE_NUMS_STRIPPED   0x0004
+#define IMAGE_FILE_LOCAL_SYMS_STRIPPED  0x0008
+#define IMAGE_FILE_AGGRESIVE_WS_TRIM	0x0010
+#define IMAGE_FILE_LARGE_ADDRESS_AWARE	0x0020
+#define IMAGE_FILE_16BIT_MACHINE	0x0040
+#define IMAGE_FILE_BYTES_REVERSED_LO	0x0080
+#define IMAGE_FILE_32BIT_MACHINE	0x0100
+#define IMAGE_FILE_DEBUG_STRIPPED	0x0200
+#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP	0x0400
+#define IMAGE_FILE_NET_RUN_FROM_SWAP	0x0800
+#define IMAGE_FILE_SYSTEM		0x1000
+#define IMAGE_FILE_DLL			0x2000
+#define IMAGE_FILE_UP_SYSTEM_ONLY	0x4000
+#define IMAGE_FILE_BYTES_REVERSED_HI	0x8000
+
+/* These are the settings of the Machine field. */
+#define	IMAGE_FILE_MACHINE_UNKNOWN	0
+#define	IMAGE_FILE_MACHINE_I860		0x014d
+#define	IMAGE_FILE_MACHINE_I386		0x014c
+#define	IMAGE_FILE_MACHINE_R3000	0x0162
+#define	IMAGE_FILE_MACHINE_R4000	0x0166
+#define	IMAGE_FILE_MACHINE_R10000	0x0168
+#define	IMAGE_FILE_MACHINE_WCEMIPSV2	0x0169
+#define	IMAGE_FILE_MACHINE_ALPHA	0x0184
+#define	IMAGE_FILE_MACHINE_SH3		0x01a2
+#define	IMAGE_FILE_MACHINE_SH3DSP	0x01a3
+#define	IMAGE_FILE_MACHINE_SH3E		0x01a4
+#define	IMAGE_FILE_MACHINE_SH4		0x01a6
+#define	IMAGE_FILE_MACHINE_SH5		0x01a8
+#define	IMAGE_FILE_MACHINE_ARM		0x01c0
+#define	IMAGE_FILE_MACHINE_THUMB	0x01c2
+#define	IMAGE_FILE_MACHINE_AM33		0x01d3
+#define	IMAGE_FILE_MACHINE_POWERPC	0x01f0
+#define	IMAGE_FILE_MACHINE_POWERPCFP	0x01f1
+#define	IMAGE_FILE_MACHINE_IA64		0x0200
+#define	IMAGE_FILE_MACHINE_MIPS16	0x0266
+#define	IMAGE_FILE_MACHINE_ALPHA64	0x0284
+#define	IMAGE_FILE_MACHINE_MIPSFPU	0x0366
+#define	IMAGE_FILE_MACHINE_MIPSFPU16	0x0466
+#define	IMAGE_FILE_MACHINE_AXP64	IMAGE_FILE_MACHINE_ALPHA64
+#define	IMAGE_FILE_MACHINE_TRICORE	0x0520
+#define	IMAGE_FILE_MACHINE_CEF		0x0cef
+#define	IMAGE_FILE_MACHINE_EBC		0x0ebc
+#define	IMAGE_FILE_MACHINE_AMD64	0x8664
+#define	IMAGE_FILE_MACHINE_M32R		0x9041
+#define	IMAGE_FILE_MACHINE_CEE		0xc0ee
+
+#define	IMAGE_SIZEOF_FILE_HEADER		20
+#define IMAGE_SIZEOF_ROM_OPTIONAL_HEADER	56
+#define IMAGE_SIZEOF_STD_OPTIONAL_HEADER	28
+#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER32	224
+#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER64	240
+#define IMAGE_SIZEOF_SHORT_NAME			8
+#define IMAGE_SIZEOF_SECTION_HEADER		40
+#define IMAGE_SIZEOF_SYMBOL			18
+#define IMAGE_SIZEOF_AUX_SYMBOL			18
+#define IMAGE_SIZEOF_RELOCATION			10
+#define IMAGE_SIZEOF_BASE_RELOCATION		8
+#define IMAGE_SIZEOF_LINENUMBER			6
+#define IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR		60
+
+/* Possible Magic values */
+#define IMAGE_NT_OPTIONAL_HDR32_MAGIC        0x010b
+#define IMAGE_NT_OPTIONAL_HDR64_MAGIC        0x020b
+#define IMAGE_ROM_OPTIONAL_HDR_MAGIC       0x0107
+
+#ifdef CONFIG_X86_64
+#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL_HEADER64
+#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR64_MAGIC
+#else
+#define IMAGE_SIZEOF_NT_OPTIONAL_HEADER IMAGE_SIZEOF_NT_OPTIONAL_HEADER32
+#define IMAGE_NT_OPTIONAL_HDR_MAGIC IMAGE_NT_OPTIONAL_HDR32_MAGIC
+#endif
+
+/* These are indexes into the DataDirectory array */
+#define IMAGE_FILE_EXPORT_DIRECTORY		0
+#define IMAGE_FILE_IMPORT_DIRECTORY		1
+#define IMAGE_FILE_RESOURCE_DIRECTORY		2
+#define IMAGE_FILE_EXCEPTION_DIRECTORY		3
+#define IMAGE_FILE_SECURITY_DIRECTORY		4
+#define IMAGE_FILE_BASE_RELOCATION_TABLE	5
+#define IMAGE_FILE_DEBUG_DIRECTORY		6
+#define IMAGE_FILE_DESCRIPTION_STRING		7
+#define IMAGE_FILE_MACHINE_VALUE		8  /* Mips */
+#define IMAGE_FILE_THREAD_LOCAL_STORAGE		9
+#define IMAGE_FILE_CALLBACK_DIRECTORY		10
+
+/* Directory Entries, indices into the DataDirectory array */
+
+#define	IMAGE_DIRECTORY_ENTRY_EXPORT		0
+#define	IMAGE_DIRECTORY_ENTRY_IMPORT		1
+#define	IMAGE_DIRECTORY_ENTRY_RESOURCE		2
+#define	IMAGE_DIRECTORY_ENTRY_EXCEPTION		3
+#define	IMAGE_DIRECTORY_ENTRY_SECURITY		4
+#define	IMAGE_DIRECTORY_ENTRY_BASERELOC		5
+#define	IMAGE_DIRECTORY_ENTRY_DEBUG		6
+#define	IMAGE_DIRECTORY_ENTRY_COPYRIGHT		7
+#define	IMAGE_DIRECTORY_ENTRY_GLOBALPTR		8  /* (MIPS GP) */
+#define	IMAGE_DIRECTORY_ENTRY_TLS		9
+#define	IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG	10
+#define	IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT	11
+#define	IMAGE_DIRECTORY_ENTRY_IAT		12  /* Import Address Table */
+#define	IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT	13
+#define	IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR	14
+
+/* Subsystem Values */
+
+#define	IMAGE_SUBSYSTEM_UNKNOWN			0
+#define	IMAGE_SUBSYSTEM_NATIVE			1
+#define	IMAGE_SUBSYSTEM_WINDOWS_GUI		2  /* Windows GUI subsystem */
+#define	IMAGE_SUBSYSTEM_WINDOWS_CUI		3  /* Windows character subsystem */
+#define	IMAGE_SUBSYSTEM_OS2_CUI			5
+#define	IMAGE_SUBSYSTEM_POSIX_CUI		7
+#define	IMAGE_SUBSYSTEM_NATIVE_WINDOWS		8  /* native Win9x driver */
+#define	IMAGE_SUBSYSTEM_WINDOWS_CE_GUI		9  /* Windows CE subsystem */
+#define	IMAGE_SUBSYSTEM_EFI_APPLICATION		10
+#define	IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER	11
+#define	IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER	12
+#define	IMAGE_SUBSYSTEM_EFI_ROM			13
+#define	IMAGE_SUBSYSTEM_XBOX			14
+
+typedef struct _IMAGE_FILE_HEADER {
+  WORD  Machine;
+  WORD  NumberOfSections;
+  DWORD TimeDateStamp;
+  DWORD PointerToSymbolTable;
+  DWORD NumberOfSymbols;
+  WORD  SizeOfOptionalHeader;
+  WORD  Characteristics;
+} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
+
+typedef struct _IMAGE_DATA_DIRECTORY {
+  DWORD VirtualAddress;
+  DWORD Size;
+} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
+
+#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
+
+typedef struct _IMAGE_OPTIONAL_HEADER32 {
+
+  /* Standard fields */
+
+  WORD  Magic;
+  BYTE  MajorLinkerVersion;
+  BYTE  MinorLinkerVersion;
+  DWORD SizeOfCode;
+  DWORD SizeOfInitializedData;
+  DWORD SizeOfUninitializedData;
+  DWORD AddressOfEntryPoint;
+  DWORD BaseOfCode;
+  DWORD BaseOfData;
+
+  /* NT additional fields */
+  DWORD ImageBase;
+  DWORD SectionAlignment;
+  DWORD FileAlignment;
+  WORD  MajorOperatingSystemVersion;
+  WORD  MinorOperatingSystemVersion;
+  WORD  MajorImageVersion;
+  WORD  MinorImageVersion;
+  WORD  MajorSubsystemVersion;
+  WORD  MinorSubsystemVersion;
+  DWORD Win32VersionValue;
+  DWORD SizeOfImage;
+  DWORD SizeOfHeaders;
+  DWORD CheckSum;
+  WORD  Subsystem;
+  WORD  DllCharacteristics;
+  DWORD SizeOfStackReserve;
+  DWORD SizeOfStackCommit;
+  DWORD SizeOfHeapReserve;
+  DWORD SizeOfHeapCommit;
+  DWORD LoaderFlags;
+  DWORD NumberOfRvaAndSizes;
+  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
+
+typedef struct _IMAGE_OPTIONAL_HEADER64 {
+
+  /* Standard fields */
+
+  WORD  Magic;
+  BYTE  MajorLinkerVersion;
+  BYTE  MinorLinkerVersion;
+  DWORD SizeOfCode;
+  DWORD SizeOfInitializedData;
+  DWORD SizeOfUninitializedData;
+  DWORD AddressOfEntryPoint;
+  DWORD BaseOfCode;
+
+  /* NT additional fields */
+  ULONGLONG ImageBase;
+  DWORD SectionAlignment;
+  DWORD FileAlignment;
+  WORD  MajorOperatingSystemVersion;
+  WORD  MinorOperatingSystemVersion;
+  WORD  MajorImageVersion;
+  WORD  MinorImageVersion;
+  WORD  MajorSubsystemVersion;
+  WORD  MinorSubsystemVersion;
+  DWORD Win32VersionValue;
+  DWORD SizeOfImage;
+  DWORD SizeOfHeaders;
+  DWORD CheckSum;
+  WORD  Subsystem;
+  WORD  DllCharacteristics;
+  ULONGLONG SizeOfStackReserve;
+  ULONGLONG SizeOfStackCommit;
+  ULONGLONG SizeOfHeapReserve;
+  ULONGLONG SizeOfHeapCommit;
+  DWORD LoaderFlags;
+  DWORD NumberOfRvaAndSizes;
+  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
+
+#ifdef CONFIG_X86_64
+typedef IMAGE_OPTIONAL_HEADER64 IMAGE_OPTIONAL_HEADER;
+typedef PIMAGE_OPTIONAL_HEADER64 PIMAGE_OPTIONAL_HEADER;
+#else
+typedef IMAGE_OPTIONAL_HEADER32 IMAGE_OPTIONAL_HEADER;
+typedef PIMAGE_OPTIONAL_HEADER32 PIMAGE_OPTIONAL_HEADER;
+#endif
+
+typedef struct _IMAGE_NT_HEADERS32 {
+  DWORD Signature; /* "PE"\0\0 */	/* 0x00 */
+  IMAGE_FILE_HEADER FileHeader;		/* 0x04 */
+  IMAGE_OPTIONAL_HEADER32 OptionalHeader;	/* 0x18 */
+} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
+
+typedef struct _IMAGE_NT_HEADERS64 {
+  DWORD Signature; /* "PE"\0\0 */	/* 0x00 */
+  IMAGE_FILE_HEADER FileHeader;		/* 0x04 */
+  IMAGE_OPTIONAL_HEADER64 OptionalHeader;	/* 0x18 */
+} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
+
+#ifdef CONFIG_X86_64
+typedef IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS;
+typedef PIMAGE_NT_HEADERS64 PIMAGE_NT_HEADERS;
+#else
+typedef IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS;
+typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS;
+#endif
+
+#define IMAGE_SIZEOF_SHORT_NAME 8
+
+typedef struct _IMAGE_SECTION_HEADER {
+  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];
+  union {
+    DWORD PhysicalAddress;
+    DWORD VirtualSize;
+  } Misc;
+  DWORD VirtualAddress;
+  DWORD SizeOfRawData;
+  DWORD PointerToRawData;
+  DWORD PointerToRelocations;
+  DWORD PointerToLinenumbers;
+  WORD  NumberOfRelocations;
+  WORD  NumberOfLinenumbers;
+  DWORD Characteristics;
+} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
+
+#define	IMAGE_SIZEOF_SECTION_HEADER 40
+
+#define IMAGE_FIRST_SECTION(ntheader) \
+((PIMAGE_SECTION_HEADER)((LPBYTE)&((PIMAGE_NT_HEADERS)(ntheader))->OptionalHeader + \
+((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader))
+
+/* These defines are for the Characteristics bitfield. */
+/* #define IMAGE_SCN_TYPE_REG			0x00000000 - Reserved */
+/* #define IMAGE_SCN_TYPE_DSECT			0x00000001 - Reserved */
+/* #define IMAGE_SCN_TYPE_NOLOAD		0x00000002 - Reserved */
+/* #define IMAGE_SCN_TYPE_GROUP			0x00000004 - Reserved */
+#define IMAGE_SCN_TYPE_NO_PAD			0x00000008 /* Reserved */
+/* #define IMAGE_SCN_TYPE_COPY			0x00000010 - Reserved */
+
+#define IMAGE_SCN_CNT_CODE			0x00000020
+#define IMAGE_SCN_CNT_INITIALIZED_DATA		0x00000040
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA	0x00000080
+
+#define	IMAGE_SCN_LNK_OTHER			0x00000100
+#define	IMAGE_SCN_LNK_INFO			0x00000200
+/* #define IMAGE_SCN_TYPE_OVER		0x00000400 - Reserved */
+#define	IMAGE_SCN_LNK_REMOVE			0x00000800
+#define	IMAGE_SCN_LNK_COMDAT			0x00001000
+
+/*						0x00002000 - Reserved */
+/* #define IMAGE_SCN_MEM_PROTECTED		0x00004000 - Obsolete */
+#define	IMAGE_SCN_MEM_FARDATA			0x00008000
+
+/* #define IMAGE_SCN_MEM_SYSHEAP		0x00010000 - Obsolete */
+#define	IMAGE_SCN_MEM_PURGEABLE			0x00020000
+#define	IMAGE_SCN_MEM_16BIT			0x00020000
+#define	IMAGE_SCN_MEM_LOCKED			0x00040000
+#define	IMAGE_SCN_MEM_PRELOAD			0x00080000
+
+#define	IMAGE_SCN_ALIGN_1BYTES			0x00100000
+#define	IMAGE_SCN_ALIGN_2BYTES			0x00200000
+#define	IMAGE_SCN_ALIGN_4BYTES			0x00300000
+#define	IMAGE_SCN_ALIGN_8BYTES			0x00400000
+#define	IMAGE_SCN_ALIGN_16BYTES			0x00500000  /* Default */
+#define	IMAGE_SCN_ALIGN_32BYTES			0x00600000
+#define	IMAGE_SCN_ALIGN_64BYTES			0x00700000
+#define	IMAGE_SCN_ALIGN_128BYTES		0x00800000
+#define	IMAGE_SCN_ALIGN_256BYTES		0x00900000
+#define	IMAGE_SCN_ALIGN_512BYTES		0x00A00000
+#define	IMAGE_SCN_ALIGN_1024BYTES		0x00B00000
+#define	IMAGE_SCN_ALIGN_2048BYTES		0x00C00000
+#define	IMAGE_SCN_ALIGN_4096BYTES		0x00D00000
+#define	IMAGE_SCN_ALIGN_8192BYTES		0x00E00000
+/*						0x00F00000 - Unused */
+#define	IMAGE_SCN_ALIGN_MASK			0x00F00000
+
+#define IMAGE_SCN_LNK_NRELOC_OVFL		0x01000000
+
+
+#define IMAGE_SCN_MEM_DISCARDABLE		0x02000000
+#define IMAGE_SCN_MEM_NOT_CACHED		0x04000000
+#define IMAGE_SCN_MEM_NOT_PAGED			0x08000000
+#define IMAGE_SCN_MEM_SHARED			0x10000000
+#define IMAGE_SCN_MEM_EXECUTE			0x20000000
+#define IMAGE_SCN_MEM_READ			0x40000000
+#define IMAGE_SCN_MEM_WRITE			0x80000000
+
+typedef struct _IMAGE_SYMBOL {
+    union {
+        BYTE    ShortName[8];
+        struct {
+            DWORD   Short;
+            DWORD   Long;
+        } Name;
+        DWORD   LongName[2];
+    } N;
+    DWORD   Value;
+    SHORT   SectionNumber;
+    WORD    Type;
+    BYTE    StorageClass;
+    BYTE    NumberOfAuxSymbols;
+} IMAGE_SYMBOL;
+typedef IMAGE_SYMBOL *PIMAGE_SYMBOL;
+
+#define IMAGE_SIZEOF_SYMBOL 18
+
+typedef struct _IMAGE_LINENUMBER {
+    union {
+        DWORD   SymbolTableIndex;
+        DWORD   VirtualAddress;
+    } Type;
+    WORD    Linenumber;
+} IMAGE_LINENUMBER;
+typedef IMAGE_LINENUMBER *PIMAGE_LINENUMBER;
+
+#define IMAGE_SIZEOF_LINENUMBER  6
+
+typedef union _IMAGE_AUX_SYMBOL {
+    struct {
+        DWORD    TagIndex;
+        union {
+            struct {
+                WORD    Linenumber;
+                WORD    Size;
+            } LnSz;
+           DWORD    TotalSize;
+        } Misc;
+        union {
+            struct {
+                DWORD    PointerToLinenumber;
+                DWORD    PointerToNextFunction;
+            } Function;
+            struct {
+                WORD     Dimension[4];
+            } Array;
+        } FcnAry;
+        WORD    TvIndex;
+    } Sym;
+    struct {
+        BYTE    Name[IMAGE_SIZEOF_SYMBOL];
+    } File;
+    struct {
+        DWORD   Length;
+        WORD    NumberOfRelocations;
+        WORD    NumberOfLinenumbers;
+        DWORD   CheckSum;
+        SHORT   Number;
+        BYTE    Selection;
+    } Section;
+} IMAGE_AUX_SYMBOL;
+typedef IMAGE_AUX_SYMBOL *PIMAGE_AUX_SYMBOL;
+
+#define IMAGE_SIZEOF_AUX_SYMBOL 18
+
+#define IMAGE_SYM_UNDEFINED           (SHORT)0
+#define IMAGE_SYM_ABSOLUTE            (SHORT)-1
+#define IMAGE_SYM_DEBUG               (SHORT)-2
+
+#define IMAGE_SYM_TYPE_NULL                 0x0000
+#define IMAGE_SYM_TYPE_VOID                 0x0001
+#define IMAGE_SYM_TYPE_CHAR                 0x0002
+#define IMAGE_SYM_TYPE_SHORT                0x0003
+#define IMAGE_SYM_TYPE_INT                  0x0004
+#define IMAGE_SYM_TYPE_LONG                 0x0005
+#define IMAGE_SYM_TYPE_FLOAT                0x0006
+#define IMAGE_SYM_TYPE_DOUBLE               0x0007
+#define IMAGE_SYM_TYPE_STRUCT               0x0008
+#define IMAGE_SYM_TYPE_UNION                0x0009
+#define IMAGE_SYM_TYPE_ENUM                 0x000A
+#define IMAGE_SYM_TYPE_MOE                  0x000B
+#define IMAGE_SYM_TYPE_BYTE                 0x000C
+#define IMAGE_SYM_TYPE_WORD                 0x000D
+#define IMAGE_SYM_TYPE_UINT                 0x000E
+#define IMAGE_SYM_TYPE_DWORD                0x000F
+#define IMAGE_SYM_TYPE_PCODE                0x8000
+
+#define IMAGE_SYM_DTYPE_NULL                0
+#define IMAGE_SYM_DTYPE_POINTER             1
+#define IMAGE_SYM_DTYPE_FUNCTION            2
+#define IMAGE_SYM_DTYPE_ARRAY               3
+
+#define IMAGE_SYM_CLASS_END_OF_FUNCTION     (BYTE )-1
+#define IMAGE_SYM_CLASS_NULL                0x0000
+#define IMAGE_SYM_CLASS_AUTOMATIC           0x0001
+#define IMAGE_SYM_CLASS_EXTERNAL            0x0002
+#define IMAGE_SYM_CLASS_STATIC              0x0003
+#define IMAGE_SYM_CLASS_REGISTER            0x0004
+#define IMAGE_SYM_CLASS_EXTERNAL_DEF        0x0005
+#define IMAGE_SYM_CLASS_LABEL               0x0006
+#define IMAGE_SYM_CLASS_UNDEFINED_LABEL     0x0007
+#define IMAGE_SYM_CLASS_MEMBER_OF_STRUCT    0x0008
+#define IMAGE_SYM_CLASS_ARGUMENT            0x0009
+#define IMAGE_SYM_CLASS_STRUCT_TAG          0x000A
+#define IMAGE_SYM_CLASS_MEMBER_OF_UNION     0x000B
+#define IMAGE_SYM_CLASS_UNION_TAG           0x000C
+#define IMAGE_SYM_CLASS_TYPE_DEFINITION     0x000D
+#define IMAGE_SYM_CLASS_UNDEFINED_STATIC    0x000E
+#define IMAGE_SYM_CLASS_ENUM_TAG            0x000F
+#define IMAGE_SYM_CLASS_MEMBER_OF_ENUM      0x0010
+#define IMAGE_SYM_CLASS_REGISTER_PARAM      0x0011
+#define IMAGE_SYM_CLASS_BIT_FIELD           0x0012
+
+#define IMAGE_SYM_CLASS_FAR_EXTERNAL        0x0044
+#define IMAGE_SYM_CLASS_BLOCK               0x0064
+#define IMAGE_SYM_CLASS_FUNCTION            0x0065
+#define IMAGE_SYM_CLASS_END_OF_STRUCT       0x0066
+#define IMAGE_SYM_CLASS_FILE                0x0067
+#define IMAGE_SYM_CLASS_SECTION             0x0068
+#define IMAGE_SYM_CLASS_WEAK_EXTERNAL       0x0069
+
+#define N_BTMASK                            0x000F
+#define N_TMASK                             0x0030
+#define N_TMASK1                            0x00C0
+#define N_TMASK2                            0x00F0
+#define N_BTSHFT                            4
+#define N_TSHIFT                            2
+
+#define BTYPE(x) ((x) & N_BTMASK)
+
+#ifndef ISPTR
+#define ISPTR(x) (((x) & N_TMASK) == (IMAGE_SYM_DTYPE_POINTER << N_BTSHFT))
+#endif
+
+#ifndef ISFCN
+#define ISFCN(x) (((x) & N_TMASK) == (IMAGE_SYM_DTYPE_FUNCTION << N_BTSHFT))
+#endif
+
+#ifndef ISARY
+#define ISARY(x) (((x) & N_TMASK) == (IMAGE_SYM_DTYPE_ARRAY << N_BTSHFT))
+#endif
+
+#ifndef ISTAG
+#define ISTAG(x) ((x)==IMAGE_SYM_CLASS_STRUCT_TAG || (x)==IMAGE_SYM_CLASS_UNION_TAG || (x)==IMAGE_SYM_CLASS_ENUM_TAG)
+#endif
+
+#ifndef INCREF
+#define INCREF(x) ((((x)&~N_BTMASK)<<N_TSHIFT)|(IMAGE_SYM_DTYPE_POINTER<<N_BTSHFT)|((x)&N_BTMASK))
+#endif
+#ifndef DECREF
+#define DECREF(x) ((((x)>>N_TSHIFT)&~N_BTMASK)|((x)&N_BTMASK))
+#endif
+
+#define IMAGE_COMDAT_SELECT_NODUPLICATES    1
+#define IMAGE_COMDAT_SELECT_ANY             2
+#define IMAGE_COMDAT_SELECT_SAME_SIZE       3
+#define IMAGE_COMDAT_SELECT_EXACT_MATCH     4
+#define IMAGE_COMDAT_SELECT_ASSOCIATIVE     5
+#define IMAGE_COMDAT_SELECT_LARGEST         6
+#define IMAGE_COMDAT_SELECT_NEWEST          7
+
+#define IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY  1
+#define IMAGE_WEAK_EXTERN_SEARCH_LIBRARY    2
+#define IMAGE_WEAK_EXTERN_SEARCH_ALIAS      3
+
+/* Export module directory */
+
+typedef struct _IMAGE_EXPORT_DIRECTORY {
+	DWORD	Characteristics;
+	DWORD	TimeDateStamp;
+	WORD	MajorVersion;
+	WORD	MinorVersion;
+	DWORD	Name;
+	DWORD	Base;
+	DWORD	NumberOfFunctions;
+	DWORD	NumberOfNames;
+	DWORD	AddressOfFunctions;
+	DWORD	AddressOfNames;
+	DWORD	AddressOfNameOrdinals;
+} IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;
+
+/* Import name entry */
+typedef struct _IMAGE_IMPORT_BY_NAME {
+	WORD	Hint;
+	BYTE	Name[1];
+} IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;
+
+/* Import thunk */
+typedef struct _IMAGE_THUNK_DATA32 {
+	union {
+		DWORD    ForwarderString;
+		DWORD    Function;
+		DWORD    Ordinal;
+		DWORD	AddressOfData;
+	} u1;
+} IMAGE_THUNK_DATA32,*PIMAGE_THUNK_DATA32;
+
+typedef struct _IMAGE_THUNK_DATA64 {
+	union {
+		ULONGLONG    ForwarderString;
+		ULONGLONG    Function;
+		ULONGLONG    Ordinal;
+		ULONGLONG    AddressOfData;
+	} u1;
+} IMAGE_THUNK_DATA64,*PIMAGE_THUNK_DATA64;
+
+#ifdef CONFIG_X86_64
+typedef IMAGE_THUNK_DATA32 IMAGE_THUNK_DATA;
+typedef PIMAGE_THUNK_DATA32 PIMAGE_THUNK_DATA;
+#else
+typedef IMAGE_THUNK_DATA64 IMAGE_THUNK_DATA;
+typedef PIMAGE_THUNK_DATA64 PIMAGE_THUNK_DATA;
+#endif
+
+/* Import module directory */
+
+typedef struct __packed _IMAGE_IMPORT_DESCRIPTOR {
+	union {
+		DWORD	Characteristics; /* 0 for terminating null
+					  * import descriptor */
+		DWORD   OriginalFirstThunk; /* RVA to original unbound
+					     * IAT */
+	} u;
+	DWORD	TimeDateStamp;	/* 0 if not bound,
+				 * -1 if bound, and real date\time stamp
+				 *    in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT
+				 * (new BIND)
+				 * otherwise date/time stamp of DLL bound to
+				 * (Old BIND)
+				 */
+	DWORD	ForwarderChain;	/* -1 if no forwarders */
+	DWORD	Name;
+	/* RVA to IAT (if bound this IAT has actual addresses) */
+	DWORD   FirstThunk;
+} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;
+
+#define	IMAGE_ORDINAL_FLAG32		0x80000000
+#define	IMAGE_ORDINAL_FLAG64		0x8000000000000000UL
+#define	IMAGE_SNAP_BY_ORDINAL32(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG32) != 0)
+#define	IMAGE_SNAP_BY_ORDINAL64(Ordinal) ((Ordinal & IMAGE_ORDINAL_FLAG64) != 0)
+#define	IMAGE_ORDINAL(Ordinal) (Ordinal & 0xffff)
+
+#ifdef CONFIG_X86_64
+#define IMAGE_ORDINAL_FLAG IMAGE_ORDINAL_FLAG64
+#define IMAGE_SNAP_BY_ORDINAL IMAGE_SNAP_BY_ORDINAL64
+#else
+#define IMAGE_ORDINAL_FLAG IMAGE_ORDINAL_FLAG32
+#define IMAGE_SNAP_BY_ORDINAL IMAGE_SNAP_BY_ORDINAL32
+#endif
+
+typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR
+{
+    DWORD   TimeDateStamp;
+    WORD    OffsetModuleName;
+    WORD    NumberOfModuleForwarderRefs;
+/* Array of zero or more IMAGE_BOUND_FORWARDER_REF follows */
+} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;
+
+typedef struct _IMAGE_BOUND_FORWARDER_REF
+{
+    DWORD   TimeDateStamp;
+    WORD    OffsetModuleName;
+    WORD    Reserved;
+} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;
+
+typedef struct _IMAGE_BASE_RELOCATION
+{
+	DWORD	VirtualAddress;
+	DWORD	SizeOfBlock;
+	WORD	TypeOffset[0];
+} IMAGE_BASE_RELOCATION,*PIMAGE_BASE_RELOCATION;
+
+typedef struct _IMAGE_RELOCATION
+{
+    union {
+        DWORD   VirtualAddress;
+        DWORD   RelocCount;
+    } DUMMYUNIONNAME;
+    DWORD   SymbolTableIndex;
+    WORD    Type;
+} IMAGE_RELOCATION, *PIMAGE_RELOCATION;
+
+#define IMAGE_SIZEOF_RELOCATION 10
+
+/* generic relocation types */
+#define IMAGE_REL_BASED_ABSOLUTE		0
+#define IMAGE_REL_BASED_HIGH			1
+#define IMAGE_REL_BASED_LOW			2
+#define IMAGE_REL_BASED_HIGHLOW			3
+#define IMAGE_REL_BASED_HIGHADJ			4
+#define IMAGE_REL_BASED_MIPS_JMPADDR		5
+#define IMAGE_REL_BASED_SECTION			6
+#define	IMAGE_REL_BASED_REL			7
+#define IMAGE_REL_BASED_MIPS_JMPADDR16		9
+#define IMAGE_REL_BASED_IA64_IMM64		9 /* yes, 9 too */
+#define IMAGE_REL_BASED_DIR64			10
+#define IMAGE_REL_BASED_HIGH3ADJ		11
+
+/* I386 relocation types */
+#define	IMAGE_REL_I386_ABSOLUTE			0
+#define	IMAGE_REL_I386_DIR16			1
+#define	IMAGE_REL_I386_REL16			2
+#define	IMAGE_REL_I386_DIR32			6
+#define	IMAGE_REL_I386_DIR32NB			7
+#define	IMAGE_REL_I386_SEG12			9
+#define	IMAGE_REL_I386_SECTION			10
+#define	IMAGE_REL_I386_SECREL			11
+#define	IMAGE_REL_I386_REL32			20
+
+/* MIPS relocation types */
+#define IMAGE_REL_MIPS_ABSOLUTE		0x0000
+#define IMAGE_REL_MIPS_REFHALF		0x0001
+#define IMAGE_REL_MIPS_REFWORD		0x0002
+#define IMAGE_REL_MIPS_JMPADDR		0x0003
+#define IMAGE_REL_MIPS_REFHI		0x0004
+#define IMAGE_REL_MIPS_REFLO		0x0005
+#define IMAGE_REL_MIPS_GPREL		0x0006
+#define IMAGE_REL_MIPS_LITERAL		0x0007
+#define IMAGE_REL_MIPS_SECTION		0x000A
+#define IMAGE_REL_MIPS_SECREL		0x000B
+#define IMAGE_REL_MIPS_SECRELLO		0x000C
+#define IMAGE_REL_MIPS_SECRELHI		0x000D
+#define	IMAGE_REL_MIPS_JMPADDR16	0x0010
+#define IMAGE_REL_MIPS_REFWORDNB	0x0022
+#define IMAGE_REL_MIPS_PAIR		0x0025
+
+/* ALPHA relocation types */
+#define IMAGE_REL_ALPHA_ABSOLUTE	0x0000
+#define IMAGE_REL_ALPHA_REFLONG		0x0001
+#define IMAGE_REL_ALPHA_REFQUAD		0x0002
+#define IMAGE_REL_ALPHA_GPREL		0x0003
+#define IMAGE_REL_ALPHA_LITERAL		0x0004
+#define IMAGE_REL_ALPHA_LITUSE		0x0005
+#define IMAGE_REL_ALPHA_GPDISP		0x0006
+#define IMAGE_REL_ALPHA_BRADDR		0x0007
+#define IMAGE_REL_ALPHA_HINT		0x0008
+#define IMAGE_REL_ALPHA_INLINE_REFLONG	0x0009
+#define IMAGE_REL_ALPHA_REFHI		0x000A
+#define IMAGE_REL_ALPHA_REFLO		0x000B
+#define IMAGE_REL_ALPHA_PAIR		0x000C
+#define IMAGE_REL_ALPHA_MATCH		0x000D
+#define IMAGE_REL_ALPHA_SECTION		0x000E
+#define IMAGE_REL_ALPHA_SECREL		0x000F
+#define IMAGE_REL_ALPHA_REFLONGNB	0x0010
+#define IMAGE_REL_ALPHA_SECRELLO	0x0011
+#define IMAGE_REL_ALPHA_SECRELHI	0x0012
+#define IMAGE_REL_ALPHA_REFQ3		0x0013
+#define IMAGE_REL_ALPHA_REFQ2		0x0014
+#define IMAGE_REL_ALPHA_REFQ1		0x0015
+#define IMAGE_REL_ALPHA_GPRELLO		0x0016
+#define IMAGE_REL_ALPHA_GPRELHI		0x0017
+
+/* PowerPC relocation types */
+#define IMAGE_REL_PPC_ABSOLUTE          0x0000
+#define IMAGE_REL_PPC_ADDR64            0x0001
+#define IMAGE_REL_PPC_ADDR            0x0002
+#define IMAGE_REL_PPC_ADDR24            0x0003
+#define IMAGE_REL_PPC_ADDR16            0x0004
+#define IMAGE_REL_PPC_ADDR14            0x0005
+#define IMAGE_REL_PPC_REL24             0x0006
+#define IMAGE_REL_PPC_REL14             0x0007
+#define IMAGE_REL_PPC_TOCREL16          0x0008
+#define IMAGE_REL_PPC_TOCREL14          0x0009
+#define IMAGE_REL_PPC_ADDR32NB          0x000A
+#define IMAGE_REL_PPC_SECREL            0x000B
+#define IMAGE_REL_PPC_SECTION           0x000C
+#define IMAGE_REL_PPC_IFGLUE            0x000D
+#define IMAGE_REL_PPC_IMGLUE            0x000E
+#define IMAGE_REL_PPC_SECREL16          0x000F
+#define IMAGE_REL_PPC_REFHI             0x0010
+#define IMAGE_REL_PPC_REFLO             0x0011
+#define IMAGE_REL_PPC_PAIR              0x0012
+#define IMAGE_REL_PPC_SECRELLO          0x0013
+#define IMAGE_REL_PPC_SECRELHI          0x0014
+#define IMAGE_REL_PPC_GPREL		0x0015
+#define IMAGE_REL_PPC_TYPEMASK          0x00FF
+/* modifier bits */
+#define IMAGE_REL_PPC_NEG               0x0100
+#define IMAGE_REL_PPC_BRTAKEN           0x0200
+#define IMAGE_REL_PPC_BRNTAKEN          0x0400
+#define IMAGE_REL_PPC_TOCDEFN           0x0800
+
+/* SH3 ? relocation type */
+#define IMAGE_REL_SH3_ABSOLUTE          0x0000
+#define IMAGE_REL_SH3_DIRECT16          0x0001
+#define IMAGE_REL_SH3_DIRECT          0x0002
+#define IMAGE_REL_SH3_DIRECT8           0x0003
+#define IMAGE_REL_SH3_DIRECT8_WORD      0x0004
+#define IMAGE_REL_SH3_DIRECT8_LONG      0x0005
+#define IMAGE_REL_SH3_DIRECT4           0x0006
+#define IMAGE_REL_SH3_DIRECT4_WORD      0x0007
+#define IMAGE_REL_SH3_DIRECT4_LONG      0x0008
+#define IMAGE_REL_SH3_PCREL8_WORD       0x0009
+#define IMAGE_REL_SH3_PCREL8_LONG       0x000A
+#define IMAGE_REL_SH3_PCREL12_WORD      0x000B
+#define IMAGE_REL_SH3_STARTOF_SECTION   0x000C
+#define IMAGE_REL_SH3_SIZEOF_SECTION    0x000D
+#define IMAGE_REL_SH3_SECTION           0x000E
+#define IMAGE_REL_SH3_SECREL            0x000F
+#define IMAGE_REL_SH3_DIRECT32_NB       0x0010
+
+/* ARM (Archimedes?) relocation types */
+#define IMAGE_REL_ARM_ABSOLUTE		0x0000
+#define IMAGE_REL_ARM_ADDR		0x0001
+#define IMAGE_REL_ARM_ADDR32NB		0x0002
+#define IMAGE_REL_ARM_BRANCH24		0x0003
+#define IMAGE_REL_ARM_BRANCH11		0x0004
+#define IMAGE_REL_ARM_SECTION		0x000E
+#define IMAGE_REL_ARM_SECREL		0x000F
+
+/* IA64 relocation types */
+#define IMAGE_REL_IA64_ABSOLUTE		0x0000
+#define IMAGE_REL_IA64_IMM14		0x0001
+#define IMAGE_REL_IA64_IMM22		0x0002
+#define IMAGE_REL_IA64_IMM64		0x0003
+#define IMAGE_REL_IA64_DIR		0x0004
+#define IMAGE_REL_IA64_DIR64		0x0005
+#define IMAGE_REL_IA64_PCREL21B		0x0006
+#define IMAGE_REL_IA64_PCREL21M		0x0007
+#define IMAGE_REL_IA64_PCREL21F		0x0008
+#define IMAGE_REL_IA64_GPREL22		0x0009
+#define IMAGE_REL_IA64_LTOFF22		0x000A
+#define IMAGE_REL_IA64_SECTION		0x000B
+#define IMAGE_REL_IA64_SECREL22		0x000C
+#define IMAGE_REL_IA64_SECREL64I	0x000D
+#define IMAGE_REL_IA64_SECREL		0x000E
+#define IMAGE_REL_IA64_LTOFF64		0x000F
+#define IMAGE_REL_IA64_DIR32NB		0x0010
+#define IMAGE_REL_IA64_RESERVED_11	0x0011
+#define IMAGE_REL_IA64_RESERVED_12	0x0012
+#define IMAGE_REL_IA64_RESERVED_13	0x0013
+#define IMAGE_REL_IA64_RESERVED_14	0x0014
+#define IMAGE_REL_IA64_RESERVED_15	0x0015
+#define IMAGE_REL_IA64_RESERVED_16	0x0016
+#define IMAGE_REL_IA64_ADDEND		0x001F
+
+/* archive format */
+
+#define IMAGE_ARCHIVE_START_SIZE             8
+#define IMAGE_ARCHIVE_START                  "!<arch>\n"
+#define IMAGE_ARCHIVE_END                    "`\n"
+#define IMAGE_ARCHIVE_PAD                    "\n"
+#define IMAGE_ARCHIVE_LINKER_MEMBER          "/               "
+#define IMAGE_ARCHIVE_LONGNAMES_MEMBER       "//              "
+
+typedef struct _IMAGE_ARCHIVE_MEMBER_HEADER
+{
+    BYTE     Name[16];
+    BYTE     Date[12];
+    BYTE     UserID[6];
+    BYTE     GroupID[6];
+    BYTE     Mode[8];
+    BYTE     Size[10];
+    BYTE     EndHeader[2];
+} IMAGE_ARCHIVE_MEMBER_HEADER, *PIMAGE_ARCHIVE_MEMBER_HEADER;
+
+#define IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60
+
+/*
+ * Resource directory stuff
+ */
+typedef struct _IMAGE_RESOURCE_DIRECTORY {
+	DWORD	Characteristics;
+	DWORD	TimeDateStamp;
+	WORD	MajorVersion;
+	WORD	MinorVersion;
+	WORD	NumberOfNamedEntries;
+	WORD	NumberOfIdEntries;
+	/*  IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[]; */
+} IMAGE_RESOURCE_DIRECTORY,*PIMAGE_RESOURCE_DIRECTORY;
+
+#define	IMAGE_RESOURCE_NAME_IS_STRING		0x80000000
+#define	IMAGE_RESOURCE_DATA_IS_DIRECTORY	0x80000000
+
+typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
+	union {
+		struct {
+#ifdef BITFIELDS_BIGENDIAN
+			unsigned NameIsString:1;
+			unsigned NameOffset:31;
+#else
+			unsigned NameOffset:31;
+			unsigned NameIsString:1;
+#endif
+		} DUMMYSTRUCTNAME1;
+		DWORD   Name;
+                struct {
+#ifdef WORDS_BIGENDIAN
+			WORD    __pad;
+			WORD    Id;
+#else
+			WORD    Id;
+			WORD    __pad;
+#endif
+		} DUMMYSTRUCTNAME2;
+	} DUMMYUNIONNAME1;
+	union {
+		DWORD   OffsetToData;
+		struct {
+#ifdef BITFIELDS_BIGENDIAN
+			unsigned DataIsDirectory:1;
+			unsigned OffsetToDirectory:31;
+#else
+			unsigned OffsetToDirectory:31;
+			unsigned DataIsDirectory:1;
+#endif
+		} DUMMYSTRUCTNAME3;
+	} DUMMYUNIONNAME2;
+} IMAGE_RESOURCE_DIRECTORY_ENTRY,*PIMAGE_RESOURCE_DIRECTORY_ENTRY;
+
+
+typedef struct _IMAGE_RESOURCE_DIRECTORY_STRING {
+	WORD	Length;
+	CHAR	NameString[ 1 ];
+} IMAGE_RESOURCE_DIRECTORY_STRING,*PIMAGE_RESOURCE_DIRECTORY_STRING;
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/pnp.c linux-5.6.11-ndis/3rdparty/ndiswrapper/pnp.c
--- linux-5.6.11/3rdparty/ndiswrapper/pnp.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/pnp.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,697 @@
+/*
+ *  Copyright (C) 2005 Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include "usb.h"
+#include "pnp.h"
+#include "wrapndis.h"
+#include "loader.h"
+
+/* Functions callable from the NDIS driver */
+wstdcall NTSTATUS pdoDispatchDeviceControl(struct device_object *pdo,
+					   struct irp *irp);
+wstdcall NTSTATUS pdoDispatchPnp(struct device_object *pdo, struct irp *irp);
+wstdcall NTSTATUS pdoDispatchPower(struct device_object *pdo, struct irp *irp);
+
+static NTSTATUS start_pdo(struct device_object *pdo)
+{
+	int i, ret, count, resources_size;
+	struct wrap_device *wd;
+	struct pci_dev *pdev;
+	struct cm_partial_resource_descriptor *entry;
+	struct cm_partial_resource_list *partial_resource_list;
+
+	ENTER1("%p, %p", pdo, pdo->reserved);
+	wd = pdo->reserved;
+	if (ntoskernel_init_device(wd))
+		EXIT1(return STATUS_FAILURE);
+	if (wrap_is_usb_bus(wd->dev_bus)) {
+		if (usb_init_device(wd)) {
+			ntoskernel_exit_device(wd);
+			EXIT1(return STATUS_FAILURE);
+		}
+		EXIT1(return STATUS_SUCCESS);
+	}
+	if (!wrap_is_pci_bus(wd->dev_bus))
+		EXIT1(return STATUS_SUCCESS);
+	pdev = wd->pci.pdev;
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		ERROR("couldn't enable PCI device: %x", ret);
+		return STATUS_FAILURE;
+	}
+	ret = pci_request_regions(pdev, DRIVER_NAME);
+	if (ret) {
+		ERROR("couldn't request PCI regions: %x", ret);
+		goto err_enable;
+	}
+	pci_set_power_state(pdev, PCI_D0);
+#ifdef CONFIG_X86_64
+	/* 64-bit broadcom driver doesn't work if DMA is allocated
+	 * from over 1GB */
+	if (wd->vendor == 0x14e4) {
+		if (pci_set_dma_mask(pdev, DMA_BIT_MASK(30)) ||
+		    pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(30)))
+			WARNING("couldn't set DMA mask; this driver "
+				"may not work with more than 1GB RAM");
+	}
+#endif
+	/* IRQ resource entry is filled in from pdev, instead of
+	 * pci_resource macros */
+	for (i = count = 0; pci_resource_start(pdev, i); i++)
+		if ((pci_resource_flags(pdev, i) & IORESOURCE_MEM) ||
+		    (pci_resource_flags(pdev, i) & IORESOURCE_IO))
+			count++;
+	/* space for entry for IRQ is already in
+	 * cm_partial_resource_list */
+	resources_size = sizeof(struct cm_resource_list) +
+		sizeof(struct cm_partial_resource_descriptor) * count;
+	TRACE2("resources: %d, %d", count, resources_size);
+	wd->resource_list = kzalloc(resources_size, GFP_KERNEL);
+	if (!wd->resource_list) {
+		WARNING("couldn't allocate memory");
+		goto err_regions;
+	}
+	wd->resource_list->count = 1;
+	wd->resource_list->list[0].interface_type = PCIBus;
+	/* bus_number is not used by WDM drivers */
+	wd->resource_list->list[0].bus_number = pdev->bus->number;
+
+	partial_resource_list =
+		&wd->resource_list->list->partial_resource_list;
+	partial_resource_list->version = 1;
+	partial_resource_list->revision = 1;
+	partial_resource_list->count = count + 1;
+
+	for (i = count = 0; pci_resource_start(pdev, i); i++) {
+		entry = &partial_resource_list->partial_descriptors[count];
+		TRACE2("%d", count);
+		if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) {
+			entry->type = CmResourceTypeMemory;
+			entry->flags = CM_RESOURCE_MEMORY_READ_WRITE;
+			entry->share = CmResourceShareDeviceExclusive;
+		} else if (pci_resource_flags(pdev, i) & IORESOURCE_IO) {
+			entry->type = CmResourceTypePort;
+			entry->flags = CM_RESOURCE_PORT_IO;
+			entry->share = CmResourceShareDeviceExclusive;
+#if 0
+		} else if (pci_resource_flags(pdev, i) & IORESOURCE_DMA) {
+			/* it looks like no driver uses this resource */
+			typeof(pci_resource_flags(pdev, 0)) flags;
+			entry->type = CmResourceTypeDma;
+			flags = pci_resource_flags(pdev, i);
+			if (flags & IORESOURCE_DMA_TYPEA)
+				entry->flags |= CM_RESOURCE_DMA_TYPE_A;
+			else if (flags & IORESOURCE_DMA_TYPEB)
+				entry->flags |= CM_RESOURCE_DMA_TYPE_B;
+			else if (flags & IORESOURCE_DMA_TYPEF)
+				entry->flags |= CM_RESOURCE_DMA_TYPE_F;
+			if (flags & IORESOURCE_DMA_8BIT)
+				entry->flags |= CM_RESOURCE_DMA_8;
+			else if (flags & IORESOURCE_DMA_16BIT)
+				entry->flags |= CM_RESOURCE_DMA_16;
+			/* what about 32bit DMA? */
+			else if (flags & IORESOURCE_DMA_8AND16BIT)
+				entry->flags |= CM_RESOURCE_DMA_8_AND_16;
+			if (flags & IORESOURCE_DMA_MASTER)
+				entry->flags |= CM_RESOURCE_DMA_BUS_MASTER;
+			entry->u.dma.channel = pci_resource_start(pdev, i);
+			/* what should this be? */
+			entry->u.dma.port = 1;
+#endif
+		} else
+			continue;
+		/* TODO: Add other resource types? */
+		entry->u.generic.start =
+			(ULONG_PTR)pci_resource_start(pdev, i);
+		entry->u.generic.length = pci_resource_len(pdev, i);
+		count++;
+	}
+
+	/* put IRQ resource at the end */
+	entry = &partial_resource_list->partial_descriptors[count++];
+	entry->type = CmResourceTypeInterrupt;
+	entry->flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
+	/* we assume all devices use shared IRQ */
+	entry->share = CmResourceShareShared;
+	/* as per documentation, interrupt level should be DIRQL, but
+	 * examples from DDK as well some drivers, such as AR5211,
+	 * RT8180L use interrupt level as interrupt vector also in
+	 * NdisMRegisterInterrupt */
+	entry->u.interrupt.level = pdev->irq;
+	entry->u.interrupt.vector = pdev->irq;
+	entry->u.interrupt.affinity = -1;
+
+	TRACE2("resource list count %d, irq: %d",
+	       partial_resource_list->count, pdev->irq);
+	pci_set_drvdata(pdev, wd);
+	EXIT1(return STATUS_SUCCESS);
+err_regions:
+	pci_release_regions(pdev);
+err_enable:
+	pci_disable_device(pdev);
+	wd->pci.pdev = NULL;
+	wd->pdo = NULL;
+	EXIT1(return STATUS_FAILURE);
+}
+
+static void remove_pdo(struct device_object *pdo)
+{
+	struct wrap_device *wd = pdo->reserved;
+
+	ntoskernel_exit_device(wd);
+	if (wrap_is_pci_bus(wd->dev_bus)) {
+		struct pci_dev *pdev = wd->pci.pdev;
+		pci_release_regions(pdev);
+		pci_disable_device(pdev);
+		wd->pci.pdev = NULL;
+		pci_set_drvdata(pdev, NULL);
+	} else if (wrap_is_usb_bus(wd->dev_bus)) {
+		usb_exit_device(wd);
+	}
+	kfree(wd->resource_list);
+	wd->resource_list = NULL;
+	return;
+}
+
+static NTSTATUS IoSendIrpTopDev(struct device_object *dev_obj, ULONG major_fn,
+				ULONG minor_fn, struct io_stack_location *sl)
+{
+	NTSTATUS status;
+	struct nt_event event;
+	struct irp *irp;
+	struct io_stack_location *irp_sl;
+	struct device_object *top_dev = IoGetAttachedDeviceReference(dev_obj);
+
+	KeInitializeEvent(&event, NotificationEvent, FALSE);
+	irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, top_dev, NULL, 0, NULL,
+					   &event, NULL);
+	irp->io_status.status = STATUS_NOT_IMPLEMENTED;
+	irp->io_status.info = 0;
+	irp_sl = IoGetNextIrpStackLocation(irp);
+	if (sl)
+		memcpy(irp_sl, sl, sizeof(*irp_sl));
+	irp_sl->major_fn = major_fn;
+	irp_sl->minor_fn = minor_fn;
+	status = IoCallDriver(top_dev, irp);
+	if (status == STATUS_PENDING) {
+		KeWaitForSingleObject(&event, Executive, KernelMode,
+				      FALSE, NULL);
+		status = irp->io_status.status;
+	}
+	ObDereferenceObject(top_dev);
+	return status;
+}
+
+wstdcall NTSTATUS pdoDispatchDeviceControl(struct device_object *pdo,
+					   struct irp *irp)
+{
+	NTSTATUS status;
+	struct wrap_device *wd = pdo->reserved;
+
+	DUMP_IRP(irp);
+	(void)wd;
+	if (wrap_is_usb_bus(wd->dev_bus)) {
+		status = wrap_submit_irp(pdo, irp);
+		IOTRACE("status: %08X", status);
+		if (status != STATUS_PENDING)
+			IoCompleteRequest(irp, IO_NO_INCREMENT);
+	} else {
+		status = irp->io_status.status = STATUS_NOT_IMPLEMENTED;
+		IoCompleteRequest(irp, IO_NO_INCREMENT);
+	}
+	IOEXIT(return status);
+}
+WIN_FUNC_DECL(pdoDispatchDeviceControl,2)
+
+wstdcall NTSTATUS pdoDispatchPnp(struct device_object *pdo, struct irp *irp)
+{
+	struct io_stack_location *irp_sl;
+	NTSTATUS status;
+	struct wrap_device *wd = pdo->reserved;
+
+	irp_sl = IoGetCurrentIrpStackLocation(irp);
+	TRACE2("%p %d:%d", pdo, irp_sl->major_fn, irp_sl->minor_fn);
+	switch (irp_sl->minor_fn) {
+	case IRP_MN_START_DEVICE:
+		status = start_pdo(pdo);
+		break;
+	case IRP_MN_QUERY_STOP_DEVICE:
+	case IRP_MN_STOP_DEVICE:
+	case IRP_MN_QUERY_REMOVE_DEVICE:
+		status = STATUS_SUCCESS;
+		break;
+	case IRP_MN_REMOVE_DEVICE:
+		remove_pdo(pdo);
+		status = STATUS_SUCCESS;
+		break;
+	case IRP_MN_QUERY_INTERFACE:
+		if (wrap_is_usb_bus(wd->dev_bus))
+			status = usb_query_interface(wd, irp_sl);
+		else
+			status = STATUS_NOT_IMPLEMENTED;
+		break;
+	default:
+		TRACE2("fn %d not implemented", irp_sl->minor_fn);
+		status = STATUS_SUCCESS;
+		break;
+	}
+	irp->io_status.status = status;
+	TRACE2("status: %08X", status);
+	IoCompleteRequest(irp, IO_NO_INCREMENT);
+	IOEXIT(return status);
+}
+WIN_FUNC_DECL(pdoDispatchPnp,2)
+
+wstdcall NTSTATUS pdoDispatchPower(struct device_object *pdo, struct irp *irp)
+{
+	struct io_stack_location *irp_sl;
+	struct wrap_device *wd;
+	union power_state power_state;
+	struct pci_dev *pdev;
+	NTSTATUS status;
+
+	irp_sl = IoGetCurrentIrpStackLocation(irp);
+	wd = pdo->reserved;
+	TRACE2("pdo: %p, fn: %d:%d, wd: %p",
+	       pdo, irp_sl->major_fn, irp_sl->minor_fn, wd);
+	switch (irp_sl->minor_fn) {
+	case IRP_MN_WAIT_WAKE:
+		/* TODO: this is not complete/correct */
+		TRACE2("state: %d, completion: %p",
+			  irp_sl->params.power.state.system_state,
+			  irp_sl->completion_routine);
+		IoMarkIrpPending(irp);
+		status = STATUS_PENDING;
+		break;
+	case IRP_MN_SET_POWER:
+		power_state = irp_sl->params.power.state;
+		if (power_state.device_state == PowerDeviceD0) {
+			TRACE2("resuming %p", wd);
+			if (wrap_is_pci_bus(wd->dev_bus)) {
+				pdev = wd->pci.pdev;
+				pci_restore_state(pdev);
+				if (wd->pci.wake_state == PowerDeviceD3) {
+					pci_enable_wake(wd->pci.pdev,
+							PCI_D3hot, 0);
+					pci_enable_wake(wd->pci.pdev,
+							PCI_D3cold, 0);
+				}
+				pci_set_power_state(pdev, PCI_D0);
+			} else if (wrap_is_usb_bus(wd->dev_bus)) {
+				wrap_resume_urbs(wd);
+			}
+		} else {
+			TRACE2("suspending device %p", wd);
+			if (wrap_is_pci_bus(wd->dev_bus)) {
+				pdev = wd->pci.pdev;
+				pci_save_state(pdev);
+				TRACE2("%d", wd->pci.wake_state);
+				if (wd->pci.wake_state == PowerDeviceD3) {
+					pci_enable_wake(wd->pci.pdev,
+							PCI_D3hot, 1);
+					pci_enable_wake(wd->pci.pdev,
+							PCI_D3cold, 1);
+				}
+				pci_set_power_state(pdev, PCI_D3hot);
+			} else if (wrap_is_usb_bus(wd->dev_bus)) {
+				wrap_suspend_urbs(wd);
+			}
+		}
+		status = STATUS_SUCCESS;
+		break;
+	case IRP_MN_QUERY_POWER:
+		status = STATUS_SUCCESS;
+		break;
+	default:
+		TRACE2("fn %d not implemented", irp_sl->minor_fn);
+		status = STATUS_SUCCESS;
+		break;
+	}
+	irp->io_status.status = status;
+	IoCompleteRequest(irp, IO_NO_INCREMENT);
+	return status;
+}
+WIN_FUNC_DECL(pdoDispatchPower,2)
+
+static NTSTATUS pnp_set_device_power_state(struct wrap_device *wd,
+					   enum device_power_state state)
+{
+	NTSTATUS status;
+	struct device_object *pdo;
+	struct io_stack_location irp_sl;
+
+	pdo = wd->pdo;
+	IOTRACE("%p, %p", pdo, IoGetAttachedDevice(pdo));
+	memset(&irp_sl, 0, sizeof(irp_sl));
+	irp_sl.params.power.state.device_state = state;
+	irp_sl.params.power.type = DevicePowerState;
+	if (state > PowerDeviceD0) {
+		status = IoSendIrpTopDev(pdo, IRP_MJ_POWER, IRP_MN_QUERY_POWER,
+					 &irp_sl);
+		if (status != STATUS_SUCCESS) {
+			TRACE1("query of power to %d returns %08X",
+			       state, status);
+			EXIT1(return status);
+		}
+	}
+	status = IoSendIrpTopDev(pdo, IRP_MJ_POWER, IRP_MN_SET_POWER, &irp_sl);
+	if (status != STATUS_SUCCESS)
+		WARNING("setting power to %d failed: %08X", state, status);
+	EXIT1(return status);
+}
+
+static NTSTATUS pnp_start_device(struct wrap_device *wd)
+{
+	struct device_object *fdo;
+	struct device_object *pdo;
+	struct io_stack_location irp_sl;
+	NTSTATUS status;
+
+	pdo = wd->pdo;
+	/* TODO: for now we use same resources for both translated
+	 * resources and raw resources */
+	memset(&irp_sl, 0, sizeof(irp_sl));
+	irp_sl.params.start_device.allocated_resources =
+		wd->resource_list;
+	irp_sl.params.start_device.allocated_resources_translated =
+		wd->resource_list;
+	status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_START_DEVICE, &irp_sl);
+	fdo = IoGetAttachedDevice(pdo);
+	fdo->drv_obj->drv_ext->count++;
+	if (status != STATUS_SUCCESS)
+		WARNING("Windows driver couldn't initialize the device (%08X)",
+			status);
+	EXIT1(return status);
+}
+
+#if 0
+static NTSTATUS pnp_stop_device(struct wrap_device *wd)
+{
+	struct device_object *pdo;
+	NTSTATUS status;
+
+	pdo = wd->pdo;
+	status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_QUERY_STOP_DEVICE,
+				 NULL);
+	if (status != STATUS_SUCCESS)
+		WARNING("status: %08X", status);
+	/* for now we ignore query status */
+	status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_STOP_DEVICE, NULL);
+	if (status != STATUS_SUCCESS)
+		WARNING("status: %08X", status);
+	if (status != STATUS_SUCCESS)
+		WARNING("status: %08X", status);
+	EXIT2(return status);
+}
+#endif
+
+static NTSTATUS pnp_remove_device(struct wrap_device *wd)
+{
+	struct device_object *pdo, *fdo;
+	struct driver_object *fdo_drv_obj;
+	NTSTATUS status;
+
+	pdo = wd->pdo;
+	fdo = IoGetAttachedDevice(pdo);
+	fdo_drv_obj = fdo->drv_obj;
+	TRACE2("%p, %p, %p", pdo, fdo, fdo_drv_obj);
+	status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_QUERY_REMOVE_DEVICE,
+				 NULL);
+	if (status != STATUS_SUCCESS)
+		WARNING("status: %08X", status);
+
+	status = IoSendIrpTopDev(pdo, IRP_MJ_PNP, IRP_MN_REMOVE_DEVICE, NULL);
+	if (status != STATUS_SUCCESS)
+		WARNING("status: %08X", status);
+	/* TODO: should we use count in drv_ext or driver's Object
+	 * header reference count to keep count of devices associated
+	 * with a driver? */
+	if (status == STATUS_SUCCESS)
+		fdo_drv_obj->drv_ext->count--;
+	TRACE1("count: %d", fdo_drv_obj->drv_ext->count);
+	if ((LONG)fdo_drv_obj->drv_ext->count < 0)
+		WARNING("wrong count: %d", fdo_drv_obj->drv_ext->count);
+	if (fdo_drv_obj->drv_ext->count == 0) {
+		struct wrap_driver *wrap_driver;
+		TRACE1("unloading driver: %p", fdo_drv_obj);
+		wrap_driver =
+			IoGetDriverObjectExtension(fdo_drv_obj,
+					   (void *)WRAP_DRIVER_CLIENT_ID);
+		if (fdo_drv_obj->unload)
+			LIN2WIN1(fdo_drv_obj->unload, fdo_drv_obj);
+		if (wrap_driver) {
+			mutex_lock(&loader_mutex);
+			unload_wrap_driver(wrap_driver);
+			mutex_unlock(&loader_mutex);
+		} else
+			ERROR("couldn't get wrap_driver");
+		ObDereferenceObject(fdo_drv_obj);
+	}
+	IoDeleteDevice(pdo);
+	unload_wrap_device(wd);
+	EXIT1(return status);
+}
+
+WIN_FUNC_DECL(IoInvalidDeviceRequest,2)
+
+static struct device_object *alloc_pdo(struct driver_object *drv_obj)
+{
+	struct device_object *pdo;
+	NTSTATUS status;
+	int i;
+	struct ansi_string ansi_name;
+	struct unicode_string unicode_name;
+
+	RtlInitAnsiString(&ansi_name, "NDISpdo");
+	if (RtlAnsiStringToUnicodeString(&unicode_name, &ansi_name, TRUE) ==
+	    STATUS_SUCCESS) {
+		status = IoCreateDevice(drv_obj, 0, &unicode_name,
+					FILE_DEVICE_UNKNOWN,
+					FILE_AUTOGENERATED_DEVICE_NAME,
+					FALSE, &pdo);
+		RtlFreeUnicodeString(&unicode_name);
+	} else {
+		status = IoCreateDevice(drv_obj, 0, NULL,
+					FILE_DEVICE_UNKNOWN,
+					FILE_AUTOGENERATED_DEVICE_NAME,
+					FALSE, &pdo);
+	}
+	TRACE1("%p, %d, %p", drv_obj, status, pdo);
+	if (status != STATUS_SUCCESS)
+		return NULL;
+	/* dispatch routines are called as Windows functions */
+	for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
+		drv_obj->major_func[i] = WIN_FUNC_PTR(IoInvalidDeviceRequest,2);
+	drv_obj->major_func[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
+		WIN_FUNC_PTR(pdoDispatchDeviceControl,2);
+	drv_obj->major_func[IRP_MJ_DEVICE_CONTROL] =
+		WIN_FUNC_PTR(pdoDispatchDeviceControl,2);
+	drv_obj->major_func[IRP_MJ_POWER] = WIN_FUNC_PTR(pdoDispatchPower,2);
+	drv_obj->major_func[IRP_MJ_PNP] = WIN_FUNC_PTR(pdoDispatchPnp,2);
+	return pdo;
+}
+
+static int wrap_pnp_start_device(struct wrap_device *wd)
+{
+	struct wrap_driver *driver;
+	struct device_object *pdo;
+	struct driver_object *pdo_drv_obj;
+
+	ENTER1("wd: %p", wd);
+
+	if (!((wrap_is_pci_bus(wd->dev_bus)) ||
+	      (wrap_is_usb_bus(wd->dev_bus)))) {
+		ERROR("bus type %d (%d) not supported",
+		      WRAP_BUS(wd->dev_bus), wd->dev_bus);
+		EXIT1(return -EINVAL);
+	}
+	driver = load_wrap_driver(wd);
+	if (!driver)
+		return -ENODEV;
+
+	wd->driver = driver;
+	wd->dev_bus = WRAP_DEVICE_BUS(driver->dev_type, WRAP_BUS(wd->dev_bus));
+	TRACE1("dev type: %d, bus type: %d, %d", WRAP_DEVICE(wd->dev_bus),
+	       WRAP_BUS(wd->dev_bus), wd->dev_bus);
+	TRACE1("%d, %d", driver->dev_type, wrap_is_usb_bus(wd->dev_bus));
+	/* first create pdo */
+	if (wrap_is_pci_bus(wd->dev_bus))
+		pdo_drv_obj = find_bus_driver("PCI");
+	else // if (wrap_is_usb_bus(wd->dev_bus))
+		pdo_drv_obj = find_bus_driver("USB");
+	if (!pdo_drv_obj)
+		return -EINVAL;
+	pdo = alloc_pdo(pdo_drv_obj);
+	if (!pdo)
+		return -ENOMEM;
+	wd->pdo = pdo;
+	pdo->reserved = wd;
+	if (WRAP_DEVICE(wd->dev_bus) == WRAP_NDIS_DEVICE) {
+		if (init_ndis_driver(driver->drv_obj)) {
+			IoDeleteDevice(pdo);
+			return -EINVAL;
+		}
+	}
+	TRACE1("%p", driver->drv_obj->drv_ext->add_device);
+	if (driver->drv_obj->drv_ext->add_device(driver->drv_obj, pdo) !=
+	    STATUS_SUCCESS) {
+		IoDeleteDevice(pdo);
+		return -ENOMEM;
+	}
+	if (pnp_start_device(wd) != STATUS_SUCCESS) {
+		/* TODO: we need proper cleanup, to deallocate memory,
+		 * for example */
+		pnp_remove_device(wd);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int wrap_pnp_start_pci_device(struct pci_dev *pdev,
+			      const struct pci_device_id *ent)
+{
+	struct load_device load_device;
+	struct wrap_device *wd;
+
+	ENTER1("called for %04x:%04x:%04x:%04x", pdev->vendor, pdev->device,
+	       pdev->subsystem_vendor, pdev->subsystem_device);
+
+	load_device.bus = WRAP_PCI_BUS;
+	load_device.vendor = pdev->vendor;
+	load_device.device = pdev->device;
+	load_device.subvendor = pdev->subsystem_vendor;
+	load_device.subdevice = pdev->subsystem_device;
+	wd = load_wrap_device(&load_device);
+	if (!wd)
+		EXIT1(return -ENODEV);
+	wd->pci.pdev = pdev;
+	return wrap_pnp_start_device(wd);
+}
+
+void wrap_pnp_remove_pci_device(struct pci_dev *pdev)
+{
+	struct wrap_device *wd;
+
+	wd = (struct wrap_device *)pci_get_drvdata(pdev);
+	ENTER1("%p, %p", pdev, wd);
+	if (!wd)
+		EXIT1(return);
+	pnp_remove_device(wd);
+}
+
+int wrap_pnp_suspend_pci_device(struct pci_dev *pdev, pm_message_t state)
+{
+	struct wrap_device *wd;
+
+	wd = (struct wrap_device *)pci_get_drvdata(pdev);
+	return pnp_set_device_power_state(wd, PowerDeviceD3);
+}
+
+int wrap_pnp_resume_pci_device(struct pci_dev *pdev)
+{
+	struct wrap_device *wd;
+
+	wd = (struct wrap_device *)pci_get_drvdata(pdev);
+	return pnp_set_device_power_state(wd, PowerDeviceD0);
+}
+
+#ifdef ENABLE_USB
+int wrap_pnp_start_usb_device(struct usb_interface *intf,
+			      const struct usb_device_id *usb_id)
+{
+	struct wrap_device *wd;
+	int ret;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	ENTER1("%04x, %04x, %04x", udev->descriptor.idVendor,
+	       udev->descriptor.idProduct, udev->descriptor.bDeviceClass);
+
+	/* USB device (e.g., RNDIS) may have multiple interfaces;
+	  initialize one interface only (is there a way to know which
+	  of these interfaces is for network?) */
+
+	if ((wd = get_wrap_device(udev, WRAP_USB_BUS))) {
+		TRACE1("device already initialized: %p", wd);
+		usb_set_intfdata(intf, NULL);
+		ret = 0;
+	} else {
+		struct load_device load_device;
+
+		load_device.bus = WRAP_USB_BUS;
+		load_device.vendor = le16_to_cpu(udev->descriptor.idVendor);
+		load_device.device = le16_to_cpu(udev->descriptor.idProduct);
+		load_device.subvendor = 0;
+		load_device.subdevice = 0;
+		wd = load_wrap_device(&load_device);
+		TRACE2("%p", wd);
+		if (wd) {
+			/* some devices (e.g., TI 4150, RNDIS) need
+			 * full reset */
+			ret = usb_reset_device(udev);
+			if (ret)
+				WARNING("reset failed: %d", ret);
+			usb_set_intfdata(intf, wd);
+			wd->usb.intf = intf;
+			wd->usb.udev = udev;
+			ret = wrap_pnp_start_device(wd);
+		} else
+			ret = -ENODEV;
+	}
+
+	TRACE2("ret: %d", ret);
+	if (ret)
+		EXIT1(return ret);
+	else
+		return 0;
+}
+
+void wrap_pnp_remove_usb_device(struct usb_interface *intf)
+{
+	struct wrap_device *wd;
+
+	wd = (struct wrap_device *)usb_get_intfdata(intf);
+	TRACE1("%p, %p", intf, wd);
+	if (wd == NULL)
+		EXIT1(return);
+	usb_set_intfdata(intf, NULL);
+	wd->usb.intf = NULL;
+	pnp_remove_device(wd);
+}
+
+int wrap_pnp_suspend_usb_device(struct usb_interface *intf, pm_message_t state)
+{
+	struct wrap_device *wd;
+
+	wd = usb_get_intfdata(intf);
+	ENTER1("%p, %p", intf, wd);
+	if (!wd)
+		EXIT1(return 0);
+	if (pnp_set_device_power_state(wd, PowerDeviceD3))
+		return -1;
+	return 0;
+}
+
+int wrap_pnp_resume_usb_device(struct usb_interface *intf)
+{
+	struct wrap_device *wd;
+	wd = usb_get_intfdata(intf);
+	ENTER1("%p, %p", intf, wd);
+	if (!wd)
+		EXIT1(return 0);
+	if (pnp_set_device_power_state(wd, PowerDeviceD0))
+		return -1;
+	return 0;
+}
+
+#endif // USB
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/pnp.h linux-5.6.11-ndis/3rdparty/ndiswrapper/pnp.h
--- linux-5.6.11/3rdparty/ndiswrapper/pnp.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/pnp.h	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,36 @@
+/*
+ *  Copyright (C) 2005 Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#ifndef _PNP_H_
+#define _PNP_H_
+
+#include "ntoskernel.h"
+#include "ndis.h"
+#include "wrapndis.h"
+
+int wrap_pnp_start_pci_device(struct pci_dev *pdev,
+			      const struct pci_device_id *ent);
+void wrap_pnp_remove_pci_device(struct pci_dev *pdev);
+int wrap_pnp_suspend_pci_device(struct pci_dev *pdev, pm_message_t state);
+int wrap_pnp_resume_pci_device(struct pci_dev *pdev);
+
+int wrap_pnp_start_usb_device(struct usb_interface *intf,
+			      const struct usb_device_id *usb_id);
+void wrap_pnp_remove_usb_device(struct usb_interface *intf);
+int wrap_pnp_suspend_usb_device(struct usb_interface *intf,
+				pm_message_t state);
+int wrap_pnp_resume_usb_device(struct usb_interface *intf);
+
+#endif
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/proc.c linux-5.6.11-ndis/3rdparty/ndiswrapper/proc.c
--- linux-5.6.11/3rdparty/ndiswrapper/proc.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/proc.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,619 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+#include "ndis.h"
+#include "iw_ndis.h"
+#include "wrapndis.h"
+#include "pnp.h"
+#include "wrapper.h"
+
+#define MAX_PROC_STR_LEN 32
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+static kuid_t proc_kuid;
+static kgid_t proc_kgid;
+#else
+#define proc_kuid proc_uid
+#define proc_kgid proc_gid
+#define kuid_t uid_t
+#define kgid_t gid_t
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+static inline struct inode *file_inode(struct file *f)
+{
+	return f->f_dentry->d_inode;
+}
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
+static inline struct inode *file_inode(struct file *f)
+{
+	return f->f_path.dentry->d_inode;
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
+static inline void proc_set_user(struct proc_dir_entry *de, kuid_t uid,
+				 kgid_t gid)
+{
+	de->uid = uid;
+	de->gid = gid;
+}
+
+static inline void proc_remove(struct proc_dir_entry *de)
+{
+	if (de)
+		remove_proc_entry(de->name, de->parent);
+}
+
+static inline void *PDE_DATA(const struct inode *inode)
+{
+	return PDE(inode)->data;
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+static inline struct proc_dir_entry *proc_create_data(const char *name,
+	umode_t mode, struct proc_dir_entry *parent,
+	struct file_operations *fops, void *data)
+{
+	struct proc_dir_entry *de;
+
+	de = create_proc_entry(name, mode, parent);
+	if (de) {
+		de->data = data;
+		de->proc_fops = fops;
+	}
+
+	return de;
+}
+#endif
+
+static int do_proc_make_entry(const char *name, umode_t mode,
+			      struct proc_dir_entry *parent,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
+			      const struct proc_ops *fops,
+#else
+			      struct file_operations *fops,
+#endif
+			      kuid_t uid, kgid_t gid, struct ndis_device *wnd)
+{
+	struct proc_dir_entry *de;
+
+	de = proc_create_data(name, mode, parent, fops, wnd);
+	if (de == NULL) {
+		ERROR("couldn't create proc entry for '%s'", name);
+		return -ENOMEM;
+	}
+	proc_set_user(de, uid, gid);
+	return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
+#define PROC_DECLARE_RO(name) \
+	static int proc_##name##_open(struct inode *inode, struct file *file) \
+	{ \
+		return single_open(file, proc_##name##_read, PDE_DATA(inode)); \
+	} \
+	static const struct proc_ops name##_fops = { \
+		.proc_open = proc_##name##_open, \
+		.proc_read = seq_read, \
+		.proc_lseek = seq_lseek, \
+		.proc_release = single_release, \
+	};
+
+#define PROC_DECLARE_RW(name) \
+	static int proc_##name##_open(struct inode *inode, struct file *file) \
+	{ \
+		return single_open(file, proc_##name##_read, PDE_DATA(inode)); \
+	} \
+	static const struct proc_ops name##_fops = { \
+		.proc_open = proc_##name##_open, \
+		.proc_read = seq_read, \
+		.proc_lseek = seq_lseek, \
+		.proc_release = single_release, \
+		.proc_write = proc_##name##_write, \
+	};
+#else
+#define PROC_DECLARE_RO(name) \
+	static int proc_##name##_open(struct inode *inode, struct file *file) \
+	{ \
+		return single_open(file, proc_##name##_read, PDE_DATA(inode)); \
+	} \
+	static struct file_operations name##_fops = { \
+		.owner = THIS_MODULE, \
+		.open = proc_##name##_open, \
+		.read = seq_read, \
+		.llseek = seq_lseek, \
+		.release = single_release, \
+	};
+
+#define PROC_DECLARE_RW(name) \
+	static int proc_##name##_open(struct inode *inode, struct file *file) \
+	{ \
+		return single_open(file, proc_##name##_read, PDE_DATA(inode)); \
+	} \
+	static struct file_operations name##_fops = { \
+		.owner = THIS_MODULE, \
+		.open = proc_##name##_open, \
+		.read = seq_read, \
+		.llseek = seq_lseek, \
+		.release = single_release, \
+		.write = proc_##name##_write, \
+	};
+#endif
+
+#define proc_make_entry_ro(name, parent, wnd) \
+	do_proc_make_entry(#name, S_IFREG | S_IRUSR | S_IRGRP, parent, \
+			   &name##_fops, proc_kuid, proc_kgid, wnd)
+#define proc_make_entry_rw(name, parent, wnd) \
+	do_proc_make_entry(#name, \
+			   S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP, \
+			   parent, &name##_fops, proc_kuid, proc_kgid, wnd)
+
+#define add_text(fmt, ...) seq_printf(sf, fmt, ##__VA_ARGS__)
+
+static struct proc_dir_entry *wrap_procfs_entry;
+
+static int proc_stats_read(struct seq_file *sf, void *v)
+{
+	struct ndis_device *wnd = (struct ndis_device *)sf->private;
+	struct ndis_wireless_stats stats;
+	NDIS_STATUS res;
+	ndis_rssi rssi;
+
+	res = mp_query(wnd, OID_802_11_RSSI, &rssi, sizeof(rssi));
+	if (!res)
+		add_text("signal_level=%d dBm\n", (s32)rssi);
+
+	res = mp_query(wnd, OID_802_11_STATISTICS, &stats, sizeof(stats));
+	if (!res) {
+		add_text("tx_frames=%llu\n", stats.tx_frag);
+		add_text("tx_multicast_frames=%llu\n", stats.tx_multi_frag);
+		add_text("tx_failed=%llu\n", stats.failed);
+		add_text("tx_retry=%llu\n", stats.retry);
+		add_text("tx_multi_retry=%llu\n", stats.multi_retry);
+		add_text("tx_rtss_success=%llu\n", stats.rtss_succ);
+		add_text("tx_rtss_fail=%llu\n", stats.rtss_fail);
+		add_text("ack_fail=%llu\n", stats.ack_fail);
+		add_text("frame_duplicates=%llu\n", stats.frame_dup);
+		add_text("rx_frames=%llu\n", stats.rx_frag);
+		add_text("rx_multicast_frames=%llu\n", stats.rx_multi_frag);
+		add_text("fcs_errors=%llu\n", stats.fcs_err);
+	}
+
+	return 0;
+}
+
+PROC_DECLARE_RO(stats)
+
+static int proc_encr_read(struct seq_file *sf, void *v)
+{
+	struct ndis_device *wnd = (struct ndis_device *)sf->private;
+	int i, encr_status, auth_mode, infra_mode;
+	NDIS_STATUS res;
+	struct ndis_essid essid;
+	mac_address ap_address;
+
+	res = mp_query(wnd, OID_802_11_BSSID,
+		       &ap_address, sizeof(ap_address));
+	if (res)
+		memset(ap_address, 0, ETH_ALEN);
+	add_text("ap_address=" MACSTRSEP "\n", MAC2STR(ap_address));
+
+	res = mp_query(wnd, OID_802_11_SSID, &essid, sizeof(essid));
+	if (!res)
+		add_text("essid=%.*s\n", essid.length, essid.essid);
+
+	res = mp_query_int(wnd, OID_802_11_ENCRYPTION_STATUS, &encr_status);
+	if (!res) {
+		typeof(&wnd->encr_info.keys[0]) tx_key;
+		add_text("tx_key=%u\n", wnd->encr_info.tx_key_index);
+		add_text("key=");
+		tx_key = &wnd->encr_info.keys[wnd->encr_info.tx_key_index];
+		if (tx_key->length > 0)
+			for (i = 0; i < tx_key->length; i++)
+				add_text("%2.2X", tx_key->key[i]);
+		else
+			add_text("off");
+		add_text("\n");
+		add_text("encr_mode=%d\n", encr_status);
+	}
+	res = mp_query_int(wnd, OID_802_11_AUTHENTICATION_MODE, &auth_mode);
+	if (!res)
+		add_text("auth_mode=%d\n", auth_mode);
+	res = mp_query_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, &infra_mode);
+	add_text("mode=%s\n", (infra_mode == Ndis802_11IBSS) ? "adhoc" :
+		 (infra_mode == Ndis802_11Infrastructure) ? "managed" : "auto");
+
+	return 0;
+}
+
+PROC_DECLARE_RO(encr)
+
+static int proc_hw_read(struct seq_file *sf, void *v)
+{
+	struct ndis_device *wnd = (struct ndis_device *)sf->private;
+	struct ndis_configuration config;
+	enum ndis_power power_mode;
+	NDIS_STATUS res;
+	ndis_tx_power_level tx_power;
+	ULONG bit_rate;
+	ndis_rts_threshold rts_threshold;
+	ndis_fragmentation_threshold frag_threshold;
+	ndis_antenna antenna;
+	ULONG packet_filter;
+	int n;
+	mac_address mac;
+	char *hw_status[] = {"ready", "initializing", "resetting", "closing",
+			     "not ready"};
+
+	res = mp_query_int(wnd, OID_GEN_HARDWARE_STATUS, &n);
+	if (res == NDIS_STATUS_SUCCESS && n >= 0 && n < ARRAY_SIZE(hw_status))
+		add_text("status=%s\n", hw_status[n]);
+
+	res = mp_query(wnd, OID_802_3_CURRENT_ADDRESS, mac, sizeof(mac));
+	if (!res)
+		add_text("mac: " MACSTRSEP "\n", MAC2STR(mac));
+	res = mp_query(wnd, OID_802_11_CONFIGURATION, &config, sizeof(config));
+	if (!res) {
+		add_text("beacon_period=%u msec\n", config.beacon_period);
+		add_text("atim_window=%u msec\n", config.atim_window);
+		add_text("frequency=%u kHz\n", config.ds_config);
+		add_text("hop_pattern=%u\n", config.fh_config.hop_pattern);
+		add_text("hop_set=%u\n", config.fh_config.hop_set);
+		add_text("dwell_time=%u msec\n", config.fh_config.dwell_time);
+	}
+
+	res = mp_query(wnd, OID_802_11_TX_POWER_LEVEL,
+		       &tx_power, sizeof(tx_power));
+	if (!res)
+		add_text("tx_power=%u mW\n", tx_power);
+
+	res = mp_query(wnd, OID_GEN_LINK_SPEED, &bit_rate, sizeof(bit_rate));
+	if (!res)
+		add_text("bit_rate=%u kBps\n", (u32)bit_rate / 10);
+
+	res = mp_query(wnd, OID_802_11_RTS_THRESHOLD,
+		       &rts_threshold, sizeof(rts_threshold));
+	if (!res)
+		add_text("rts_threshold=%u bytes\n", rts_threshold);
+
+	res = mp_query(wnd, OID_802_11_FRAGMENTATION_THRESHOLD,
+		       &frag_threshold, sizeof(frag_threshold));
+	if (!res)
+		add_text("frag_threshold=%u bytes\n", frag_threshold);
+
+	res = mp_query_int(wnd, OID_802_11_POWER_MODE, &power_mode);
+	if (!res)
+		add_text("power_mode=%s\n",
+			 (power_mode == NDIS_POWER_OFF) ? "always_on" :
+			 (power_mode == NDIS_POWER_MAX) ? "max_savings" :
+							  "min_savings");
+
+	res = mp_query(wnd, OID_802_11_NUMBER_OF_ANTENNAS,
+		       &antenna, sizeof(antenna));
+	if (!res)
+		add_text("num_antennas=%u\n", antenna);
+
+	res = mp_query(wnd, OID_802_11_TX_ANTENNA_SELECTED,
+		       &antenna, sizeof(antenna));
+	if (!res)
+		add_text("tx_antenna=%u\n", antenna);
+
+	res = mp_query(wnd, OID_802_11_RX_ANTENNA_SELECTED,
+		       &antenna, sizeof(antenna));
+	if (!res)
+		add_text("rx_antenna=%u\n", antenna);
+
+	add_text("encryption_modes=%s%s%s%s%s%s%s\n",
+		 test_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr) ?
+		 "WEP" : "none",
+		 test_bit(Ndis802_11Encryption2Enabled, &wnd->capa.encr) ?
+		 "; TKIP with WPA" : "",
+		 test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ?
+		 ", WPA2" : "",
+		 test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth) ?
+		 ", WPA2PSK" : "",
+		 test_bit(Ndis802_11Encryption3Enabled, &wnd->capa.encr) ?
+		 "; AES/CCMP with WPA" : "",
+		 test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ?
+		 ", WPA2" : "",
+		 test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth) ?
+		 ", WPA2PSK" : "");
+
+	res = mp_query_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, &packet_filter);
+	if (!res) {
+		if (packet_filter != wnd->packet_filter)
+			WARNING("wrong packet_filter? 0x%08x, 0x%08x\n",
+				packet_filter, wnd->packet_filter);
+		add_text("packet_filter: 0x%08x\n", packet_filter);
+	}
+
+	return 0;
+}
+
+PROC_DECLARE_RO(hw)
+
+static int proc_settings_read(struct seq_file *sf, void *v)
+{
+	struct ndis_device *wnd = (struct ndis_device *)sf->private;
+	struct wrap_device_setting *setting;
+
+	add_text("hangcheck_interval=%d\n", (hangcheck_interval == 0) ?
+		 (wnd->hangcheck_interval / HZ) : -1);
+
+	list_for_each_entry(setting, &wnd->wd->settings, list) {
+		add_text("%s=%s\n", setting->name, setting->value);
+	}
+
+	list_for_each_entry(setting, &wnd->wd->driver->settings, list) {
+		add_text("%s=%s\n", setting->name, setting->value);
+	}
+
+	return 0;
+}
+
+static ssize_t proc_settings_write(struct file *file, const char __user *buf,
+				   size_t count, loff_t *ppos)
+{
+	struct ndis_device *wnd = PDE_DATA(file_inode(file));
+	char setting[MAX_PROC_STR_LEN], *p;
+	unsigned int i;
+	NDIS_STATUS res;
+
+	if (count > MAX_PROC_STR_LEN)
+		return -EINVAL;
+
+	memset(setting, 0, sizeof(setting));
+	if (copy_from_user(setting, buf, count))
+		return -EFAULT;
+
+	if ((p = strchr(setting, '\n')))
+		*p = 0;
+
+	if ((p = strchr(setting, '=')))
+		*p = 0;
+
+	if (!strcmp(setting, "hangcheck_interval")) {
+		if (!p)
+			return -EINVAL;
+		p++;
+		i = simple_strtol(p, NULL, 10);
+		hangcheck_del(wnd);
+		if (i > 0) {
+			wnd->hangcheck_interval = i * HZ;
+			hangcheck_add(wnd);
+		}
+	} else if (!strcmp(setting, "suspend")) {
+		if (!p)
+			return -EINVAL;
+		p++;
+		i = simple_strtol(p, NULL, 10);
+		if (i <= 0 || i > 3)
+			return -EINVAL;
+		i = -1;
+		if (wrap_is_pci_bus(wnd->wd->dev_bus))
+			i = wrap_pnp_suspend_pci_device(wnd->wd->pci.pdev,
+							PMSG_SUSPEND);
+		else if (wrap_is_usb_bus(wnd->wd->dev_bus))
+			i = wrap_pnp_suspend_usb_device(wnd->wd->usb.intf,
+							PMSG_SUSPEND);
+		if (i)
+			return -EINVAL;
+	} else if (!strcmp(setting, "resume")) {
+		i = -1;
+		if (wrap_is_pci_bus(wnd->wd->dev_bus))
+			i = wrap_pnp_resume_pci_device(wnd->wd->pci.pdev);
+		else if (wrap_is_usb_bus(wnd->wd->dev_bus))
+			i = wrap_pnp_resume_usb_device(wnd->wd->usb.intf);
+		if (i)
+			return -EINVAL;
+	} else if (!strcmp(setting, "stats_enabled")) {
+		if (!p)
+			return -EINVAL;
+		p++;
+		i = simple_strtol(p, NULL, 10);
+		if (i > 0)
+			wnd->iw_stats_enabled = TRUE;
+		else
+			wnd->iw_stats_enabled = FALSE;
+	} else if (!strcmp(setting, "packet_filter")) {
+		if (!p)
+			return -EINVAL;
+		p++;
+		i = simple_strtol(p, NULL, 10);
+		res = mp_set_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, i);
+		if (res)
+			WARNING("setting packet_filter failed: %08X", res);
+	} else if (!strcmp(setting, "reinit")) {
+		if (ndis_reinit(wnd) != NDIS_STATUS_SUCCESS)
+			return -EFAULT;
+	} else {
+		struct ndis_configuration_parameter param;
+		struct unicode_string key;
+		struct ansi_string ansi;
+
+		if (!p)
+			return -EINVAL;
+		p++;
+		RtlInitAnsiString(&ansi, p);
+		if (RtlAnsiStringToUnicodeString(&param.data.string, &ansi,
+						 TRUE) != STATUS_SUCCESS)
+			EXIT1(return -EFAULT);
+		param.type = NdisParameterString;
+		RtlInitAnsiString(&ansi, setting);
+		if (RtlAnsiStringToUnicodeString(&key, &ansi,
+						 TRUE) != STATUS_SUCCESS) {
+			RtlFreeUnicodeString(&param.data.string);
+			EXIT1(return -EINVAL);
+		}
+		NdisWriteConfiguration(&res, wnd->nmb, &key, &param);
+		RtlFreeUnicodeString(&key);
+		RtlFreeUnicodeString(&param.data.string);
+		if (res != NDIS_STATUS_SUCCESS)
+			return -EFAULT;
+	}
+	return count;
+}
+
+PROC_DECLARE_RW(settings)
+
+int wrap_procfs_add_ndis_device(struct ndis_device *wnd)
+{
+	int ret;
+
+	if (wrap_procfs_entry == NULL)
+		return -ENOMEM;
+
+	if (wnd->procfs_iface) {
+		ERROR("%s already registered?", wnd->net_dev->name);
+		return -EINVAL;
+	}
+	wnd->procfs_iface = proc_mkdir(wnd->net_dev->name, wrap_procfs_entry);
+	if (wnd->procfs_iface == NULL) {
+		ERROR("couldn't create proc directory");
+		return -ENOMEM;
+	}
+	proc_set_user(wnd->procfs_iface, proc_kuid, proc_kgid);
+
+	ret = proc_make_entry_ro(hw, wnd->procfs_iface, wnd);
+	if (ret)
+		goto err_hw;
+
+	ret = proc_make_entry_ro(stats, wnd->procfs_iface, wnd);
+	if (ret)
+		goto err_stats;
+
+	ret = proc_make_entry_ro(encr, wnd->procfs_iface, wnd);
+	if (ret)
+		goto err_encr;
+
+	ret = proc_make_entry_rw(settings, wnd->procfs_iface, wnd);
+	if (ret)
+		goto err_settings;
+
+	return 0;
+
+err_settings:
+	remove_proc_entry("encr", wnd->procfs_iface);
+err_encr:
+	remove_proc_entry("stats", wnd->procfs_iface);
+err_stats:
+	remove_proc_entry("hw", wnd->procfs_iface);
+err_hw:
+	proc_remove(wnd->procfs_iface);
+	wnd->procfs_iface = NULL;
+	return -ENOMEM;
+}
+
+void wrap_procfs_remove_ndis_device(struct ndis_device *wnd)
+{
+	struct proc_dir_entry *procfs_iface = xchg(&wnd->procfs_iface, NULL);
+
+	if (procfs_iface == NULL)
+		return;
+	remove_proc_entry("hw", procfs_iface);
+	remove_proc_entry("stats", procfs_iface);
+	remove_proc_entry("encr", procfs_iface);
+	remove_proc_entry("settings", procfs_iface);
+	if (wrap_procfs_entry)
+		proc_remove(procfs_iface);
+}
+
+static int proc_debug_read(struct seq_file *sf, void *v)
+{
+#if ALLOC_DEBUG
+	enum alloc_type type;
+#endif
+
+	add_text("%d\n", debug);
+#if ALLOC_DEBUG
+	for (type = 0; type < ALLOC_TYPE_MAX; type++)
+		add_text("total size of allocations in %s: %d\n",
+			 alloc_type_name[type], alloc_size(type));
+#endif
+	return 0;
+}
+
+static ssize_t proc_debug_write(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	int i;
+	char setting[MAX_PROC_STR_LEN], *p;
+
+	if (count > MAX_PROC_STR_LEN)
+		return -EINVAL;
+
+	memset(setting, 0, sizeof(setting));
+	if (copy_from_user(setting, buf, count))
+		return -EFAULT;
+
+	if ((p = strchr(setting, '\n')))
+		*p = 0;
+
+	if ((p = strchr(setting, '=')))
+		*p = 0;
+
+	i = simple_strtol(setting, NULL, 10);
+	if (i >= 0 && i < 10)
+		debug = i;
+	else
+		return -EINVAL;
+	return count;
+}
+
+PROC_DECLARE_RW(debug)
+
+int wrap_procfs_init(void)
+{
+	int ret;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+	struct user_namespace *ns = current_user_ns();
+	proc_kuid = make_kuid(ns, proc_uid);
+	if (!uid_valid(proc_kuid)) {
+		ERROR("invalid UID\n");
+		return -EINVAL;
+	}
+	proc_kgid = make_kgid(ns, proc_gid);
+	if (!gid_valid(proc_kgid)) {
+		ERROR("invalid GID\n");
+		return -EINVAL;
+	}
+#endif
+
+	wrap_procfs_entry = proc_mkdir(DRIVER_NAME, proc_net_root);
+	if (wrap_procfs_entry == NULL) {
+		ERROR("couldn't create procfs directory");
+		return -ENOMEM;
+	}
+	proc_set_user(wrap_procfs_entry, proc_kuid, proc_kgid);
+
+	ret = proc_make_entry_rw(debug, wrap_procfs_entry, NULL);
+
+	return ret;
+}
+
+void wrap_procfs_remove(void)
+{
+	if (wrap_procfs_entry == NULL)
+		return;
+	remove_proc_entry("debug", wrap_procfs_entry);
+	proc_remove(wrap_procfs_entry);
+}
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/rtl.c linux-5.6.11-ndis/3rdparty/ndiswrapper/rtl.c
--- linux-5.6.11/3rdparty/ndiswrapper/rtl.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/rtl.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,715 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *  Copyright (C) 2006-2007 Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include "ntoskernel.h"
+#include "rtl_exports.h"
+
+wstdcall SIZE_T WIN_FUNC(RtlCompareMemory,3)
+	(const void *a, const void *b, SIZE_T len)
+{
+	size_t i;
+	char *x, *y;
+
+	ENTER1("%p %p %zd", a, b, len);
+	x = (char *)a;
+	y = (char *)b;
+	/* MSDN says this should return number of bytes that compare as
+	 * equal. This can be interpreted as either all bytes that are
+	 * equal in 'len' bytes or that only until the bytes compare as
+	 * not equal. Initially we had it the former way, but Realtek driver
+	 * doesn't like it that way - it takes many attempts to associate
+	 * with WPA. ReactOS returns the number of bytes that are equal
+	 * before the first differing byte.
+	 * According to lords at #reactos, that is the way it should be
+	 * and that msdn is wrong about it!
+	 */
+	for (i = 0; i < len && x[i] == y[i]; i++)
+		;
+	return i;
+}
+
+wstdcall void WIN_FUNC(RtlCopyMemory,3)
+	(void *dst, const void *src, SIZE_T length)
+{
+	memcpy(dst, src, length);
+}
+
+wstdcall void WIN_FUNC(RtlZeroMemory,2)
+	(void *dst, SIZE_T length)
+{
+	memset(dst, 0, length);
+}
+
+wstdcall void WIN_FUNC(RtlSecureZeroMemory,2)
+	(void *dst, SIZE_T length)
+{
+	memset(dst, 0, length);
+}
+
+wstdcall void WIN_FUNC(RtlFillMemory,3)
+	(void *dest, SIZE_T length, UCHAR fill)
+{
+	memset(dest, fill, length);
+}
+
+wstdcall void WIN_FUNC(RtlMoveMemory,3)
+	(void *dest, const void *src, SIZE_T length)
+{
+	memmove(dest, src, length);
+}
+
+wstdcall LONG WIN_FUNC(RtlCompareString,3)
+	(const struct ansi_string *s1, const struct ansi_string *s2,
+	 BOOLEAN case_insensitive)
+{
+	unsigned int len;
+	LONG ret = 0;
+	const char *p1, *p2;
+
+	ENTER2("");
+	len = min(s1->length, s2->length);
+	p1 = s1->buf;
+	p2 = s2->buf;
+	if (case_insensitive)
+		while (!ret && len--)
+			ret = toupper(*p1++) - toupper(*p2++);
+	else
+		while (!ret && len--)
+			ret = *p1++ - *p2++;
+	if (!ret)
+		ret = s1->length - s2->length;
+	EXIT2(return ret);
+}
+
+wstdcall LONG WIN_FUNC(RtlCompareUnicodeString,3)
+	(const struct unicode_string *s1, const struct unicode_string *s2,
+	 BOOLEAN case_insensitive)
+{
+	unsigned int len;
+	LONG ret = 0;
+	const wchar_t *p1, *p2;
+
+	ENTER2("");
+
+	len = min(s1->length, s2->length) / sizeof(wchar_t);
+	p1 = s1->buf;
+	p2 = s2->buf;
+	if (case_insensitive)
+		while (!ret && len--)
+			ret = toupper((u8)*p1++) - toupper((u8)*p2++);
+	else
+		while (!ret && len--)
+			ret = (u8)*p1++ - (u8)*p2++;
+	if (!ret)
+		ret = s1->length - s2->length;
+	TRACE2("len: %d, ret: %d", len, ret);
+	EXIT2(return ret);
+}
+
+wstdcall BOOLEAN WIN_FUNC(RtlEqualString,3)
+	(const struct ansi_string *s1, const struct ansi_string *s2,
+	 BOOLEAN case_insensitive)
+{
+	ENTER1("");
+	if (s1->length != s2->length)
+		return FALSE;
+	return !RtlCompareString(s1, s2, case_insensitive);
+}
+
+wstdcall BOOLEAN WIN_FUNC(RtlEqualUnicodeString,3)
+	(const struct unicode_string *s1, const struct unicode_string *s2,
+	 BOOLEAN case_insensitive)
+{
+	if (s1->length != s2->length)
+		return FALSE;
+	return !RtlCompareUnicodeString(s1, s2, case_insensitive);
+}
+
+wstdcall void WIN_FUNC(RtlCopyUnicodeString,2)
+	(struct unicode_string *dst, struct unicode_string *src)
+{
+	ENTER1("%p, %p", dst, src);
+	if (src && src->buf && dst->buf) {
+		dst->length = min(src->length, dst->max_length);
+		memcpy(dst->buf, src->buf, dst->length);
+		if (dst->length < dst->max_length)
+			dst->buf[dst->length / sizeof(dst->buf[0])] = 0;
+	} else
+		dst->length = 0;
+	EXIT1(return);
+}
+
+wstdcall void WIN_FUNC(RtlCopyString,2)
+	(struct ansi_string *dst, struct ansi_string *src)
+{
+	ENTER1("%p, %p", dst, src);
+	if (src && src->buf && dst->buf) {
+		dst->length = min(src->length, dst->max_length);
+		memcpy(dst->buf, src->buf, dst->length);
+		if (dst->length < dst->max_length)
+			dst->buf[dst->length] = 0;
+	} else
+		dst->length = 0;
+	EXIT1(return);
+}
+
+wstdcall NTSTATUS WIN_FUNC(RtlAppendUnicodeToString,2)
+	(struct unicode_string *dst, wchar_t *src)
+{
+	if (src) {
+		int len;
+		for (len = 0; src[len]; len++)
+			;
+		if (dst->length + (len * sizeof(dst->buf[0])) > dst->max_length)
+			return STATUS_BUFFER_TOO_SMALL;
+		memcpy(&dst->buf[dst->length], src, len * sizeof(dst->buf[0]));
+		dst->length += len * sizeof(dst->buf[0]);
+		if (dst->max_length > dst->length)
+			dst->buf[dst->length / sizeof(dst->buf[0])] = 0;
+	}
+	return STATUS_SUCCESS;
+}
+
+wstdcall NTSTATUS WIN_FUNC(RtlAppendUnicodeStringToString,2)
+	(struct unicode_string *dst, struct unicode_string *src)
+{
+	if (dst->max_length < src->length + dst->length)
+		return STATUS_BUFFER_TOO_SMALL;
+	if (src->length) {
+		memcpy(&dst->buf[dst->length], src->buf, src->length);
+		dst->length += src->length;
+		if (dst->max_length > dst->length)
+			dst->buf[dst->length / sizeof(dst->buf[0])] = 0;
+	}
+	EXIT2(return STATUS_SUCCESS);
+}
+
+wstdcall ULONG WIN_FUNC(RtlxAnsiStringToUnicodeSize,1)
+	(const struct ansi_string *string)
+{
+	int i;
+
+	for (i = 0; i < string->max_length && string->buf[i]; i++)
+		;
+	return i * sizeof(wchar_t);
+}
+
+wstdcall ULONG WIN_FUNC(RtlxUnicodeStringToAnsiSize,1)
+	(const struct unicode_string *string)
+{
+	int i;
+
+	for (i = 0; i < string->max_length && string->buf[i]; i++)
+		;
+	return i;
+}
+
+wstdcall NTSTATUS WIN_FUNC(RtlAnsiStringToUnicodeString,3)
+	(struct unicode_string *dst, const struct ansi_string *src,
+	 BOOLEAN alloc)
+{
+	int i, n;
+
+	n = RtlxAnsiStringToUnicodeSize(src);
+	TRACE2("%d, %d, %d, %d, %p", n, dst->max_length, src->length,
+	       src->max_length, src->buf);
+	if (alloc == TRUE) {
+#if 0
+		if (n == 0) {
+			dst->length = dst->max_length = 0;
+			dst->buf = NULL;
+			EXIT2(return STATUS_SUCCESS);
+		}
+#endif
+		dst->max_length = n + sizeof(dst->buf[0]);
+		dst->buf = ExAllocatePoolWithTag(NonPagedPool,
+						 dst->max_length, 0);
+		if (!dst->buf) {
+			dst->max_length = dst->length = 0;
+			EXIT2(return STATUS_NO_MEMORY);
+		}
+	} else if (dst->max_length < n)
+		EXIT2(return STATUS_BUFFER_TOO_SMALL);
+
+	dst->length = n;
+	n /= sizeof(dst->buf[0]);
+	for (i = 0; i < n; i++)
+		dst->buf[i] = src->buf[i];
+	if (i * sizeof(dst->buf[0]) < dst->max_length)
+		dst->buf[i] = 0;
+	TRACE2("dst: length: %d, max_length: %d, string: %p",
+	       dst->length, dst->max_length, src->buf);
+	EXIT2(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(RtlUnicodeStringToAnsiString,3)
+	(struct ansi_string *dst, const struct unicode_string *src,
+	 BOOLEAN alloc)
+{
+	int i, n;
+
+	n = RtlxUnicodeStringToAnsiSize(src);
+	TRACE2("%d, %d, %d, %d, %p", n, dst->max_length, src->length,
+	       src->max_length, src->buf);
+	if (alloc == TRUE) {
+#if 0
+		if (n == 0) {
+			dst->length = dst->max_length = 0;
+			dst->buf = NULL;
+			EXIT2(return STATUS_SUCCESS);
+		}
+#endif
+		dst->max_length = n + sizeof(dst->buf[0]);
+		dst->buf = ExAllocatePoolWithTag(NonPagedPool,
+						 dst->max_length, 0);
+		if (!dst->buf) {
+			dst->max_length = dst->length = 0;
+			EXIT1(return STATUS_NO_MEMORY);
+		}
+	} else if (dst->max_length < n)
+		EXIT2(return STATUS_BUFFER_TOO_SMALL);
+
+	dst->length = n;
+	for (i = 0; i < n; i++)
+		dst->buf[i] = src->buf[i];
+	if (i < dst->max_length)
+		dst->buf[i] = 0;
+	TRACE2("string: %p, len: %d(%d)", dst->buf, dst->length,
+	       dst->max_length);
+	EXIT2(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(RtlUnicodeStringToInteger,3)
+	(struct unicode_string *ustring, ULONG base, ULONG *value)
+{
+	int i, sign = 1;
+	ULONG res;
+	typeof(ustring->buf) string;
+
+	if (ustring->length == 0) {
+		*value = 0;
+		return STATUS_SUCCESS;
+	}
+
+	string = ustring->buf;
+	i = 0;
+	while (i < (ustring->length / sizeof(*string)) && string[i] == ' ')
+		i++;
+	if (string[i] == '+')
+		i++;
+	else if (string[i] == '-') {
+		i++;
+		sign = -1;
+	}
+	if (base == 0) {
+		base = 10;
+		if (i <= ((ustring->length / sizeof(*string)) - 2) &&
+		    string[i] == '0') {
+			i++;
+			if (string[i] == 'b') {
+				base = 2;
+				i++;
+			} else if (string[i] == 'o') {
+				base = 8;
+				i++;
+			} else if (string[i] == 'x') {
+				base = 16;
+				i++;
+			}
+		}
+	}
+	if (!(base == 2 || base == 8 || base == 10 || base == 16))
+		EXIT2(return STATUS_INVALID_PARAMETER);
+
+	for (res = 0; i < (ustring->length / sizeof(*string)); i++) {
+		int v;
+		if (isdigit((char)string[i]))
+			v = string[i] - '0';
+		else if (isxdigit((char)string[i]))
+			v = tolower((char)string[i]) - 'a' + 10;
+		else
+			v = base;
+		if (v >= base)
+			EXIT2(return STATUS_INVALID_PARAMETER);
+		res = res * base + v;
+	}
+	*value = sign * res;
+	EXIT3(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(RtlCharToInteger,3)
+	(const char *string, ULONG base, ULONG *value)
+{
+	int sign = 1;
+	ULONG res;
+
+	if (!string || !value)
+		EXIT2(return STATUS_INVALID_PARAMETER);
+	while (*string == ' ')
+		string++;
+	if (*string == '+')
+		string++;
+	else if (*string == '-') {
+		string++;
+		sign = -1;
+	}
+	if (base == 0) {
+		base = 10;
+		if (*string == '0') {
+			string++;
+			if (*string == 'b') {
+				base = 2;
+				string++;
+			} else if (*string == 'o') {
+				base = 8;
+				string++;
+			} else if (*string == 'x') {
+				base = 16;
+				string++;
+			}
+		}
+	}
+	if (!(base == 2 || base == 8 || base == 10 || base == 16))
+		EXIT2(return STATUS_INVALID_PARAMETER);
+
+	for (res = 0; *string; string++) {
+		int v;
+		if (isdigit(*string))
+			v = *string - '0';
+		else if (isxdigit(*string))
+			v = tolower(*string) - 'a' + 10;
+		else
+			v = base;
+		if (v >= base)
+			EXIT2(return STATUS_INVALID_PARAMETER);
+		res = res * base + v;
+	}
+	*value = sign * res;
+	EXIT3(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(RtlIntegerToUnicodeString,3)
+	(ULONG value, ULONG base, struct unicode_string *ustring)
+{
+	typeof(ustring->buf) buf = ustring->buf;
+	int i;
+
+	if (base == 0)
+		base = 10;
+	if (!(base == 2 || base == 8 || base == 10 || base == 16))
+		return STATUS_INVALID_PARAMETER;
+	for (i = 0; value && i < ustring->max_length / sizeof(*buf); i++) {
+		int r;
+		r = value % base;
+		value /= base;
+		if (r < 10)
+			buf[i] = r + '0';
+		else
+			buf[i] = r + 'a' - 10;
+	}
+	if (value)
+		return STATUS_BUFFER_OVERFLOW;
+	ustring->length = i * sizeof(*buf);
+	return STATUS_SUCCESS;
+}
+
+wstdcall LARGE_INTEGER WIN_FUNC(RtlConvertUlongToLargeInteger,1)
+	(ULONG ul)
+{
+	LARGE_INTEGER li = ul;
+	return li;
+}
+
+wfastcall USHORT WIN_FUNC(RtlUshortByteSwap,1)
+	(USHORT src)
+{
+	return __swab16(src);
+}
+
+wfastcall ULONG WIN_FUNC(RtlUlongByteSwap,1)
+	(ULONG src)
+{
+	/* ULONG is 32 bits for both 32-bit and 64-bit architectures */
+	return __swab32(src);
+}
+
+wstdcall NTSTATUS WIN_FUNC(RtlUpcaseUnicodeString,3)
+	(struct unicode_string *dst, struct unicode_string *src, BOOLEAN alloc)
+{
+	USHORT i, n;
+
+	if (alloc) {
+		dst->buf = ExAllocatePoolWithTag(NonPagedPool, src->length, 0);
+		if (dst->buf)
+			dst->max_length = src->length;
+		else
+			EXIT2(return STATUS_NO_MEMORY);
+	} else {
+		if (dst->max_length < src->length)
+			EXIT2(return STATUS_BUFFER_OVERFLOW);
+	}
+
+	n = src->length / sizeof(src->buf[0]);
+	for (i = 0; i < n; i++)
+		dst->buf[i] = toupper(src->buf[i]);
+
+	dst->length = src->length;
+	EXIT3(return STATUS_SUCCESS);
+}
+
+wstdcall void WIN_FUNC(RtlInitUnicodeString,2)
+	(struct unicode_string *dst, const wchar_t *src)
+{
+	ENTER2("%p", dst);
+	if (dst == NULL)
+		EXIT1(return);
+	if (src == NULL) {
+		dst->max_length = dst->length = 0;
+		dst->buf = NULL;
+	} else {
+		int i;
+		for (i = 0; (char)src[i]; i++)
+			;
+		dst->buf = (typeof(dst->buf))src;
+		dst->length = i * sizeof(dst->buf[0]);
+		dst->max_length = (i + 1) * sizeof(dst->buf[0]);
+	}
+	EXIT1(return);
+}
+
+wstdcall void WIN_FUNC(RtlInitAnsiString,2)
+	(struct ansi_string *dst, const char *src)
+{
+	ENTER2("%p", dst);
+	if (dst == NULL)
+		EXIT2(return);
+	if (src == NULL) {
+		dst->max_length = dst->length = 0;
+		dst->buf = NULL;
+	} else {
+		int i;
+		for (i = 0; src[i]; i++)
+			;
+		dst->buf = (typeof(dst->buf))src;
+		dst->length = i;
+		dst->max_length = i + 1;
+	}
+	TRACE2("%p", dst->buf);
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(RtlInitString,2)
+	(struct ansi_string *dst, const char *src)
+{
+	ENTER2("%p", dst);
+	RtlInitAnsiString(dst, src);
+	EXIT2(return);
+}
+
+wstdcall void WIN_FUNC(RtlFreeUnicodeString,1)
+	(struct unicode_string *string)
+{
+	ENTER2("%p", string);
+	if (string == NULL)
+		return;
+	if (string->buf)
+		ExFreePool(string->buf);
+	string->length = string->max_length = 0;
+	string->buf = NULL;
+	return;
+}
+
+wstdcall void WIN_FUNC(RtlFreeAnsiString,1)
+	(struct ansi_string *string)
+{
+	ENTER2("%p", string);
+	if (string == NULL)
+		return;
+	if (string->buf)
+		ExFreePool(string->buf);
+	string->length = string->max_length = 0;
+	string->buf = NULL;
+	return;
+}
+
+/* guid string is of the form: {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} */
+wstdcall NTSTATUS WIN_FUNC(RtlGUIDFromString,2)
+	(struct unicode_string *guid_string, struct guid *guid)
+{
+	struct ansi_string ansi;
+	NTSTATUS ret;
+	int i, j, k, l, m;
+
+	ret = RtlUnicodeStringToAnsiString(&ansi, guid_string, TRUE);
+	if (ret != STATUS_SUCCESS)
+		return ret;
+	if (ansi.length != 37 || ansi.buf[0] != '{' ||
+	    ansi.buf[36] != '}' || ansi.buf[9] != '-' ||
+	    ansi.buf[14] != '-' || ansi.buf[19] != '-' ||
+	    ansi.buf[24] != '-') {
+		RtlFreeAnsiString(&ansi);
+		EXIT2(return STATUS_INVALID_PARAMETER);
+	}
+	memcpy(&guid->data4, &ansi.buf[29], sizeof(guid->data3));
+	/* set end of data3 for scanf */
+	ansi.buf[29] = 0;
+	if (sscanf(&ansi.buf[1], "%x", &i) == 1 &&
+	    sscanf(&ansi.buf[10], "%x", &j) == 1 &&
+	    sscanf(&ansi.buf[15], "%x", &k) == 1 &&
+	    sscanf(&ansi.buf[20], "%x", &l) == 1 &&
+	    sscanf(&ansi.buf[25], "%x", &m) == 1) {
+		guid->data1 = (i << 16) | (j < 8) | k;
+		guid->data2 = l;
+		guid->data3 = m;
+		ret = STATUS_SUCCESS;
+	} else
+		ret = STATUS_INVALID_PARAMETER;
+	RtlFreeAnsiString(&ansi);
+	return ret;
+}
+
+wstdcall NTSTATUS WIN_FUNC(RtlQueryRegistryValues,5)
+	(ULONG relative, wchar_t *path, struct rtl_query_registry_table *tbl,
+	 void *context, void *env)
+{
+	struct ansi_string ansi;
+	struct unicode_string unicode;
+	NTSTATUS status, ret;
+	static int i = 0;
+
+	ENTER3("%x, %p", relative, tbl);
+//	TODO();
+
+	RtlInitUnicodeString(&unicode, path);
+	if (RtlUnicodeStringToAnsiString(&ansi, &unicode, TRUE) ==
+	    STATUS_SUCCESS) {
+		TRACE2("%s", ansi.buf);
+		RtlFreeAnsiString(&ansi);
+	}
+	ret = STATUS_SUCCESS;
+	for (; tbl->name; tbl++) {
+		RtlInitUnicodeString(&unicode, tbl->name);
+		if (RtlUnicodeStringToAnsiString(&ansi, &unicode, TRUE) ==
+		    STATUS_SUCCESS) {
+			TRACE2("name: %s", ansi.buf);
+			RtlFreeAnsiString(&ansi);
+		}
+		TRACE2("flags: %08X", tbl->flags);
+		if (tbl->flags == RTL_QUERY_REGISTRY_DIRECT) {
+			TRACE2("type: %08X", tbl->def_type);
+			if (tbl->def_type == REG_DWORD) {
+				/* Atheros USB driver needs this, but
+				 * don't know where and how to get its
+				 * value */
+				if (tbl->def_data) {
+					TRACE2("def_data: %x",
+					       *(int *)tbl->def_data);
+					*(DWORD *)tbl->context = 0x5f292a + i++;
+//						*(DWORD *)tbl->def_data;
+				} else
+					*(DWORD *)tbl->context = 0x2345dbe;
+			}
+		} else {
+			void *data;
+			ULONG type, length;
+
+			if (!tbl->query_func) {
+				ERROR("oops: no query_func");
+				ret = STATUS_INVALID_PARAMETER;
+				break;
+			}
+			if (tbl->flags & RTL_QUERY_REGISTRY_NOVALUE) {
+				data = NULL;
+				type = REG_NONE;
+				length = 0;
+			} else {
+				data = tbl->def_data;
+				type = tbl->def_type;
+				length = tbl->def_length;;
+			}
+			TRACE2("calling query_func: %p", tbl->query_func);
+			status = LIN2WIN6(tbl->query_func, tbl->name, type,
+					  data, length, context, env);
+			TRACE2("status: %08X", status);
+			if (status) {
+				if (status == STATUS_BUFFER_TOO_SMALL)
+					ret = STATUS_BUFFER_TOO_SMALL;
+				else
+					EXIT2(return STATUS_INVALID_PARAMETER);
+			}
+		}
+	}
+	EXIT3(return ret);
+}
+
+wstdcall NTSTATUS WIN_FUNC(RtlWriteRegistryValue,6)
+	(ULONG relative, wchar_t *path, wchar_t *name, ULONG type,
+	 void *data, ULONG length)
+{
+	struct ansi_string ansi;
+	struct unicode_string unicode;
+
+	ENTER3("%d", relative);
+	TODO();
+
+	RtlInitUnicodeString(&unicode, path);
+	if (RtlUnicodeStringToAnsiString(&ansi, &unicode, TRUE) ==
+	    STATUS_SUCCESS) {
+		TRACE2("%s", ansi.buf);
+		RtlFreeAnsiString(&ansi);
+	}
+	RtlInitUnicodeString(&unicode, name);
+	if (RtlUnicodeStringToAnsiString(&ansi, &unicode, TRUE) ==
+	    STATUS_SUCCESS) {
+		TRACE2("%s", ansi.buf);
+		RtlFreeAnsiString(&ansi);
+	}
+	EXIT5(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS WIN_FUNC(RtlDeleteRegistryValue,3)
+	(ULONG relative, wchar_t *path, wchar_t *name)
+{
+	return STATUS_SUCCESS;
+}
+
+wstdcall void WIN_FUNC(RtlAssert,4)
+	(char *failed_assertion, char *file_name, ULONG line_num, char *message)
+{
+	ERROR("assertion '%s' failed at %s line %d%s",
+	      failed_assertion, file_name, line_num, message ? message : "");
+	return;
+}
+
+wstdcall void WIN_FUNC(RtlUnwind,0)
+	(void)
+{
+	TODO();
+}
+
+wstdcall void WIN_FUNC(RtlRaiseException,1)
+	(void *exception_record)
+{
+	TODO();
+}
+
+wstdcall BOOLEAN WIN_FUNC(RtlIsServicePackVersionInstalled,1)
+	(ULONG version)
+{
+	/* Assume we have all service packs */
+	TRACE1("version: %d", version);
+	return TRUE;
+}
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/usb.c linux-5.6.11-ndis/3rdparty/ndiswrapper/usb.c
--- linux-5.6.11/3rdparty/ndiswrapper/usb.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/usb.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,1501 @@
+/*
+ *  Copyright (C) 2004 Jan Kiszka
+ *  Copyright (C) 2005 Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include "ndis.h"
+#include "usb.h"
+#include "usb_exports.h"
+
+#ifdef USB_DEBUG
+static unsigned int urb_id = 0;
+
+#define DUMP_WRAP_URB(wrap_urb, dir)					\
+	USBTRACE("urb %p (%d) %s: buf: %p, len: %d, pipe: 0x%x, %d",	\
+		 (wrap_urb)->urb, (wrap_urb)->id,			\
+		 (dir == USB_DIR_OUT) ? "going down" : "coming back",	\
+		 (wrap_urb)->urb->transfer_buffer,			\
+		 (wrap_urb)->urb->transfer_buffer_length,		\
+		 (wrap_urb)->urb->pipe, (wrap_urb)->urb->status)
+
+#define DUMP_URB_BUFFER(urb, dir)					\
+	while (debug >= 2) {						\
+		int i;							\
+		char msg[20], *t;					\
+		if (!urb->transfer_buffer)				\
+			break;						\
+		if (!((usb_pipein(urb->pipe) && dir == USB_DIR_IN) ||	\
+		      (usb_pipeout(urb->pipe) && dir == USB_DIR_OUT)))	\
+			break;						\
+		t = msg;						\
+		t += sprintf(t, "%d: ", (urb)->actual_length);		\
+		for (i = 0; i < urb->actual_length &&			\
+			     t < &msg[sizeof(msg) - 4]; i++)		\
+			t += sprintf(t, "%02X ",			\
+				     ((char *)urb->transfer_buffer)[i]); \
+		*t = 0;							\
+		USBTRACE("%s", msg);					\
+		break;							\
+	}
+
+#else
+
+#define DUMP_WRAP_URB(wrap_urb, dir) (void)0
+#define DUMP_URB_BUFFER(urb, dir) (void)0
+
+#endif
+
+#define CUR_ALT_SETTING(intf) (intf)->cur_altsetting
+
+#ifndef USB_CTRL_SET_TIMEOUT
+#define USB_CTRL_SET_TIMEOUT 5000
+#endif
+
+#ifndef USB_CTRL_GET_TIMEOUT
+#define USB_CTRL_GET_TIMEOUT 5000
+#endif
+
+#ifndef URB_NO_TRANSFER_DMA_MAP
+#define URB_NO_TRANSFER_DMA_MAP 0
+#endif
+
+/* wrap_urb->flags */
+/* transfer_buffer for urb is allocated; free it in wrap_free_urb */
+#define WRAP_URB_COPY_BUFFER 0x01
+
+static inline int wrap_cancel_urb(struct wrap_urb *wrap_urb)
+{
+	int ret;
+	USBTRACE("%p, %p, %d", wrap_urb, wrap_urb->urb, wrap_urb->state);
+	if (wrap_urb->state != URB_SUBMITTED)
+		USBEXIT(return -1);
+	ret = usb_unlink_urb(wrap_urb->urb);
+	USBTRACE("ret: %d", ret);
+	if (ret == -EINPROGRESS)
+		return 0;
+	else {
+		WARNING("unlink failed: %d", ret);
+		return ret;
+	}
+}
+
+#define URB_STATUS(wrap_urb) (wrap_urb->urb->status)
+
+static struct nt_list wrap_urb_complete_list;
+static spinlock_t wrap_urb_complete_list_lock;
+
+static struct work_struct wrap_urb_complete_work;
+static void wrap_urb_complete_worker(struct work_struct *dummy);
+
+static void kill_all_urbs(struct wrap_device *wd, int complete)
+{
+	struct nt_list *ent;
+	struct wrap_urb *wrap_urb;
+	KIRQL irql;
+
+	USBTRACE("%d", wd->usb.num_alloc_urbs);
+	while (1) {
+		IoAcquireCancelSpinLock(&irql);
+		ent = RemoveHeadList(&wd->usb.wrap_urb_list);
+		IoReleaseCancelSpinLock(irql);
+		if (!ent)
+			break;
+		wrap_urb = container_of(ent, struct wrap_urb, list);
+		if (wrap_urb->state == URB_SUBMITTED) {
+			WARNING("Windows driver %s didn't free urb: %p",
+				wd->driver->name, wrap_urb->urb);
+			if (!complete)
+				wrap_urb->urb->complete = NULL;
+			usb_kill_urb(wrap_urb->urb);
+		}
+		USBTRACE("%p, %p", wrap_urb, wrap_urb->urb);
+		usb_free_urb(wrap_urb->urb);
+		kfree(wrap_urb);
+	}
+	wd->usb.num_alloc_urbs = 0;
+}
+
+/* for a given Linux urb status code, return corresponding NT urb status */
+static USBD_STATUS wrap_urb_status(int urb_status)
+{
+	switch (urb_status) {
+	case 0:
+		return USBD_STATUS_SUCCESS;
+	case -EPROTO:
+		return USBD_STATUS_TIMEOUT;
+	case -EILSEQ:
+		return USBD_STATUS_CRC;
+	case -EPIPE:
+		return USBD_STATUS_INVALID_PIPE_HANDLE;
+	case -ECOMM:
+		return USBD_STATUS_DATA_OVERRUN;
+	case -ENOSR:
+		return USBD_STATUS_DATA_UNDERRUN;
+	case -EOVERFLOW:
+		return USBD_STATUS_BABBLE_DETECTED;
+	case -EREMOTEIO:
+		return USBD_STATUS_ERROR_SHORT_TRANSFER;;
+	case -ENODEV:
+	case -ESHUTDOWN:
+	case -ENOENT:
+		return USBD_STATUS_DEVICE_GONE;
+	case -ENOMEM:
+		return USBD_STATUS_NO_MEMORY;
+	case -EINVAL:
+		return USBD_STATUS_REQUEST_FAILED;
+	default:
+		return USBD_STATUS_NOT_SUPPORTED;
+	}
+}
+
+/* for a given USBD_STATUS, return its corresponding NTSTATUS (for irp) */
+static NTSTATUS nt_urb_irp_status(USBD_STATUS nt_urb_status)
+{
+	switch (nt_urb_status) {
+	case USBD_STATUS_SUCCESS:
+		return STATUS_SUCCESS;
+	case USBD_STATUS_DEVICE_GONE:
+		return STATUS_DEVICE_REMOVED;
+	case USBD_STATUS_PENDING:
+		return STATUS_PENDING;
+	case USBD_STATUS_NOT_SUPPORTED:
+		return STATUS_NOT_IMPLEMENTED;
+	case USBD_STATUS_NO_MEMORY:
+		return STATUS_NO_MEMORY;
+	case USBD_STATUS_REQUEST_FAILED:
+		return STATUS_NOT_SUPPORTED;
+	default:
+		return STATUS_FAILURE;
+	}
+}
+
+static void wrap_free_urb(struct urb *urb)
+{
+	struct wrap_urb *wrap_urb = urb->context;
+	struct irp *irp = wrap_urb->irp;
+	struct wrap_device *wd = IRP_WRAP_DEVICE(irp);
+
+	USBTRACE("freeing urb: %p", urb);
+	irp->cancel_routine = NULL;
+	IRP_WRAP_URB(irp) = NULL;
+	if (wrap_urb->flags & WRAP_URB_COPY_BUFFER) {
+		USBTRACE("freeing DMA buffer for URB: %p %p",
+			 urb, urb->transfer_buffer);
+		usb_free_coherent(wd->usb.udev, urb->transfer_buffer_length,
+				  urb->transfer_buffer, urb->transfer_dma);
+	}
+	kfree(urb->setup_packet);
+	if (wd->usb.num_alloc_urbs > MAX_ALLOCATED_URBS) {
+		IoAcquireCancelSpinLock(&irp->cancel_irql);
+		RemoveEntryList(&wrap_urb->list);
+		wd->usb.num_alloc_urbs--;
+		IoReleaseCancelSpinLock(irp->cancel_irql);
+		usb_free_urb(urb);
+		kfree(wrap_urb);
+	} else {
+		wrap_urb->state = URB_FREE;
+		wrap_urb->flags = 0;
+		wrap_urb->irp = NULL;
+	}
+	return;
+}
+
+void wrap_suspend_urbs(struct wrap_device *wd)
+{
+	/* TODO: do we need to cancel urbs? */
+	USBTRACE("%p, %d", wd, wd->usb.num_alloc_urbs);
+}
+
+void wrap_resume_urbs(struct wrap_device *wd)
+{
+	/* TODO: do we need to resubmit urbs? */
+	USBTRACE("%p, %d", wd, wd->usb.num_alloc_urbs);
+}
+
+wstdcall void wrap_cancel_irp(struct device_object *dev_obj, struct irp *irp)
+{
+	struct urb *urb;
+	struct wrap_urb *wrap_urb = IRP_WRAP_URB(irp);
+
+	/* NB: this function is called holding Cancel spinlock */
+	USBENTER("irp: %p", irp);
+	urb = wrap_urb->urb;
+	USBTRACE("canceling urb %p", urb);
+	if (wrap_cancel_urb(IRP_WRAP_URB(irp))) {
+		irp->cancel = FALSE;
+		ERROR("urb %p can't be canceled: %d", urb, wrap_urb->state);
+	} else
+		USBTRACE("urb %p canceled", urb);
+	IoReleaseCancelSpinLock(irp->cancel_irql);
+	return;
+}
+WIN_FUNC_DECL(wrap_cancel_irp,2)
+
+static struct urb *wrap_alloc_urb(struct irp *irp, unsigned int pipe,
+				  void *buf, unsigned int buf_len)
+{
+	struct urb *urb;
+	gfp_t alloc_flags;
+	struct wrap_urb *wrap_urb;
+	struct wrap_device *wd;
+
+	USBENTER("irp: %p", irp);
+	wd = IRP_WRAP_DEVICE(irp);
+
+	/* Don't interfere with URB cleanup by the kernel */
+	if (test_bit(HW_DISABLED, &wd->hw_status))
+		return NULL;
+
+	alloc_flags = irql_gfp();
+	IoAcquireCancelSpinLock(&irp->cancel_irql);
+	urb = NULL;
+	nt_list_for_each_entry(wrap_urb, &wd->usb.wrap_urb_list, list) {
+		if (cmpxchg(&wrap_urb->state, URB_FREE,
+			    URB_ALLOCATED) == URB_FREE) {
+			urb = wrap_urb->urb;
+			/* Clean URB but keep the refcount */
+			memset((char *)urb + sizeof(urb->kref), 0,
+			       sizeof(*urb) - sizeof(urb->kref));
+			break;
+		}
+	}
+	if (!urb) {
+		IoReleaseCancelSpinLock(irp->cancel_irql);
+		wrap_urb = kzalloc(sizeof(*wrap_urb), alloc_flags);
+		if (!wrap_urb) {
+			WARNING("couldn't allocate memory");
+			return NULL;
+		}
+		urb = usb_alloc_urb(0, alloc_flags);
+		if (!urb) {
+			WARNING("couldn't allocate urb");
+			kfree(wrap_urb);
+			return NULL;
+		}
+		IoAcquireCancelSpinLock(&irp->cancel_irql);
+		wrap_urb->urb = urb;
+		wrap_urb->state = URB_ALLOCATED;
+		InsertTailList(&wd->usb.wrap_urb_list, &wrap_urb->list);
+		wd->usb.num_alloc_urbs++;
+	}
+
+#ifdef URB_ASYNC_UNLINK
+	urb->transfer_flags |= URB_ASYNC_UNLINK;
+#elif defined(USB_ASYNC_UNLINK)
+	urb->transfer_flags |= USB_ASYNC_UNLINK;
+#endif
+	urb->context = wrap_urb;
+	wrap_urb->irp = irp;
+	IRP_WRAP_URB(irp) = wrap_urb;
+	/* called as Windows function */
+	irp->cancel_routine = WIN_FUNC_PTR(wrap_cancel_irp,2);
+	IoReleaseCancelSpinLock(irp->cancel_irql);
+	USBTRACE("urb: %p", urb);
+
+	urb->transfer_buffer_length = buf_len;
+	if (buf_len && buf && (!virt_addr_valid(buf)
+#if defined(CONFIG_HIGHMEM) || defined(CONFIG_HIGHMEM4G)
+			       || PageHighMem(virt_to_page(buf))
+#endif
+		    )) {
+		urb->transfer_buffer =
+			usb_alloc_coherent(wd->usb.udev, buf_len, alloc_flags,
+					 &urb->transfer_dma);
+		if (!urb->transfer_buffer) {
+			WARNING("couldn't allocate dma buf");
+			IoAcquireCancelSpinLock(&irp->cancel_irql);
+			irp->cancel_routine = NULL;
+			wrap_urb->state = URB_FREE;
+			wrap_urb->irp = NULL;
+			IRP_WRAP_URB(irp) = NULL;
+			IoReleaseCancelSpinLock(irp->cancel_irql);
+			return NULL;
+		}
+		if (urb->transfer_dma)
+			urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+		wrap_urb->flags |= WRAP_URB_COPY_BUFFER;
+		if (usb_pipeout(pipe))
+			memcpy(urb->transfer_buffer, buf, buf_len);
+		USBTRACE("DMA buf for urb %p: %p", urb, urb->transfer_buffer);
+	} else
+		urb->transfer_buffer = buf;
+	return urb;
+}
+
+static USBD_STATUS wrap_submit_urb(struct irp *irp)
+{
+	int ret;
+	struct wrap_urb *wrap_urb = IRP_WRAP_URB(irp);
+	struct urb *urb = wrap_urb->urb;
+	union nt_urb *nt_urb = IRP_URB(irp);
+
+#ifdef USB_DEBUG
+	if (wrap_urb->state != URB_ALLOCATED) {
+		ERROR("urb %p is in wrong state: %d",
+		      urb, wrap_urb->state);
+		NT_URB_STATUS(nt_urb) = USBD_STATUS_REQUEST_FAILED;
+		return NT_URB_STATUS(nt_urb);
+	}
+	wrap_urb->id = pre_atomic_add(urb_id, 1);
+#endif
+	DUMP_WRAP_URB(IRP_WRAP_URB(irp), USB_DIR_OUT);
+	irp->io_status.status = STATUS_PENDING;
+	irp->io_status.info = 0;
+	NT_URB_STATUS(nt_urb) = USBD_STATUS_PENDING;
+	IoMarkIrpPending(irp);
+	DUMP_URB_BUFFER(urb, USB_DIR_OUT);
+	USBTRACE("%p", urb);
+	wrap_urb->state = URB_SUBMITTED;
+	ret = usb_submit_urb(urb, irql_gfp());
+	if (ret) {
+		USBTRACE("ret: %d", ret);
+		wrap_free_urb(urb);
+		/* we assume that IRP was not in pending state before */
+		IoUnmarkIrpPending(irp);
+		NT_URB_STATUS(nt_urb) = wrap_urb_status(ret);
+		USBEXIT(return NT_URB_STATUS(nt_urb));
+	} else
+		USBEXIT(return USBD_STATUS_PENDING);
+}
+
+static void wrap_urb_complete(struct urb *urb ISR_PT_REGS_PARAM_DECL)
+{
+	struct irp *irp;
+	struct wrap_urb *wrap_urb;
+
+	wrap_urb = urb->context;
+	USBTRACE("%p (%p) completed", wrap_urb, urb);
+	irp = wrap_urb->irp;
+	DUMP_WRAP_URB(wrap_urb, USB_DIR_IN);
+	irp->cancel_routine = NULL;
+#ifdef USB_DEBUG
+	if (wrap_urb->state != URB_SUBMITTED) {
+		WARNING("urb %p in wrong state: %d (%d)", urb, wrap_urb->state,
+			urb->status);
+		return;
+	}
+#endif
+	wrap_urb->state = URB_COMPLETED;
+	spin_lock(&wrap_urb_complete_list_lock);
+	InsertTailList(&wrap_urb_complete_list, &wrap_urb->complete_list);
+	spin_unlock(&wrap_urb_complete_list_lock);
+	queue_work(ntos_wq, &wrap_urb_complete_work);
+}
+
+/* one worker for all devices */
+static void wrap_urb_complete_worker(struct work_struct *dummy)
+{
+	struct irp *irp;
+	struct urb *urb;
+	struct usbd_bulk_or_intr_transfer *bulk_int_tx;
+	struct usbd_vendor_or_class_request *vc_req;
+	union nt_urb *nt_urb;
+	struct wrap_urb *wrap_urb;
+	struct nt_list *ent;
+	unsigned long flags;
+
+	USBENTER("");
+	while (1) {
+		spin_lock_irqsave(&wrap_urb_complete_list_lock, flags);
+		ent = RemoveHeadList(&wrap_urb_complete_list);
+		spin_unlock_irqrestore(&wrap_urb_complete_list_lock, flags);
+		if (!ent)
+			break;
+		wrap_urb = container_of(ent, struct wrap_urb, complete_list);
+		urb = wrap_urb->urb;
+#ifdef USB_DEBUG
+		if (wrap_urb->state != URB_COMPLETED &&
+		    wrap_urb->state != URB_INT_UNLINKED)
+			WARNING("urb %p in wrong state: %d",
+				urb, wrap_urb->state);
+#endif
+		irp = wrap_urb->irp;
+		DUMP_IRP(irp);
+		nt_urb = IRP_URB(irp);
+		USBTRACE("urb: %p, nt_urb: %p, status: %d",
+			 urb, nt_urb, urb->status);
+		switch (urb->status) {
+		case 0:
+			/* successfully transferred */
+			irp->io_status.info = urb->actual_length;
+			if (nt_urb->header.function ==
+			    URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER) {
+				bulk_int_tx = &nt_urb->bulk_int_transfer;
+				bulk_int_tx->transfer_buffer_length =
+					urb->actual_length;
+				DUMP_URB_BUFFER(urb, USB_DIR_IN);
+				if ((wrap_urb->flags & WRAP_URB_COPY_BUFFER) &&
+				    usb_pipein(urb->pipe))
+					memcpy(bulk_int_tx->transfer_buffer,
+					       urb->transfer_buffer,
+					       urb->actual_length);
+			} else { // vendor or class request
+				vc_req = &nt_urb->vendor_class_request;
+				vc_req->transfer_buffer_length =
+					urb->actual_length;
+				DUMP_URB_BUFFER(urb, USB_DIR_IN);
+				if ((wrap_urb->flags & WRAP_URB_COPY_BUFFER) &&
+				    usb_pipein(urb->pipe))
+					memcpy(vc_req->transfer_buffer,
+					       urb->transfer_buffer,
+					       urb->actual_length);
+			}
+			NT_URB_STATUS(nt_urb) = USBD_STATUS_SUCCESS;
+			irp->io_status.status = STATUS_SUCCESS;
+			break;
+		case -ENOENT:
+		case -ECONNRESET:
+			/* urb canceled */
+			irp->io_status.info = 0;
+			TRACE2("urb %p canceled", urb);
+			NT_URB_STATUS(nt_urb) = USBD_STATUS_SUCCESS;
+			irp->io_status.status = STATUS_CANCELLED;
+			break;
+		default:
+			TRACE2("irp: %p, urb: %p, status: %d/%d",
+				 irp, urb, urb->status, wrap_urb->state);
+			irp->io_status.info = 0;
+			NT_URB_STATUS(nt_urb) = wrap_urb_status(urb->status);
+			irp->io_status.status =
+				nt_urb_irp_status(NT_URB_STATUS(nt_urb));
+			break;
+		}
+		wrap_free_urb(urb);
+		IoCompleteRequest(irp, IO_NO_INCREMENT);
+	}
+	USBEXIT(return);
+}
+
+static USBD_STATUS wrap_bulk_or_intr_trans(struct irp *irp)
+{
+	struct usb_endpoint_descriptor *pipe_handle;
+	struct urb *urb;
+	unsigned int pipe;
+	struct usbd_bulk_or_intr_transfer *bulk_int_tx;
+	USBD_STATUS status;
+	struct wrap_device *wd = IRP_WRAP_DEVICE(irp);
+	struct usb_device *udev = wd->usb.udev;
+	union nt_urb *nt_urb = IRP_URB(irp);
+
+	bulk_int_tx = &nt_urb->bulk_int_transfer;
+	pipe_handle = bulk_int_tx->pipe_handle;
+	USBTRACE("flags: 0x%x, length: %u, buffer: %p, handle: %p",
+		 bulk_int_tx->transfer_flags,
+		 bulk_int_tx->transfer_buffer_length,
+		 bulk_int_tx->transfer_buffer, pipe_handle);
+
+	if (USBD_IS_BULK_PIPE(pipe_handle)) {
+		if (bulk_int_tx->transfer_flags & USBD_TRANSFER_DIRECTION_IN)
+			pipe = usb_rcvbulkpipe(udev,
+					       pipe_handle->bEndpointAddress);
+		else
+			pipe = usb_sndbulkpipe(udev,
+					       pipe_handle->bEndpointAddress);
+	} else {
+		if (bulk_int_tx->transfer_flags & USBD_TRANSFER_DIRECTION_IN)
+			pipe = usb_rcvintpipe(udev,
+					      pipe_handle->bEndpointAddress);
+		else
+			pipe = usb_sndintpipe(udev,
+					      pipe_handle->bEndpointAddress);
+	}
+
+	DUMP_IRP(irp);
+	urb = wrap_alloc_urb(irp, pipe, bulk_int_tx->transfer_buffer,
+			     bulk_int_tx->transfer_buffer_length);
+	if (!urb) {
+		ERROR("couldn't allocate urb");
+		return USBD_STATUS_NO_MEMORY;
+	}
+	if (usb_pipein(pipe) &&
+	    (!(bulk_int_tx->transfer_flags & USBD_SHORT_TRANSFER_OK))) {
+		USBTRACE("short not ok");
+		urb->transfer_flags |= URB_SHORT_NOT_OK;
+	}
+	if (usb_pipebulk(pipe)) {
+		usb_fill_bulk_urb(urb, udev, pipe, urb->transfer_buffer,
+				  bulk_int_tx->transfer_buffer_length,
+				  wrap_urb_complete, urb->context);
+		USBTRACE("submitting bulk urb %p on pipe 0x%x (ep 0x%x)",
+			 urb, urb->pipe, pipe_handle->bEndpointAddress);
+	} else {
+		usb_fill_int_urb(urb, udev, pipe, urb->transfer_buffer,
+				 bulk_int_tx->transfer_buffer_length,
+				 wrap_urb_complete, urb->context,
+				 pipe_handle->bInterval);
+		USBTRACE("submitting interrupt urb %p on pipe 0x%x (ep 0x%x), "
+			 "intvl: %d", urb, urb->pipe,
+			 pipe_handle->bEndpointAddress, pipe_handle->bInterval);
+	}
+	status = wrap_submit_urb(irp);
+	USBTRACE("status: %08X", status);
+	USBEXIT(return status);
+}
+
+static USBD_STATUS wrap_vendor_or_class_req(struct irp *irp)
+{
+	u8 req_type;
+	unsigned int pipe;
+	struct usbd_vendor_or_class_request *vc_req;
+	USBD_STATUS status;
+	struct urb *urb;
+	struct usb_ctrlrequest *dr;
+	struct wrap_device *wd = IRP_WRAP_DEVICE(irp);
+	struct usb_device *udev = wd->usb.udev;
+	union nt_urb *nt_urb = IRP_URB(irp);
+
+	vc_req = &nt_urb->vendor_class_request;
+	USBTRACE("bits: %x, req: %x, val: %08x, index: %08x, flags: %x,"
+		 "buf: %p, len: %d", vc_req->reserved_bits, vc_req->request,
+		 vc_req->value, vc_req->index, vc_req->transfer_flags,
+		 vc_req->transfer_buffer, vc_req->transfer_buffer_length);
+
+	USBTRACE("%x", nt_urb->header.function);
+	switch (nt_urb->header.function) {
+	case URB_FUNCTION_VENDOR_DEVICE:
+		req_type = USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+		break;
+	case URB_FUNCTION_VENDOR_INTERFACE:
+		req_type = USB_TYPE_VENDOR | USB_RECIP_INTERFACE;
+		break;
+	case URB_FUNCTION_VENDOR_ENDPOINT:
+		req_type = USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
+		break;
+	case URB_FUNCTION_VENDOR_OTHER:
+		req_type = USB_TYPE_VENDOR | USB_RECIP_OTHER;
+		break;
+	case URB_FUNCTION_CLASS_DEVICE:
+		req_type = USB_TYPE_CLASS | USB_RECIP_DEVICE;
+		break;
+	case URB_FUNCTION_CLASS_INTERFACE:
+		req_type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+		break;
+	case URB_FUNCTION_CLASS_ENDPOINT:
+		req_type = USB_TYPE_CLASS | USB_RECIP_ENDPOINT;
+		break;
+	case URB_FUNCTION_CLASS_OTHER:
+		req_type = USB_TYPE_CLASS | USB_RECIP_OTHER;
+		break;
+	default:
+		ERROR("unknown request type: %x", nt_urb->header.function);
+		req_type = 0;
+		break;
+	}
+
+	req_type |= vc_req->reserved_bits;
+	USBTRACE("req type: %08x", req_type);
+
+	if (vc_req->transfer_flags & USBD_TRANSFER_DIRECTION_IN) {
+		pipe = usb_rcvctrlpipe(udev, 0);
+		req_type |= USB_DIR_IN;
+		USBTRACE("pipe: %x, dir in", pipe);
+	} else {
+		pipe = usb_sndctrlpipe(udev, 0);
+		req_type |= USB_DIR_OUT;
+		USBTRACE("pipe: %x, dir out", pipe);
+	}
+	urb = wrap_alloc_urb(irp, pipe, vc_req->transfer_buffer,
+			     vc_req->transfer_buffer_length);
+	if (!urb) {
+		ERROR("couldn't allocate urb");
+		return USBD_STATUS_NO_MEMORY;
+	}
+
+	if (usb_pipein(pipe) &&
+	    (!(vc_req->transfer_flags & USBD_SHORT_TRANSFER_OK))) {
+		USBTRACE("short not ok");
+		urb->transfer_flags |= URB_SHORT_NOT_OK;
+	}
+
+	dr = kzalloc(sizeof(*dr), irql_gfp());
+	if (!dr) {
+		ERROR("couldn't allocate memory");
+		wrap_free_urb(urb);
+		return USBD_STATUS_NO_MEMORY;
+	}
+	dr->bRequestType = req_type;
+	dr->bRequest = vc_req->request;
+	dr->wValue = cpu_to_le16(vc_req->value);
+	dr->wIndex = cpu_to_le16((u16)vc_req->index);
+	dr->wLength = cpu_to_le16((u16)urb->transfer_buffer_length);
+
+	usb_fill_control_urb(urb, udev, pipe, (unsigned char *)dr,
+			     urb->transfer_buffer, urb->transfer_buffer_length,
+			     wrap_urb_complete, urb->context);
+	status = wrap_submit_urb(irp);
+	USBTRACE("status: %08X", status);
+	USBEXIT(return status);
+}
+
+static USBD_STATUS wrap_reset_pipe(struct usb_device *udev, struct irp *irp)
+{
+	int ret;
+	union nt_urb *nt_urb;
+	struct usb_endpoint_descriptor *pipe_handle;
+	unsigned int pipe1, pipe2;
+
+	nt_urb = IRP_URB(irp);
+	pipe_handle = nt_urb->pipe_req.pipe_handle;
+	/* TODO: not clear if both directions should be cleared? */
+	if (USBD_IS_BULK_PIPE(pipe_handle)) {
+		pipe1 = usb_rcvbulkpipe(udev, pipe_handle->bEndpointAddress);
+		pipe2 = usb_sndbulkpipe(udev, pipe_handle->bEndpointAddress);
+	} else if (USBD_IS_INT_PIPE(pipe_handle)) {
+		pipe1 = usb_rcvintpipe(udev, pipe_handle->bEndpointAddress);
+		pipe2 = pipe1;
+	} else {
+		WARNING("invalid pipe %d", pipe_handle->bEndpointAddress);
+		return USBD_STATUS_INVALID_PIPE_HANDLE;
+	}
+	USBTRACE("ep: %d, pipe: 0x%x", pipe_handle->bEndpointAddress, pipe1);
+	ret = usb_clear_halt(udev, pipe1);
+	if (ret)
+		USBTRACE("resetting pipe %d failed: %d", pipe1, ret);
+	if (pipe2 != pipe1) {
+		ret = usb_clear_halt(udev, pipe2);
+		if (ret)
+			USBTRACE("resetting pipe %d failed: %d", pipe2, ret);
+	}
+//	return wrap_urb_status(ret);
+	return USBD_STATUS_SUCCESS;
+}
+
+static USBD_STATUS wrap_abort_pipe(struct usb_device *udev, struct irp *irp)
+{
+	union nt_urb *nt_urb;
+	struct usb_endpoint_descriptor *pipe_handle;
+	struct wrap_urb *wrap_urb;
+	struct wrap_device *wd;
+	KIRQL irql;
+
+	wd = IRP_WRAP_DEVICE(irp);
+	nt_urb = IRP_URB(irp);
+	pipe_handle = nt_urb->pipe_req.pipe_handle;
+	USBENTER("%p, %x", irp, pipe_handle->bEndpointAddress);
+	IoAcquireCancelSpinLock(&irql);
+	nt_list_for_each_entry(wrap_urb, &wd->usb.wrap_urb_list, list) {
+		USBTRACE("%p, %p, %d, %x, %x", wrap_urb, wrap_urb->urb,
+			 wrap_urb->state, wrap_urb->urb->pipe,
+			 usb_pipeendpoint(wrap_urb->urb->pipe));
+		/* for WG111T driver, urbs for endpoint 0 should also
+		 * be canceled */
+		if ((usb_pipeendpoint(wrap_urb->urb->pipe) ==
+		     pipe_handle->bEndpointAddress) ||
+		    (usb_pipeendpoint(wrap_urb->urb->pipe) == 0)) {
+			if (wrap_cancel_urb(wrap_urb) == 0)
+				USBTRACE("canceled wrap_urb: %p", wrap_urb);
+		}
+	}
+	IoReleaseCancelSpinLock(irql);
+	NT_URB_STATUS(nt_urb) = USBD_STATUS_CANCELED;
+	USBEXIT(return USBD_STATUS_SUCCESS);
+}
+
+static USBD_STATUS wrap_set_clear_feature(struct usb_device *udev,
+					  struct irp *irp)
+{
+	union nt_urb *nt_urb;
+	struct urb_control_feature_request *feat_req;
+	int ret = 0;
+	__u8 request, type;
+	__u16 feature;
+
+	nt_urb = IRP_URB(irp);
+	feat_req = &nt_urb->feat_req;
+	feature = feat_req->feature_selector;
+	switch (nt_urb->header.function) {
+	case URB_FUNCTION_SET_FEATURE_TO_DEVICE:
+		request = USB_REQ_SET_FEATURE;
+		type = USB_DT_DEVICE;
+		break;
+	case URB_FUNCTION_SET_FEATURE_TO_INTERFACE:
+		request = USB_REQ_SET_FEATURE;
+		type = USB_DT_INTERFACE;
+		break;
+	case URB_FUNCTION_SET_FEATURE_TO_ENDPOINT:
+		request = USB_REQ_SET_FEATURE;
+		type = USB_DT_ENDPOINT;
+		break;
+	case URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE:
+		request = USB_REQ_CLEAR_FEATURE;
+		type = USB_DT_DEVICE;
+		break;
+	case URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE:
+		request = USB_REQ_CLEAR_FEATURE;
+		type = USB_DT_INTERFACE;
+		break;
+	case URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT:
+		request = USB_REQ_CLEAR_FEATURE;
+		type = USB_DT_ENDPOINT;
+		break;
+	default:
+		WARNING("invalid function: %x", nt_urb->header.function);
+		NT_URB_STATUS(nt_urb) = USBD_STATUS_NOT_SUPPORTED;
+		return NT_URB_STATUS(nt_urb);
+	}
+	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), request, type,
+			      feature, feat_req->index, NULL, 0, 1000);
+	NT_URB_STATUS(nt_urb) = wrap_urb_status(ret);
+	USBEXIT(return NT_URB_STATUS(nt_urb));
+}
+
+static USBD_STATUS wrap_get_status_request(struct usb_device *udev,
+					   struct irp *irp)
+{
+	union nt_urb *nt_urb;
+	struct urb_control_get_status_request *status_req;
+	int ret = 0;
+	__u8 type;
+
+	nt_urb = IRP_URB(irp);
+	status_req = &nt_urb->status_req;
+	switch (nt_urb->header.function) {
+	case URB_FUNCTION_GET_STATUS_FROM_DEVICE:
+		type = USB_RECIP_DEVICE;
+		break;
+	case URB_FUNCTION_GET_STATUS_FROM_INTERFACE:
+		type = USB_RECIP_INTERFACE;
+		break;
+	case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT:
+		type = USB_RECIP_ENDPOINT;
+		break;
+	default:
+		WARNING("invalid function: %x", nt_urb->header.function);
+		NT_URB_STATUS(nt_urb) = USBD_STATUS_NOT_SUPPORTED;
+		return NT_URB_STATUS(nt_urb);
+	}
+	assert(status_req->transfer_buffer_length == sizeof(u16));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+	ret = usb_get_std_status(udev, type, status_req->index,
+			         status_req->transfer_buffer);
+#else
+	ret = usb_get_status(udev, type, status_req->index,
+			     status_req->transfer_buffer);
+#endif
+	if (ret >= 0) {
+		assert(ret <= status_req->transfer_buffer_length);
+		status_req->transfer_buffer_length = ret;
+		NT_URB_STATUS(nt_urb) = USBD_STATUS_SUCCESS;
+	} else
+		NT_URB_STATUS(nt_urb) = wrap_urb_status(ret);
+	USBEXIT(return NT_URB_STATUS(nt_urb));
+}
+
+static void set_intf_pipe_info(struct wrap_device *wd,
+			       struct usb_interface *usb_intf,
+			       struct usbd_interface_information *intf)
+{
+	int i;
+	struct usb_endpoint_descriptor *ep;
+	struct usbd_pipe_information *pipe;
+
+	for (i = 0; i < CUR_ALT_SETTING(usb_intf)->desc.bNumEndpoints; i++) {
+		ep = &(CUR_ALT_SETTING(usb_intf)->endpoint[i]).desc;
+		if (i >= intf->bNumEndpoints) {
+			ERROR("intf %p has only %d endpoints, "
+			      "ignoring endpoints above %d",
+			      intf, intf->bNumEndpoints, i);
+			break;
+		}
+		pipe = &intf->pipes[i];
+
+		if (pipe->flags & USBD_PF_CHANGE_MAX_PACKET)
+			USBTRACE("pkt_sz: %d: %d", pipe->wMaxPacketSize,
+				 pipe->max_tx_size);
+		USBTRACE("driver wants max_tx_size to %d",
+			 pipe->max_tx_size);
+
+		pipe->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
+		pipe->bEndpointAddress = ep->bEndpointAddress;
+		pipe->type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+		if (pipe->type == UsbdPipeTypeInterrupt) {
+			/* Windows and Linux differ in how the
+			 * bInterval is interpreted */
+			/* for low speed:
+			   interval (Windows) -> frames per ms (Linux)
+			   0 to 15    -> 8
+			   16 to 35   -> 16
+			   36 to 255  -> 32
+
+			   for full speed: interval -> frames per ms
+			   1          -> 1
+			   2 to 3     -> 2
+			   4 to 7     -> 4
+			   8 to 15    -> 8
+			   16 to 31   -> 16
+			   32 to 255  -> 32
+
+			   for high speed: interval -> microframes
+			   1          -> 1
+			   2          -> 2
+			   3          -> 4
+			   4          -> 8
+			   5          -> 16
+			   6          -> 32
+			   7 to 255   -> 32
+			*/
+			if (wd->usb.udev->speed == USB_SPEED_LOW)
+				pipe->bInterval = ep->bInterval + 5;
+			else if (wd->usb.udev->speed == USB_SPEED_FULL)
+				pipe->bInterval = ep->bInterval;
+			else {
+				int j, k;
+				for (j = k = 1; j < ep->bInterval; k++)
+					j *= 2;
+				pipe->bInterval = k;
+			}
+		}
+		pipe->handle = ep;
+		USBTRACE("%d: ep 0x%x, type %d, pkt_sz %d, intv %d (%d),"
+			 "type: %d, handle %p", i, ep->bEndpointAddress,
+			 ep->bmAttributes, pipe->wMaxPacketSize, ep->bInterval,
+			 pipe->bInterval, pipe->type, pipe->handle);
+	}
+}
+
+static USBD_STATUS wrap_select_configuration(struct wrap_device *wd,
+					     union nt_urb *nt_urb,
+					     struct irp *irp)
+{
+	int i, ret;
+	struct usbd_select_configuration *sel_conf;
+	struct usb_device *udev;
+	struct usbd_interface_information *intf;
+	struct usb_config_descriptor *config;
+	struct usb_interface *usb_intf;
+
+	udev = wd->usb.udev;
+	sel_conf = &nt_urb->select_conf;
+	config = sel_conf->config;
+	USBTRACE("%p", config);
+	if (config == NULL) {
+		kill_all_urbs(wd, 1);
+		ret = usb_reset_configuration(udev);
+		return wrap_urb_status(ret);
+	}
+
+	USBTRACE("conf: %d, type: %d, length: %d, numif: %d, attr: %08x",
+		 config->bConfigurationValue, config->bDescriptorType,
+		 config->wTotalLength, config->bNumInterfaces,
+		 config->bmAttributes);
+	ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			      USB_REQ_SET_CONFIGURATION, 0,
+			      config->bConfigurationValue, 0,
+			      NULL, 0, USB_CTRL_SET_TIMEOUT);
+	if (ret < 0) {
+		ERROR("ret: %d", ret);
+		return wrap_urb_status(ret);
+	}
+	sel_conf->handle = udev->actconfig;
+	intf = &sel_conf->intf;
+	for (i = 0; i < config->bNumInterfaces && intf->bLength > 0;
+	     i++, intf = (((void *)intf) + intf->bLength)) {
+
+		USBTRACE("intf: %d, alt setting: %d",
+			 intf->bInterfaceNumber, intf->bAlternateSetting);
+		ret = usb_set_interface(udev, intf->bInterfaceNumber,
+					intf->bAlternateSetting);
+		if (ret < 0) {
+			ERROR("failed with %d", ret);
+			return wrap_urb_status(ret);
+		}
+		usb_intf = usb_ifnum_to_if(udev, intf->bInterfaceNumber);
+		if (!usb_intf) {
+			ERROR("couldn't obtain ifnum");
+			return USBD_STATUS_REQUEST_FAILED;
+		}
+		USBTRACE("intf: %p, num ep: %d", intf, intf->bNumEndpoints);
+		set_intf_pipe_info(wd, usb_intf, intf);
+	}
+	return USBD_STATUS_SUCCESS;
+}
+
+static USBD_STATUS wrap_select_interface(struct wrap_device *wd,
+					 union nt_urb *nt_urb,
+					 struct irp *irp)
+{
+	int ret;
+	struct usbd_select_interface *sel_intf;
+	struct usb_device *udev;
+	struct usbd_interface_information *intf;
+	struct usb_interface *usb_intf;
+
+	udev = wd->usb.udev;
+	sel_intf = &nt_urb->select_intf;
+	intf = &sel_intf->intf;
+
+	ret = usb_set_interface(udev, intf->bInterfaceNumber,
+				intf->bAlternateSetting);
+	if (ret < 0) {
+		ERROR("failed with %d", ret);
+		return wrap_urb_status(ret);
+	}
+	usb_intf = usb_ifnum_to_if(udev, intf->bInterfaceNumber);
+	if (!usb_intf) {
+		ERROR("couldn't get interface information");
+		return USBD_STATUS_REQUEST_FAILED;
+	}
+	USBTRACE("intf: %p, num ep: %d", usb_intf, intf->bNumEndpoints);
+	set_intf_pipe_info(wd, usb_intf, intf);
+	return USBD_STATUS_SUCCESS;
+}
+
+static int wrap_usb_get_string(struct usb_device *udev, unsigned short langid,
+			       unsigned char index, void *buf, int size)
+{
+	int i, ret;
+	/* if langid is 0, return array of languages supported in
+	 * buf */
+	for (i = 0; i < 3; i++) {
+		ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+				      USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+				      (USB_DT_STRING << 8) + index, langid,
+				      buf, size, USB_CTRL_GET_TIMEOUT);
+		if (ret > 0 || ret == -EPIPE)
+			break;
+	}
+	return ret;
+}
+
+static USBD_STATUS wrap_get_descriptor(struct wrap_device *wd,
+				       union nt_urb *nt_urb, struct irp *irp)
+{
+	struct usbd_control_descriptor_request *control_desc;
+	int ret = 0;
+	struct usb_device *udev;
+
+	udev = wd->usb.udev;
+	control_desc = &nt_urb->control_desc;
+	USBTRACE("desctype = %d, descindex = %d, transfer_buffer = %p,"
+		 "transfer_buffer_length = %d", control_desc->desc_type,
+		 control_desc->index, control_desc->transfer_buffer,
+		 control_desc->transfer_buffer_length);
+
+	if (control_desc->desc_type == USB_DT_STRING) {
+		USBTRACE("langid: %x", control_desc->language_id);
+		ret = wrap_usb_get_string(udev, control_desc->language_id,
+					  control_desc->index,
+					  control_desc->transfer_buffer,
+					  control_desc->transfer_buffer_length);
+	} else {
+		ret = usb_get_descriptor(udev, control_desc->desc_type,
+					 control_desc->index,
+					 control_desc->transfer_buffer,
+					 control_desc->transfer_buffer_length);
+	}
+	if (ret < 0) {
+		USBTRACE("request %d failed: %d", control_desc->desc_type, ret);
+		control_desc->transfer_buffer_length = 0;
+		return wrap_urb_status(ret);
+	} else {
+		USBTRACE("ret: %08x", ret);
+		control_desc->transfer_buffer_length = ret;
+		irp->io_status.info = ret;
+		return USBD_STATUS_SUCCESS;
+	}
+}
+
+static USBD_STATUS wrap_process_nt_urb(struct irp *irp)
+{
+	USBD_STATUS status;
+	struct wrap_device *wd = IRP_WRAP_DEVICE(irp);
+	struct usb_device *udev = wd->usb.udev;
+	union nt_urb *nt_urb = IRP_URB(irp);
+
+	USBENTER("nt_urb = %p, irp = %p, length = %d, function = %x",
+		 nt_urb, irp, nt_urb->header.length, nt_urb->header.function);
+
+	if (test_bit(HW_DISABLED, &wd->hw_status)) {
+		status = USBD_STATUS_DEVICE_GONE;
+		NT_URB_STATUS(nt_urb) = status;
+		return status;
+	}
+
+	DUMP_IRP(irp);
+	switch (nt_urb->header.function) {
+		/* bulk/int and vendor/class urbs are submitted to
+		 * Linux USB core; if the call is successful, urb's
+		 * completion worker will return IRP later */
+	case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
+		USBTRACE("submitting bulk/int irp: %p", irp);
+		status = wrap_bulk_or_intr_trans(irp);
+		break;
+
+	case URB_FUNCTION_VENDOR_DEVICE:
+	case URB_FUNCTION_VENDOR_INTERFACE:
+	case URB_FUNCTION_VENDOR_ENDPOINT:
+	case URB_FUNCTION_VENDOR_OTHER:
+	case URB_FUNCTION_CLASS_DEVICE:
+	case URB_FUNCTION_CLASS_INTERFACE:
+	case URB_FUNCTION_CLASS_ENDPOINT:
+	case URB_FUNCTION_CLASS_OTHER:
+		USBTRACE("submitting vendor/class irp: %p", irp);
+		status = wrap_vendor_or_class_req(irp);
+		break;
+
+		/* rest are synchronous */
+	case URB_FUNCTION_SELECT_CONFIGURATION:
+		status = wrap_select_configuration(wd, nt_urb, irp);
+		NT_URB_STATUS(nt_urb) = status;
+		break;
+
+	case URB_FUNCTION_SELECT_INTERFACE:
+		status = wrap_select_interface(wd, nt_urb, irp);
+		NT_URB_STATUS(nt_urb) = status;
+		break;
+
+	case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
+		status = wrap_get_descriptor(wd, nt_urb, irp);
+		NT_URB_STATUS(nt_urb) = status;
+		break;
+
+	case URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL:
+		status = wrap_reset_pipe(udev, irp);
+		NT_URB_STATUS(nt_urb) = status;
+		break;
+
+	case URB_FUNCTION_ABORT_PIPE:
+		status = wrap_abort_pipe(udev, irp);
+		break;
+
+	case URB_FUNCTION_SET_FEATURE_TO_DEVICE:
+	case URB_FUNCTION_SET_FEATURE_TO_INTERFACE:
+	case URB_FUNCTION_SET_FEATURE_TO_ENDPOINT:
+	case URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE:
+	case URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE:
+	case URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT:
+		status = wrap_set_clear_feature(udev, irp);
+		break;
+
+	case URB_FUNCTION_GET_STATUS_FROM_DEVICE:
+	case URB_FUNCTION_GET_STATUS_FROM_INTERFACE:
+	case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT:
+		status = wrap_get_status_request(udev, irp);
+		break;
+
+	default:
+		ERROR("function %x not implemented", nt_urb->header.function);
+		status = NT_URB_STATUS(nt_urb) = USBD_STATUS_NOT_SUPPORTED;
+		break;
+	}
+	USBTRACE("status: %08X", status);
+	return status;
+}
+
+static USBD_STATUS wrap_reset_port(struct irp *irp)
+{
+	int ret, lock = 0;
+	struct wrap_device *wd;
+
+	wd = IRP_WRAP_DEVICE(irp);
+	USBENTER("%p, %p", wd, wd->usb.udev);
+	lock = usb_lock_device_for_reset(wd->usb.udev, wd->usb.intf);
+	if (lock < 0) {
+		WARNING("locking failed: %d", lock);
+//		return wrap_urb_status(lock);
+		return USBD_STATUS_SUCCESS;
+	}
+	ret = usb_reset_device(wd->usb.udev);
+	if (ret < 0)
+		USBTRACE("reset failed: %d", ret);
+	/* TODO: should reconfigure? */
+	if (lock)
+		usb_unlock_device(wd->usb.udev);
+//	return wrap_urb_status(ret);
+	return USBD_STATUS_SUCCESS;
+}
+
+static USBD_STATUS wrap_get_port_status(struct irp *irp)
+{
+	struct wrap_device *wd;
+	ULONG *status;
+	enum usb_device_state state;
+
+	wd = IRP_WRAP_DEVICE(irp);
+	USBENTER("%p, %p", wd, wd->usb.udev);
+	status = IoGetCurrentIrpStackLocation(irp)->params.others.arg1;
+	state = wd->usb.udev->state;
+	if (state != USB_STATE_NOTATTACHED &&
+	    state != USB_STATE_SUSPENDED) {
+		*status |= USBD_PORT_CONNECTED;
+		if (state == USB_STATE_CONFIGURED)
+			*status |= USBD_PORT_ENABLED;
+	}
+	USBTRACE("state: %d, *status: %08X", state, *status);
+	return USBD_STATUS_SUCCESS;
+}
+
+NTSTATUS wrap_submit_irp(struct device_object *pdo, struct irp *irp)
+{
+	struct io_stack_location *irp_sl;
+	struct wrap_device *wd;
+	USBD_STATUS status;
+	struct usbd_idle_callback *idle_callback;
+
+	USBENTER("%p, %p", pdo, irp);
+	wd = pdo->reserved;
+	if (wd->usb.intf == NULL) {
+		USBTRACE("%p", irp);
+		irp->io_status.status = STATUS_DEVICE_REMOVED;
+		irp->io_status.info = 0;
+		USBEXIT(return STATUS_DEVICE_REMOVED);
+	}
+	IRP_WRAP_DEVICE(irp) = wd;
+	irp_sl = IoGetCurrentIrpStackLocation(irp);
+	switch (irp_sl->params.dev_ioctl.code) {
+	case IOCTL_INTERNAL_USB_SUBMIT_URB:
+		status = wrap_process_nt_urb(irp);
+		break;
+	case IOCTL_INTERNAL_USB_RESET_PORT:
+		status = wrap_reset_port(irp);
+		break;
+	case IOCTL_INTERNAL_USB_GET_PORT_STATUS:
+		status = wrap_get_port_status(irp);
+		break;
+	case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION:
+		idle_callback = irp_sl->params.dev_ioctl.type3_input_buf;
+		(void)idle_callback;
+		USBTRACE("suspend function: %p", idle_callback->callback);
+		status = USBD_STATUS_NOT_SUPPORTED;
+		break;
+	default:
+		ERROR("ioctl %08X NOT IMPLEMENTED",
+		      irp_sl->params.dev_ioctl.code);
+		status = USBD_STATUS_NOT_SUPPORTED;
+		break;
+	}
+
+	USBTRACE("status: %08X", status);
+	if (status == USBD_STATUS_PENDING) {
+		/* don't touch this IRP - it may have been already
+		 * completed/returned */
+		return STATUS_PENDING;
+	} else {
+		irp->io_status.status = nt_urb_irp_status(status);
+		if (status != USBD_STATUS_SUCCESS)
+			irp->io_status.info = 0;
+		USBEXIT(return irp->io_status.status);
+	}
+}
+
+/* TODO: The example on msdn in reference section suggests that second
+ * argument should be an array of usbd_interface_information, but
+ * description and examples elsewhere suggest that it should be
+ * usbd_interface_list_entry structre. Which is correct? */
+
+wstdcall union nt_urb *WIN_FUNC(USBD_CreateConfigurationRequestEx,2)
+	(struct usb_config_descriptor *config,
+	 struct usbd_interface_list_entry *intf_list)
+{
+	int size, i, n;
+	struct usbd_interface_information *intf;
+	struct usbd_pipe_information *pipe;
+	struct usb_interface_descriptor *intf_desc;
+	struct usbd_select_configuration *select_conf;
+
+	USBENTER("config = %p, intf_list = %p", config, intf_list);
+
+	/* calculate size required; select_conf already has space for
+	 * one intf structure */
+	size = sizeof(*select_conf) - sizeof(*intf);
+	for (n = 0; n < config->bNumInterfaces; n++) {
+		i = intf_list[n].intf_desc->bNumEndpoints;
+		/* intf already has space for one pipe */
+		size += sizeof(*intf) + (i - 1) * sizeof(*pipe);
+	}
+	/* don't use kmalloc - driver frees it with ExFreePool */
+	select_conf = ExAllocatePoolWithTag(NonPagedPool, size,
+					    POOL_TAG('L', 'U', 'S', 'B'));
+	if (!select_conf) {
+		WARNING("couldn't allocate memory");
+		return NULL;
+	}
+	memset(select_conf, 0, size);
+	intf = &select_conf->intf;
+	select_conf->handle = config;
+	for (n = 0; n < config->bNumInterfaces && intf_list[n].intf_desc; n++) {
+		/* initialize 'intf' fields in intf_list so they point
+		 * to appropriate entry; these may be read/written by
+		 * driver after this function returns */
+		intf_list[n].intf = intf;
+		intf_desc = intf_list[n].intf_desc;
+
+		i = intf_desc->bNumEndpoints;
+		intf->bLength = sizeof(*intf) + (i - 1) * sizeof(*pipe);
+
+		intf->bInterfaceNumber = intf_desc->bInterfaceNumber;
+		intf->bAlternateSetting = intf_desc->bAlternateSetting;
+		intf->bInterfaceClass = intf_desc->bInterfaceClass;
+		intf->bInterfaceSubClass = intf_desc->bInterfaceSubClass;
+		intf->bInterfaceProtocol = intf_desc->bInterfaceProtocol;
+		intf->bNumEndpoints = intf_desc->bNumEndpoints;
+
+		pipe = &intf->pipes[0];
+		for (i = 0; i < intf->bNumEndpoints; i++) {
+			memset(&pipe[i], 0, sizeof(*pipe));
+			pipe[i].max_tx_size =
+				USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE;
+		}
+		intf->handle = intf_desc;
+		intf = (((void *)intf) + intf->bLength);
+	}
+	select_conf->header.function = URB_FUNCTION_SELECT_CONFIGURATION;
+	select_conf->header.length = size;
+	select_conf->config = config;
+	USBEXIT(return (union nt_urb *)select_conf);
+}
+
+WIN_SYMBOL_MAP("_USBD_CreateConfigurationRequestEx@8", USBD_CreateConfigurationRequestEx)
+
+wstdcall struct usb_interface_descriptor *
+WIN_FUNC(USBD_ParseConfigurationDescriptorEx,7)
+	(struct usb_config_descriptor *config, void *start,
+	 LONG bInterfaceNumber, LONG bAlternateSetting, LONG bInterfaceClass,
+	 LONG bInterfaceSubClass, LONG bInterfaceProtocol)
+{
+	void *pos;
+	struct usb_interface_descriptor *intf;
+
+	USBENTER("config = %p, start = %p, ifnum = %d, alt_setting = %d,"
+		 " class = %d, subclass = %d, proto = %d", config, start,
+		 bInterfaceNumber, bAlternateSetting, bInterfaceClass,
+		 bInterfaceSubClass, bInterfaceProtocol);
+
+	for (pos = start;
+	     pos < ((void *)config + le16_to_cpu(config->wTotalLength));
+	     pos += intf->bLength) {
+
+		intf = pos;
+
+		if ((intf->bDescriptorType == USB_DT_INTERFACE) &&
+		    ((bInterfaceNumber == -1) ||
+		     (intf->bInterfaceNumber == bInterfaceNumber)) &&
+		    ((bAlternateSetting == -1) ||
+		     (intf->bAlternateSetting == bAlternateSetting)) &&
+		    ((bInterfaceClass == -1) ||
+		     (intf->bInterfaceClass == bInterfaceClass)) &&
+		    ((bInterfaceSubClass == -1) ||
+		     (intf->bInterfaceSubClass == bInterfaceSubClass)) &&
+		    ((bInterfaceProtocol == -1) ||
+		     (intf->bInterfaceProtocol == bInterfaceProtocol))) {
+			USBTRACE("selected interface = %p", intf);
+			USBEXIT(return intf);
+		}
+	}
+	USBEXIT(return NULL);
+}
+
+WIN_SYMBOL_MAP("_USBD_ParseConfigurationDescriptorEx@28", USBD_ParseConfigurationDescriptorEx)
+
+wstdcall union nt_urb *WIN_FUNC(USBD_CreateConfigurationRequest,2)
+	(struct usb_config_descriptor *config, USHORT *size)
+{
+	union nt_urb *nt_urb;
+	struct usbd_interface_list_entry intf_list[2];
+	struct usb_interface_descriptor *intf_desc;
+
+	USBENTER("config = %p, urb_size = %p", config, size);
+
+	intf_desc = USBD_ParseConfigurationDescriptorEx(config, config, -1, -1,
+							-1, -1, -1);
+	intf_list[0].intf_desc = intf_desc;
+	intf_list[0].intf = NULL;
+	intf_list[1].intf_desc = NULL;
+	intf_list[1].intf = NULL;
+	nt_urb = USBD_CreateConfigurationRequestEx(config, intf_list);
+	if (!nt_urb)
+		return NULL;
+
+	*size = nt_urb->select_conf.header.length;
+	USBEXIT(return nt_urb);
+}
+
+wstdcall struct usb_interface_descriptor *
+WIN_FUNC(USBD_ParseConfigurationDescriptor,3)
+	(struct usb_config_descriptor *config, UCHAR bInterfaceNumber,
+	 UCHAR bAlternateSetting)
+{
+	return USBD_ParseConfigurationDescriptorEx(config, config,
+						   bInterfaceNumber,
+						   bAlternateSetting,
+						   -1, -1, -1);
+}
+
+wstdcall struct usb_descriptor_header *WIN_FUNC(USBD_ParseDescriptors,4)
+	(void *buf, ULONG length, struct usb_descriptor_header *descr,
+	 LONG type)
+{
+	while ((void *)descr < buf + length) {
+		if (descr->bDescriptorType == type)
+			return descr;
+		if (descr->bLength == 0)
+			break;
+		descr = (void *)descr + descr->bLength;
+	}
+	USBEXIT(return NULL);
+}
+
+WIN_SYMBOL_MAP("_USBD_ParseDescriptors@16", USBD_ParseDescriptors)
+
+wstdcall void WIN_FUNC(USBD_GetUSBDIVersion,1)
+	(struct usbd_version_info *version_info)
+{
+	/* this function is obsolete in Windows XP */
+	if (version_info) {
+		version_info->usbdi_version = USBDI_VERSION_XP;
+		/* TODO: how do we get this correctly? */
+		version_info->supported_usb_version = 0x110;
+	}
+	USBEXIT(return);
+}
+
+wstdcall void
+USBD_InterfaceGetUSBDIVersion(void *context,
+			      struct usbd_version_info *version_info,
+			      ULONG *hcd_capa)
+{
+	struct wrap_device *wd = context;
+
+	if (version_info) {
+		version_info->usbdi_version = USBDI_VERSION_XP;
+		if (wd->usb.udev->speed == USB_SPEED_HIGH)
+			version_info->supported_usb_version = 0x200;
+		else
+			version_info->supported_usb_version = 0x110;
+	}
+	*hcd_capa = USB_HCD_CAPS_SUPPORTS_RT_THREADS;
+	USBEXIT(return);
+}
+
+wstdcall BOOLEAN USBD_InterfaceIsDeviceHighSpeed(void *context)
+{
+	struct wrap_device *wd = context;
+
+	USBTRACE("wd: %p", wd);
+	if (wd->usb.udev->speed == USB_SPEED_HIGH)
+		USBEXIT(return TRUE);
+	else
+		USBEXIT(return FALSE);
+}
+
+wstdcall void USBD_InterfaceReference(void *context)
+{
+	USBTRACE("%p", context);
+	TODO();
+}
+
+wstdcall void USBD_InterfaceDereference(void *context)
+{
+	USBTRACE("%p", context);
+	TODO();
+}
+
+wstdcall NTSTATUS USBD_InterfaceQueryBusTime(void *context, ULONG *frame)
+{
+	struct wrap_device *wd = context;
+
+	*frame = usb_get_current_frame_number(wd->usb.udev);
+	USBEXIT(return STATUS_SUCCESS);
+}
+
+wstdcall NTSTATUS USBD_InterfaceSubmitIsoOutUrb(void *context,
+					       union nt_urb *nt_urb)
+{
+	/* TODO: implement this */
+	TODO();
+	USBEXIT(return STATUS_NOT_IMPLEMENTED);
+}
+
+wstdcall NTSTATUS
+USBD_InterfaceQueryBusInformation(void *context, ULONG level, void *buf,
+				  ULONG *buf_length, ULONG *buf_actual_length)
+{
+#if 0
+	struct wrap_device *wd = context;
+	struct usb_bus *bus = wd->usb.udev->bus;
+	struct usb_bus_information_level *bus_info = buf;
+#endif
+
+	TODO();
+	USBEXIT(return STATUS_NOT_IMPLEMENTED);
+}
+
+wstdcall NTSTATUS
+USBD_InterfaceLogEntry(void *context, ULONG driver_tag, ULONG enum_tag,
+		       ULONG p1, ULONG p2)
+{
+	ERROR("%p, %x, %x, %x, %x", context, driver_tag, enum_tag, p1, p2);
+	USBEXIT(return STATUS_SUCCESS);
+}
+
+NTSTATUS
+usb_query_interface(struct wrap_device *wd, struct io_stack_location *irp_sl)
+{
+	struct usbd_bus_interface_usbdi *intf =
+		(struct usbd_bus_interface_usbdi *)
+		irp_sl->params.query_intf.intf;
+
+	TRACE2("type: %x, size: %d, version: %d",
+	       irp_sl->params.query_intf.type->data1,
+	       irp_sl->params.query_intf.size,
+	       irp_sl->params.query_intf.version);
+	intf->Context = wd;
+	intf->InterfaceReference =
+		WIN_FUNC_PTR(USBD_InterfaceReference, 1);
+	intf->InterfaceDereference =
+		WIN_FUNC_PTR(USBD_InterfaceDereference, 1);
+	intf->GetUSBDIVersion =
+		WIN_FUNC_PTR(USBD_InterfaceGetUSBDIVersion, 3);
+	intf->QueryBusTime =
+		WIN_FUNC_PTR(USBD_InterfaceQueryBusTime, 2);
+	intf->SubmitIsoOutUrb =
+		WIN_FUNC_PTR(USBD_InterfaceSubmitIsoOutUrb, 2);
+	intf->QueryBusInformation =
+		WIN_FUNC_PTR(USBD_InterfaceQueryBusInformation, 5);
+	if (irp_sl->params.query_intf.version >= USB_BUSIF_USBDI_VERSION_1)
+		intf->IsDeviceHighSpeed =
+			WIN_FUNC_PTR(USBD_InterfaceIsDeviceHighSpeed, 1);
+	if (irp_sl->params.query_intf.version >= USB_BUSIF_USBDI_VERSION_2)
+		intf->LogEntry = WIN_FUNC_PTR(USBD_InterfaceLogEntry, 5);
+	return STATUS_SUCCESS;
+}
+
+int usb_init(void)
+{
+	InitializeListHead(&wrap_urb_complete_list);
+	spin_lock_init(&wrap_urb_complete_list_lock);
+	INIT_WORK(&wrap_urb_complete_work, wrap_urb_complete_worker);
+#ifdef USB_DEBUG
+	urb_id = 0;
+#endif
+	return 0;
+}
+
+void usb_exit(void)
+{
+	USBEXIT(return);
+}
+
+int usb_init_device(struct wrap_device *wd)
+{
+	InitializeListHead(&wd->usb.wrap_urb_list);
+	wd->usb.num_alloc_urbs = 0;
+	USBEXIT(return 0);
+}
+
+void usb_exit_device(struct wrap_device *wd)
+{
+	kill_all_urbs(wd, 0);
+	USBEXIT(return);
+}
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/usb.h linux-5.6.11-ndis/3rdparty/ndiswrapper/usb.h
--- linux-5.6.11/3rdparty/ndiswrapper/usb.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/usb.h	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,360 @@
+/*
+ *  Copyright (C) 2004 Jan Kiszka
+ *  Copyright (C) 2005 Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#ifndef _USB_H_
+#define _USB_H_
+
+#include "ntoskernel.h"
+
+#define IOCTL_INTERNAL_USB_SUBMIT_URB			0x00220003
+#define IOCTL_INTERNAL_USB_RESET_PORT			0x00220007
+#define IOCTL_INTERNAL_USB_GET_PORT_STATUS		0x00220013
+#define IOCTL_INTERNAL_USB_CYCLE_PORT			0x0022001F
+#define IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION	0x00220027
+
+#define URB_FUNCTION_SELECT_CONFIGURATION		0x0000
+#define URB_FUNCTION_SELECT_INTERFACE			0x0001
+#define URB_FUNCTION_ABORT_PIPE				0x0002
+#define URB_FUNCTION_TAKE_FRAME_LENGTH_CONTROL		0x0003
+#define URB_FUNCTION_RELEASE_FRAME_LENGTH_CONTROL	0x0004
+#define URB_FUNCTION_GET_FRAME_LENGTH			0x0005
+#define URB_FUNCTION_SET_FRAME_LENGTH			0x0006
+#define URB_FUNCTION_GET_CURRENT_FRAME_NUMBER		0x0007
+#define URB_FUNCTION_CONTROL_TRANSFER			0x0008
+#define URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER		0x0009
+#define URB_FUNCTION_ISOCH_TRANSFER			0x000A
+#define URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE		0x000B
+#define URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE		0x000C
+#define URB_FUNCTION_SET_FEATURE_TO_DEVICE		0x000D
+#define URB_FUNCTION_SET_FEATURE_TO_INTERFACE		0x000E
+#define URB_FUNCTION_SET_FEATURE_TO_ENDPOINT		0x000F
+#define URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE		0x0010
+#define URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE		0x0011
+#define URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT		0x0012
+#define URB_FUNCTION_GET_STATUS_FROM_DEVICE		0x0013
+#define URB_FUNCTION_GET_STATUS_FROM_INTERFACE		0x0014
+#define URB_FUNCTION_GET_STATUS_FROM_ENDPOINT		0x0015
+#define URB_FUNCTION_RESERVED_0X0016			0x0016
+#define URB_FUNCTION_VENDOR_DEVICE			0x0017
+#define URB_FUNCTION_VENDOR_INTERFACE			0x0018
+#define URB_FUNCTION_VENDOR_ENDPOINT			0x0019
+#define URB_FUNCTION_CLASS_DEVICE			0x001A
+#define URB_FUNCTION_CLASS_INTERFACE			0x001B
+#define URB_FUNCTION_CLASS_ENDPOINT			0x001C
+#define URB_FUNCTION_RESERVE_0X001D			0x001D
+#define URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL	0x001E
+#define URB_FUNCTION_CLASS_OTHER			0x001F
+#define URB_FUNCTION_VENDOR_OTHER			0x0020
+#define URB_FUNCTION_GET_STATUS_FROM_OTHER		0x0021
+#define URB_FUNCTION_CLEAR_FEATURE_TO_OTHER		0x0022
+#define URB_FUNCTION_SET_FEATURE_TO_OTHER		0x0023
+#define URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT	0x0024
+#define URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT		0x0025
+#define URB_FUNCTION_GET_CONFIGURATION			0x0026
+#define URB_FUNCTION_GET_INTERFACE			0x0027
+#define URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE	0x0028
+#define URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE	0x0029
+#define URB_FUNCTION_GET_MS_FEATURE_DESCRIPTOR		0x002A
+#define URB_FUNCTION_RESERVE_0X002B			0x002B
+#define URB_FUNCTION_RESERVE_0X002C			0x002C
+#define URB_FUNCTION_RESERVE_0X002D			0x002D
+#define URB_FUNCTION_RESERVE_0X002E			0x002E
+#define URB_FUNCTION_RESERVE_0X002F			0x002F
+// USB 2.0 calls start at 0x0030
+#define URB_FUNCTION_SYNC_RESET_PIPE			0x0030
+#define URB_FUNCTION_SYNC_CLEAR_STALL			0x0031
+#define URB_FUNCTION_CONTROL_TRANSFER_EX		0x0032
+
+#define USBD_PF_CHANGE_MAX_PACKET		0x00000001
+
+#define USBD_TRANSFER_DIRECTION_OUT		0
+#define USBD_TRANSFER_DIRECTION_IN		1
+
+#define USBD_SHORT_TRANSFER_OK			0x00000002
+#define USBD_START_ISO_TRANSFER_ASAP		0x00000004
+#define USBD_DEFAULT_PIPE_TRANSFER		0x00000008
+
+#define USBD_TRANSFER_DIRECTION(flags)		\
+	((flags) & USBD_TRANSFER_DIRECTION_IN)
+
+enum pipe_type {UsbdPipeTypeControl = USB_ENDPOINT_XFER_CONTROL,
+		UsbdPipeTypeIsochronous = USB_ENDPOINT_XFER_ISOC,
+		UsbdPipeTypeBulk = USB_ENDPOINT_XFER_BULK,
+		UsbdPipeTypeInterrupt = USB_ENDPOINT_XFER_INT};
+
+#define USBD_IS_BULK_PIPE(pipe_handle)					\
+	(((pipe_handle)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)	\
+	 == USB_ENDPOINT_XFER_BULK)
+
+#define USBD_IS_INT_PIPE(pipe_handle)					\
+	(((pipe_handle)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)	\
+	 == USB_ENDPOINT_XFER_INT)
+
+#define USBD_PORT_ENABLED			0x00000001
+#define USBD_PORT_CONNECTED			0x00000002
+
+typedef LONG USBD_STATUS;
+
+#define USBD_STATUS_SUCCESS			0x0
+#define USBD_STATUS_PENDING			0x40000000
+#define USBD_STATUS_CANCELED			0x00010000
+
+#define USBD_STATUS_CRC				0xC0000001
+#define USBD_STATUS_BTSTUFF			0xC0000002
+#define USBD_STATUS_DATA_TOGGLE_MISMATCH	0xC0000003
+#define USBD_STATUS_STALL_PID			0xC0000004
+#define USBD_STATUS_DEV_NOT_RESPONDING		0xC0000005
+#define USBD_STATUS_PID_CHECK_FAILURE		0xC0000006
+#define USBD_STATUS_UNEXPECTED_PID		0xC0000007
+#define USBD_STATUS_DATA_OVERRUN		0xC0000008
+#define USBD_STATUS_DATA_UNDERRUN		0xC0000009
+#define USBD_STATUS_RESERVED1			0xC000000A
+#define USBD_STATUS_RESERVED2			0xC000000B
+#define USBD_STATUS_BUFFER_OVERRUN		0xC000000C
+#define USBD_STATUS_BUFFER_UNDERRUN		0xC000000D
+#define USBD_STATUS_NOT_ACCESSED		0xC000000F
+#define USBD_STATUS_FIFO			0xC0000010
+#define USBD_STATUS_XACT_ERROR			0xC0000011
+#define USBD_STATUS_BABBLE_DETECTED		0xC0000012
+#define USBD_STATUS_DATA_BUFFER_ERROR		0xC0000013
+
+#define USBD_STATUS_NOT_SUPPORTED		0xC0000E00
+#define USBD_STATUS_BUFFER_TOO_SMALL		0xC0003000
+#define USBD_STATUS_TIMEOUT			0xC0006000
+#define USBD_STATUS_DEVICE_GONE			0xC0007000
+
+#define USBD_STATUS_NO_MEMORY			0x80000100
+#define USBD_STATUS_INVALID_URB_FUNCTION	0x80000200
+#define USBD_STATUS_INVALID_PARAMETER		0x80000300
+#define USBD_STATUS_REQUEST_FAILED		0x80000500
+#define USBD_STATUS_INVALID_PIPE_HANDLE		0x80000600
+#define USBD_STATUS_ERROR_SHORT_TRANSFER	0x80000900
+
+#define USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE	PAGE_SIZE
+
+struct urb_hcd_area {
+	void *reserved8[8];
+};
+
+struct usbd_pipe_information {
+	USHORT wMaxPacketSize;
+	UCHAR bEndpointAddress;
+	UCHAR bInterval;
+	enum pipe_type type;
+	struct usb_endpoint_descriptor *handle;
+	ULONG max_tx_size;
+	ULONG flags;
+};
+
+struct usbd_interface_information {
+	USHORT bLength;
+	UCHAR bInterfaceNumber;
+	UCHAR bAlternateSetting;
+	UCHAR bInterfaceClass;
+	UCHAR bInterfaceSubClass;
+	UCHAR bInterfaceProtocol;
+	UCHAR reserved;
+	void *handle;
+	ULONG bNumEndpoints;
+	struct usbd_pipe_information pipes[1];
+};
+
+struct usbd_interface_list_entry {
+	struct usb_interface_descriptor *intf_desc;
+	struct usbd_interface_information *intf;
+};
+
+struct nt_urb_header {
+	USHORT length;
+	USHORT function;
+	USBD_STATUS status;
+	void *usbd_dev_handle;
+	ULONG usbd_flags;
+};
+
+struct usbd_select_interface {
+	struct nt_urb_header header;
+	void *handle;
+	struct usbd_interface_information intf;
+};
+
+struct usbd_select_configuration {
+	struct nt_urb_header header;
+	struct usb_config_descriptor *config;
+	void *handle;
+	struct usbd_interface_information intf;
+};
+
+struct usbd_control_descriptor_request {
+	struct nt_urb_header header;
+	void *reserved;
+	ULONG reserved0;
+	ULONG transfer_buffer_length;
+	void *transfer_buffer;
+	struct mdl *mdl;
+	union nt_urb *urb_link;
+	struct urb_hcd_area hca;
+	USHORT reserved1;
+	UCHAR index;
+	UCHAR desc_type;
+	USHORT language_id;
+	USHORT reserved2;
+};
+
+struct usbd_bulk_or_intr_transfer {
+	struct nt_urb_header header;
+	struct usb_endpoint_descriptor *pipe_handle;
+	ULONG transfer_flags;
+	ULONG transfer_buffer_length;
+	void *transfer_buffer;
+	struct mdl *mdl;
+	union nt_urb *urb_link;
+	struct urb_hcd_area hca;
+};
+
+struct usbd_pipe_request {
+	struct nt_urb_header header;
+	struct usb_endpoint_descriptor *pipe_handle;
+};
+
+struct usbd_vendor_or_class_request {
+	struct nt_urb_header header;
+	void *reserved;
+	ULONG transfer_flags;
+	ULONG transfer_buffer_length;
+	void *transfer_buffer;
+	struct mdl *mdl;
+	union nt_urb *link;
+	struct urb_hcd_area hca;
+	UCHAR reserved_bits;
+	UCHAR request;
+	USHORT value;
+	USHORT index;
+	USHORT reserved1;
+};
+
+struct urb_control_feature_request {
+	struct nt_urb_header header;
+	void *reserved;
+	ULONG reserved2;
+	ULONG reserved3;
+	void *reserved4;
+	struct mdl *reserved5;
+	union nt_urb *link;
+	struct urb_hcd_area hca;
+	USHORT reserved0;
+	USHORT feature_selector;
+	USHORT index;
+	USHORT reserved1;
+};
+
+struct urb_control_get_status_request {
+	struct nt_urb_header header;
+	void *reserved;
+	ULONG reserved0;
+	ULONG transfer_buffer_length;
+	void *transfer_buffer;
+	struct mdl *mdl;
+	union nt_urb *link;
+	struct urb_hcd_area hca;
+	UCHAR reserved1[4];
+	USHORT index;
+	USHORT reserved2;
+};
+
+struct usbd_iso_packet_desc {
+	ULONG offset;
+	ULONG length;
+	USBD_STATUS status;
+};
+
+struct usbd_isochronous_transfer {
+	struct nt_urb_header header;
+	struct usb_endpoint_descriptor *pipe_handle;
+	ULONG transfer_flags;
+	ULONG transfer_buffer_length;
+	void *transfer_buffer;
+	struct mdl *mdl;
+	union nt_urb *urb_link;
+	struct urb_hcd_area hca;
+	ULONG start_frame;
+	ULONG number_of_packets;
+	ULONG error_count;
+	struct usbd_iso_packet_desc iso_packet[1];
+};
+
+union nt_urb {
+	struct nt_urb_header header;
+	struct usbd_select_interface select_intf;
+	struct usbd_select_configuration select_conf;
+	struct usbd_bulk_or_intr_transfer bulk_int_transfer;
+	struct usbd_control_descriptor_request control_desc;
+	struct usbd_vendor_or_class_request vendor_class_request;
+	struct usbd_isochronous_transfer isochronous;
+	struct usbd_pipe_request pipe_req;
+	struct urb_control_feature_request feat_req;
+	struct urb_control_get_status_request status_req;
+};
+
+struct usbd_bus_interface_usbdi {
+	USHORT Size;
+	USHORT Version;
+	void *Context;
+	void *InterfaceReference;
+	void *InterfaceDereference;
+	void *GetUSBDIVersion;
+	void *QueryBusTime;
+	void *SubmitIsoOutUrb;
+	void *QueryBusInformation;
+	/* version 1 and above have following field */
+	void *IsDeviceHighSpeed;
+	/* version 2 (and above) have following field */
+	void *LogEntry;
+};
+
+struct usbd_bus_information_level {
+	ULONG TotalBandwidth;
+	ULONG ConsumedBandwidth;
+	/* level 1 and above have following fields */
+	ULONG ControllerNameLength;
+	wchar_t ControllerName[1];
+};
+
+#define USBDI_VERSION_XP			0x00000500 // Windows XP
+#define USB_HCD_CAPS_SUPPORTS_RT_THREADS	0x00000001
+#define USB_BUSIF_USBDI_VERSION_0		0x0000
+#define USB_BUSIF_USBDI_VERSION_1		0x0001
+#define USB_BUSIF_USBDI_VERSION_2		0x0002
+
+struct usbd_version_info {
+	ULONG usbdi_version;
+	ULONG supported_usb_version;
+};
+
+struct usbd_idle_callback {
+	void *callback;
+	void *context;
+};
+
+#define NT_URB_STATUS(nt_urb) ((nt_urb)->header.status)
+
+NTSTATUS wrap_submit_irp(struct device_object *pdo, struct irp *irp);
+void wrap_suspend_urbs(struct wrap_device *wd);
+void wrap_resume_urbs(struct wrap_device *wd);
+NTSTATUS usb_query_interface(struct wrap_device *wd,
+			     struct io_stack_location *irp_sl);
+
+#endif /* USB_H */
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/win2lin_stubs.S linux-5.6.11-ndis/3rdparty/ndiswrapper/win2lin_stubs.S
--- linux-5.6.11/3rdparty/ndiswrapper/win2lin_stubs.S	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/win2lin_stubs.S	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,262 @@
+/*
+ *  Copyright (C) 2005 Karl Vogel, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <linux/version.h>
+
+#ifdef CONFIG_X86_64
+
+/*
+# Windows <---> Linux register usage conversion when calling functions
+# V = Volatile
+# NV = Non Volatile (needs to be saved)
+#
+#         Win                     Lin
+# ---------------------------------------
+# Rax    Return           V       Return          V
+# Rbx                     NV                      NV
+# Rcx     Arg1            V       Arg4            V
+# Rdx     Arg2            V       Arg3            V
+# Rsi                     NV      Arg2            V
+# Rdi                     NV      Arg1            V
+# Rsp                     NV                      NV
+# Rbp                     NV                      NV
+# R8      Arg3            V       Arg5            V
+# R9      Arg4            V       Arg6            V
+# R10                     V                       V
+# R11                     V                       V
+# R12                     NV                      NV
+# R13                     NV                      NV
+# R14                     NV                      NV
+# R15                     NV                      NV
+#
+# In addition, Linux uses %rax to indicate number of SSE registers used
+# when variadic functions are called. Since there is no way to obtain this
+# from Windows, for now, we just assume this is 0 (hence %rax is cleared).
+#
+# Windows pushes arguments 5 and higher onto stack in case of integer
+# variables and 4 and higher in case of floating point variables (passed
+# in SSE registers).
+
+In a windows function, the stackframe/registers look like this:
+
+# 0x0048 ....
+# 0x0040 arg8
+# 0x0038 arg7
+# 0x0030 arg6
+# 0x0028 arg5
+# 0x0020 shadow/spill space for arg4
+# 0x0018 shadow/spill space for arg3
+# 0x0010 shadow/spill space for arg2
+# 0x0008 shadow/spill space for arg1
+# 0x0000 ret
+
+# register spill space is same irrespective of number of arguments - even
+# if Windows function takes less than 4 arguments, 32 bytes above return
+# address is reserved for the function
+
+In Linux it should look like:
+
+# 0x0018 ....
+# 0x0010 arg8
+# 0x0008 arg7
+# 0x0000 ret
+
+*/
+
+	.text
+
+#define LINUX_REG_ARGS 6
+#define LOOP_THRESHOLD 9
+#define WORD_BYTES 8
+
+/*
+ * %rsi and %rdi must be saved because they are not saved by Linux calls, but
+ * Windows callers expect them to be saved.  %rbp is saved to create a stack
+ * frame, which can help with debugging.  We need to reserve space for an odd
+ * number of registers anyway to keep 16-bit alignment of the stack (one more
+ * position is used by the return address).
+ */
+#define SAVED_REGS 3
+
+/*
+ * When calling the Linux function, several registers are saved on the stack.
+ * When passing more than 6 arguments, arguments starting with argument 7 are
+ * pushed to the stack as well.
+ *
+ * We also need to allocate an additional word on the stack to keep it aligned
+ * to the 16-bit boundary if the number of saved arguments plus one (for the
+ * return address) is odd.
+ */
+
+/*
+ * Number of arguments we pass on stack to the Linux function.
+ * The value of true is -1 in assembler, so we multiply it by another true
+ * value.
+ */
+#define stack_args(argc)					\
+	((0 < 1) * (argc > LINUX_REG_ARGS) * (argc - LINUX_REG_ARGS))
+
+/* Full required change of stack pointer, in words */
+#define stack_words_raw(argc) (stack_args(argc) + SAVED_REGS + 1)
+
+/* Full actual change of stack pointer, in words (must be even) */
+#define stack_words_aligned(argc) ((stack_words_raw(argc) + 1) & ~1)
+
+/* Space allocated for Linux arguments on stack */
+#define stack_space(argc) \
+	((stack_words_aligned(argc) - SAVED_REGS - 1) * WORD_BYTES)
+
+/*
+ * win2lin_win_arg(N, ARGC) gives the address of the Windows argument N out of
+ * total ARGC after the stack has been prepared for the Linux function call.
+ *
+ * When called from Windows, the Nth argument is at (N * 8)(%rsp).  We add the
+ * stack space allocated by the Linux function to compensate for %rsp change.
+ *
+ * Don't call with N less than 5!
+ */
+#define win2lin_win_arg(n, argc) \
+	((n + SAVED_REGS) * WORD_BYTES + stack_space(argc))(%rsp)
+
+/*
+ * win2lin_lin_arg(N) gives the address of the Nth Linux argument on the extra
+ * Linux stack frame.  When more than 6 arguments are used, %rsp points to the
+ * 7th argument.  The Nth argument is therefore at ((N - 7) * 8)(%rsp).
+ *
+ * Don't call with N less than 7!
+ */
+#define win2lin_lin_arg(n) ((n - 1 - LINUX_REG_ARGS) * WORD_BYTES)(%rsp)
+
+/* Declare function LONGNAME, call function SHORTNAME with ARGC arguments */
+.macro win2linm longname, shortname, argc
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,5,0)
+	SYM_FUNC_START(\longname)
+#else
+	.type \longname, @function
+	ENTRY(\longname)
+#endif
+
+	/* Create a call frame - it's optional, but good for debugging */
+	.cfi_startproc
+	push %rbp
+	.cfi_def_cfa %rsp, 2 * WORD_BYTES
+	.cfi_offset %rbp, -2 * WORD_BYTES
+	mov %rsp, %rbp
+	.cfi_def_cfa %rbp, 2 * WORD_BYTES
+
+	/*
+	 * Registers %rdi and %rsi are volatile on Linux, but not on Windows,
+	 * so save them on the stack.
+	 */
+	push %rsi
+	push %rdi
+
+	/* Allocate extra stack space for arguments 7 and up */
+	sub $stack_space(\argc), %rsp
+
+	/*
+	 * Copy arguments 7 and up.  We do it early, before %rdi and %rsi
+	 * are used for arguments 1 and 2, so we don't have to save them.
+	 * We still need to save %rcx if using a string copy.
+	 */
+	.if (\argc < LOOP_THRESHOLD)
+		/* If a few arguments, copy them individually through %r11 */
+		.if (\argc >= 7)
+			mov win2lin_win_arg(7, \argc), %r11
+			mov %r11, win2lin_lin_arg(7)
+		.endif
+		.if (\argc >= 8)
+			mov win2lin_win_arg(8, \argc), %r11
+			mov %r11, win2lin_lin_arg(8)
+		.endif
+	.else
+		/* If there are many arguments, copy them in a loop */
+		/* Save arg1 to %r11 */
+		mov %rcx, %r11
+		/* Source and destination */
+		lea win2lin_win_arg(LINUX_REG_ARGS + 1, \argc), %rsi
+		lea win2lin_lin_arg(LINUX_REG_ARGS + 1), %rdi
+		/* Number of arguments to copy (%ecx zero-extends to %rcx) */
+		mov $(\argc - LINUX_REG_ARGS), %ecx
+		rep movsq
+		/* Restore arg1 directly to %rdi */
+		mov %r11, %rdi
+	.endif
+
+	/*
+	 * Argument 1 - %rcx on Windows, %rdi on Linux
+	 * Micro-optimization - if we used loop, arg1 is already in %rdi
+	 */
+	.if (\argc >= 1) && (\argc < LOOP_THRESHOLD)
+		mov %rcx, %rdi
+	.endif
+
+	/* Argument 2 - %rdx on Windows, %rsi on Linux */
+	.if (\argc >= 2)
+		mov %rdx, %rsi
+	.endif
+
+	/* Argument 3 - %r8 on Windows, %rdx on Linux */
+	.if (\argc >= 3)
+		mov %r8, %rdx
+	.endif
+
+	/* Argument 4 - %r9 on Windows, %rcx on Linux */
+	.if (\argc >= 4)
+		mov %r9, %rcx
+	.endif
+
+	/* Argument 5 - first argument on stack on Windows, %r8 Linux */
+	.if (\argc >= 5)
+		mov win2lin_win_arg(5, \argc), %r8
+	.endif
+
+	/* Argument 6 - second argument on stack on Windows, %r9 Linux */
+	.if (\argc >= 6)
+		mov win2lin_win_arg(6, \argc), %r9
+	.endif
+
+	/* %rax on Linux is the number of arguments in SSE registers (zero) */
+	xor %rax, %rax
+
+	/* Call the function */
+	call \shortname
+
+	/* Free stack space for arguments 7 and up */
+	add $stack_space(\argc), %rsp
+
+	/* Restore saved registers */
+	pop %rdi
+	pop %rsi
+
+	/* Return to Windows code */
+	leave
+	.cfi_def_cfa %rsp, WORD_BYTES
+	.cfi_restore %rbp
+	ret
+	.cfi_endproc
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,5,0)
+	SYM_FUNC_END(\longname)
+#else
+	.size \longname, (. - \longname)
+#endif
+.endm
+
+#define win2lin(name, argc) win2linm win2lin_ ## name ## _ ## argc, name, argc
+
+#include "win2lin_stubs.h"
+
+#endif	/* CONFIG_X86_64 */
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/winnt_types.h linux-5.6.11-ndis/3rdparty/ndiswrapper/winnt_types.h
--- linux-5.6.11/3rdparty/ndiswrapper/winnt_types.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/winnt_types.h	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,1701 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#ifndef _WINNT_TYPES_H_
+#define _WINNT_TYPES_H_
+
+#define TRUE				1
+#define FALSE				0
+
+#define PASSIVE_LEVEL			0
+#define APC_LEVEL			1
+#define DISPATCH_LEVEL			2
+#define DEVICE_LEVEL_BASE		4
+
+/* soft interrupts / bottom-half's are disabled at SOFT_IRQL */
+#define SOFT_IRQL			(DEVICE_LEVEL_BASE + 1)
+#define DIRQL				(DEVICE_LEVEL_BASE + 2)
+
+#define STATUS_WAIT_0			0
+#define STATUS_SUCCESS			0
+#define STATUS_ALERTED			0x00000101
+#define STATUS_TIMEOUT			0x00000102
+#define STATUS_PENDING			0x00000103
+#define STATUS_FAILURE			0xC0000001
+#define STATUS_NOT_IMPLEMENTED		0xC0000002
+#define STATUS_INVALID_PARAMETER	0xC000000D
+#define STATUS_INVALID_DEVICE_REQUEST	0xC0000010
+#define STATUS_MORE_PROCESSING_REQUIRED 0xC0000016
+#define STATUS_ACCESS_DENIED		0xC0000022
+#define STATUS_BUFFER_TOO_SMALL		0xC0000023
+#define STATUS_OBJECT_NAME_INVALID	0xC0000023
+#define STATUS_MUTANT_NOT_OWNED		0xC0000046
+#define STATUS_RESOURCES		0xC000009A
+#define STATUS_DELETE_PENDING		0xC0000056
+#define STATUS_INSUFFICIENT_RESOURCES	0xC000009A
+#define STATUS_NOT_SUPPORTED		0xC00000BB
+#define STATUS_INVALID_PARAMETER_2	0xC00000F0
+#define STATUS_NO_MEMORY		0xC0000017
+#define STATUS_CANCELLED		0xC0000120
+#define STATUS_DEVICE_REMOVED		0xC00002B6
+#define STATUS_DEVICE_NOT_CONNECTED	0xC000009D
+
+#define STATUS_BUFFER_OVERFLOW		0x80000005
+
+#define SL_PENDING_RETURNED		0x01
+#define SL_INVOKE_ON_CANCEL		0x20
+#define SL_INVOKE_ON_SUCCESS		0x40
+#define SL_INVOKE_ON_ERROR		0x80
+
+#define IRP_MJ_CREATE			0x00
+#define IRP_MJ_CREATE_NAMED_PIPE	0x01
+#define IRP_MJ_CLOSE			0x02
+#define IRP_MJ_READ			0x03
+#define IRP_MJ_WRITE			0x04
+
+#define IRP_MJ_DEVICE_CONTROL		0x0E
+#define IRP_MJ_INTERNAL_DEVICE_CONTROL	0x0F
+#define IRP_MJ_POWER			0x16
+#define IRP_MJ_SYSTEM_CONTROL		0x0E
+#define IRP_MJ_PNP			0x1b
+#define IRP_MJ_MAXIMUM_FUNCTION		0x1b
+
+#define IRP_MN_WAIT_WAKE		0x00
+#define IRP_MN_POWER_SEQUENCE		0x01
+#define IRP_MN_SET_POWER		0x02
+#define IRP_MN_QUERY_POWER		0x03
+
+#define IRP_MN_REGINFO			0x08
+#define IRP_MN_REGINFO_EX		0x0b
+
+#define IRP_MN_START_DEVICE		0x00
+#define IRP_MN_QUERY_REMOVE_DEVICE	0x01
+#define IRP_MN_REMOVE_DEVICE		0x02
+#define IRP_MN_CANCEL_REMOVE_DEVICE	0x03
+#define IRP_MN_STOP_DEVICE		0x04
+#define IRP_MN_QUERY_STOP_DEVICE	0x05
+#define IRP_MN_CANCEL_STOP_DEVICE	0x06
+#define IRP_MN_QUERY_DEVICE_RELATIONS	0x07
+#define IRP_MN_QUERY_INTERFACE		0x08
+
+#define IRP_BUFFERED_IO			0x00000010
+#define IRP_DEALLOCATE_BUFFER		0x00000020
+#define IRP_INPUT_OPERATION		0x00000040
+
+#define IRP_DEFFER_IO_COMPLETION	0x00000800
+
+#define THREAD_WAIT_OBJECTS		3
+#define MAX_WAIT_OBJECTS		64
+
+#define LOW_PRIORITY			0
+#define LOW_REALTIME_PRIORITY		16
+#define HIGH_PRIORITY			31
+#define MAXIMUM_PRIORITY		32
+
+#define PROCESSOR_FEATURE_MAX		64
+
+#define IO_NO_INCREMENT			0
+
+#define WMIREG_ACTION_REGISTER		1
+#define WMIREG_ACTION_DEREGISTER	2
+#define WMIREG_ACTION_REREGISTER	3
+#define WMIREG_ACTION_UPDATE_GUIDS	4
+
+#define WMIREGISTER			0
+#define WMIUPDATE			1
+
+#ifdef CONFIG_X86_64
+#define wstdcall
+#define wfastcall
+#define noregparm
+#define regparm3
+
+#define KI_USER_SHARED_DATA 0xfffff78000000000UL
+
+#else
+
+#define noregparm __attribute__((regparm(0)))
+#define regparm3 __attribute__((regparm(3)))
+#define wstdcall __attribute__((__stdcall__, regparm(0)))
+#if defined(__GNUC__) && ((__GNUC__ == 3 && __GNUC_MINOR__ > 3) || __GNUC__ > 3)
+#undef fastcall
+#define wfastcall __attribute__((fastcall))
+#else
+#error "gcc 3.4 or newer should be used for compiling this module"
+#endif
+
+#define KI_USER_SHARED_DATA 0xffdf0000
+
+#endif
+
+typedef u8	BOOLEAN;
+typedef u8	BYTE;
+typedef u8	*LPBYTE;
+typedef s8	CHAR;
+typedef u8	UCHAR;
+typedef s16	SHORT;
+typedef u16	USHORT;
+typedef u16	WORD;
+typedef s32	INT;
+typedef u32	UINT;
+typedef u32	DWORD;
+typedef s32	LONG;
+typedef u32	ULONG;
+typedef s64	LONGLONG;
+typedef u64	ULONGLONG;
+typedef u64	ULONGULONG;
+typedef u64	ULONG64;
+
+typedef CHAR CCHAR;
+typedef USHORT wchar_t;
+typedef SHORT CSHORT;
+typedef LONGLONG LARGE_INTEGER;
+
+typedef LONG NTSTATUS;
+
+typedef LONG KPRIORITY;
+typedef LARGE_INTEGER PHYSICAL_ADDRESS;
+typedef UCHAR KIRQL;
+typedef CHAR KPROCESSOR_MODE;
+
+/* ULONG_PTR is 32 bits on 32-bit platforms and 64 bits on 64-bit
+ * platform, which is same as 'unsigned long' in Linux */
+typedef unsigned long ULONG_PTR;
+
+typedef size_t SIZE_T;
+typedef ULONG_PTR KAFFINITY;
+typedef ULONG ACCESS_MASK;
+
+typedef ULONG_PTR PFN_NUMBER;
+typedef ULONG SECURITY_INFORMATION;
+
+/* non-negative numbers indicate success */
+#define NT_SUCCESS(status) ((NTSTATUS)(status) >= 0)
+
+struct ansi_string {
+	USHORT length;
+	USHORT max_length;
+	char *buf;
+};
+
+struct unicode_string {
+	USHORT length;
+	USHORT max_length;
+	wchar_t *buf;
+};
+
+struct nt_slist {
+	struct nt_slist *next;
+};
+
+#ifdef CONFIG_X86_64
+/* it is not clear how nt_slist_head is used to store pointer to
+ * slists and depth; here we assume 'align' field is used to store
+ * depth and 'region' field is used to store slist pointers */
+struct nt_slist_head {
+	union {
+		USHORT depth;
+		ULONGLONG align;
+	};
+	union {
+		ULONGLONG region;
+		struct nt_slist *next;
+	};
+} __attribute__((aligned(16)));
+typedef struct nt_slist_head nt_slist_header;
+#else
+union nt_slist_head {
+	ULONGLONG align;
+	struct {
+		struct nt_slist *next;
+		USHORT depth;
+		USHORT sequence;
+	};
+};
+typedef union nt_slist_head nt_slist_header;
+#endif
+
+struct nt_list {
+	struct nt_list *next;
+	struct nt_list *prev;
+};
+
+typedef ULONG_PTR NT_SPIN_LOCK;
+
+enum kdpc_importance {LowImportance, MediumImportance, HighImportance};
+
+struct kdpc;
+typedef void (*DPC)(struct kdpc *kdpc, void *ctx, void *arg1,
+		    void *arg2) wstdcall;
+struct kdpc {
+	SHORT type;
+	UCHAR nr_cpu;
+	UCHAR importance;
+	struct nt_list list;
+	DPC func;
+	void *ctx;
+	void *arg1;
+	void *arg2;
+	union {
+		NT_SPIN_LOCK *lock;
+		/* 'lock' is not used; 'queued' represents whether
+		 * kdpc is queued or not */
+		int queued;
+	};
+};
+
+enum pool_type {
+	NonPagedPool, PagedPool, NonPagedPoolMustSucceed, DontUseThisType,
+	NonPagedPoolCacheAligned, PagedPoolCacheAligned,
+	NonPagedPoolCacheAlignedMustS, MaxPoolType,
+	NonPagedPoolSession = 32,
+	PagedPoolSession = NonPagedPoolSession + 1,
+	NonPagedPoolMustSucceedSession = PagedPoolSession + 1,
+	DontUseThisTypeSession = NonPagedPoolMustSucceedSession + 1,
+	NonPagedPoolCacheAlignedSession = DontUseThisTypeSession + 1,
+	PagedPoolCacheAlignedSession = NonPagedPoolCacheAlignedSession + 1,
+	NonPagedPoolCacheAlignedMustSSession = PagedPoolCacheAlignedSession + 1
+};
+
+enum memory_caching_type_orig {
+	MmFrameBufferCached = 2
+};
+
+enum memory_caching_type {
+	MmNonCached = FALSE, MmCached = TRUE,
+	MmWriteCombined = MmFrameBufferCached, MmHardwareCoherentCached,
+	MmNonCachedUnordered, MmUSWCCached, MmMaximumCacheType
+};
+
+enum lock_operation {
+	IoReadAccess, IoWriteAccess, IoModifyAccess
+};
+
+enum mode {
+	KernelMode, UserMode, MaximumMode
+};
+
+struct mdl {
+	struct mdl *next;
+	CSHORT size;
+	CSHORT flags;
+	/* NdisFreeBuffer doesn't pass pool, so we store pool in
+	 * unused field 'process' */
+	union {
+		void *process;
+		void *pool;
+	};
+	void *mappedsystemva;
+	void *startva;
+	ULONG bytecount;
+	ULONG byteoffset;
+};
+
+#define MDL_MAPPED_TO_SYSTEM_VA		0x0001
+#define MDL_PAGES_LOCKED		0x0002
+#define MDL_SOURCE_IS_NONPAGED_POOL	0x0004
+#define MDL_ALLOCATED_FIXED_SIZE	0x0008
+#define MDL_PARTIAL			0x0010
+#define MDL_PARTIAL_HAS_BEEN_MAPPED	0x0020
+#define MDL_IO_PAGE_READ		0x0040
+#define MDL_WRITE_OPERATION		0x0080
+#define MDL_PARENT_MAPPED_SYSTEM_VA	0x0100
+#define MDL_FREE_EXTRA_PTES		0x0200
+#define MDL_IO_SPACE			0x0800
+#define MDL_NETWORK_HEADER		0x1000
+#define MDL_MAPPING_CAN_FAIL		0x2000
+#define MDL_ALLOCATED_MUST_SUCCEED	0x4000
+
+#define MDL_POOL_ALLOCATED		0x0400
+#define MDL_CACHE_ALLOCATED		0x8000
+
+#define PAGE_START(ptr) ((void *)((ULONG_PTR)(ptr) & ~(PAGE_SIZE - 1)))
+#define BYTE_OFFSET(ptr) ((ULONG)((ULONG_PTR)(ptr) & (PAGE_SIZE - 1)))
+
+#define MmGetMdlByteCount(mdl) ((mdl)->bytecount)
+#define MmGetMdlVirtualAddress(mdl) ((mdl)->startva + (mdl)->byteoffset)
+#define MmGetMdlByteOffset(mdl) ((mdl)->byteoffset)
+#define MmGetSystemAddressForMdl(mdl) ((mdl)->mappedsystemva)
+#define MmGetSystemAddressForMdlSafe(mdl, priority) ((mdl)->mappedsystemva)
+#define MmGetMdlPfnArray(mdl) ((PFN_NUMBER *)(mdl + 1))
+#define MmInitializeMdl(mdl, baseva, length)				\
+do {									\
+	(mdl)->next = NULL;						\
+	(mdl)->size = MmSizeOfMdl(baseva, length);			\
+	(mdl)->flags = 0;						\
+	(mdl)->startva = PAGE_START(baseva);				\
+	(mdl)->byteoffset = BYTE_OFFSET(baseva);			\
+	(mdl)->bytecount = length;					\
+	(mdl)->mappedsystemva = baseva;					\
+	TRACE4("%p %p %p %d %d", (mdl), baseva, (mdl)->startva,	\
+		  (mdl)->byteoffset, length);				\
+} while (0)
+
+struct kdevice_queue_entry {
+	struct nt_list list;
+	ULONG sort_key;
+	BOOLEAN inserted;
+};
+
+struct kdevice_queue {
+	USHORT type;
+	USHORT size;
+	struct nt_list list;
+	NT_SPIN_LOCK lock;
+	BOOLEAN busy;
+};
+
+struct wait_context_block {
+	struct kdevice_queue_entry wait_queue_entry;
+	void *device_routine;
+	void *device_context;
+	ULONG num_regs;
+	void *device_object;
+	void *current_irp;
+	void *buffer_chaining_dpc;
+};
+
+struct wait_block {
+	struct nt_list list;
+	struct task_struct *thread;
+	void *object;
+	int *wait_done;
+	USHORT wait_key;
+	USHORT wait_type;
+};
+
+struct dispatcher_header {
+	UCHAR type;
+	UCHAR absolute;
+	UCHAR size;
+	UCHAR inserted;
+	LONG signal_state;
+	struct nt_list wait_blocks;
+};
+
+enum event_type {
+	NotificationEvent,
+	SynchronizationEvent,
+};
+
+enum timer_type {
+	NotificationTimer = NotificationEvent,
+	SynchronizationTimer = SynchronizationEvent,
+};
+
+enum dh_type {
+	NotificationObject = NotificationEvent,
+	SynchronizationObject = SynchronizationEvent,
+	MutexObject,
+	SemaphoreObject,
+	ThreadObject,
+};
+
+enum wait_type {
+	WaitAll, WaitAny
+};
+
+/* objects that use dispatcher_header have it as the first field, so
+ * whenever we need to initialize dispatcher_header, we can convert
+ * that object into a nt_event and access dispatcher_header */
+struct nt_event {
+	struct dispatcher_header dh;
+};
+
+struct wrap_timer;
+
+#define WRAP_TIMER_MAGIC 47697249
+
+struct nt_timer {
+	struct dispatcher_header dh;
+	/* We can't fit Linux timer in this structure. Instead of
+	 * padding the nt_timer structure, we replace due_time field
+	 * with *wrap_timer and allocate memory for it when nt_timer is
+	 * initialized */
+	union {
+		ULONGLONG due_time;
+		struct wrap_timer *wrap_timer;
+	};
+	struct nt_list nt_timer_list;
+	struct kdpc *kdpc;
+	union {
+		LONG period;
+		LONG wrap_timer_magic;
+	};
+};
+
+struct nt_mutex {
+	struct dispatcher_header dh;
+	struct nt_list list;
+	struct task_struct *owner_thread;
+	BOOLEAN abandoned;
+	BOOLEAN apc_disable;
+};
+
+struct nt_semaphore {
+	struct dispatcher_header dh;
+	LONG limit;
+};
+
+struct nt_thread {
+	struct dispatcher_header dh;
+	/* the rest in Windows is a long structure; since this
+	 * structure is opaque to drivers, we just define what we
+	 * need */
+	int pid;
+	NTSTATUS status;
+	struct task_struct *task;
+	struct nt_list irps;
+	NT_SPIN_LOCK lock;
+	KPRIORITY prio;
+};
+
+#define set_object_type(dh, type)	((dh)->type = (type))
+#define is_notify_object(dh)		((dh)->type == NotificationObject)
+#define is_synch_object(dh)		((dh)->type == SynchronizationObject)
+#define is_mutex_object(dh)		((dh)->type == MutexObject)
+#define is_semaphore_object(dh)		((dh)->type == SemaphoreObject)
+#define is_nt_thread_object(dh)		((dh)->type == ThreadObject)
+
+#define IO_TYPE_ADAPTER				1
+#define IO_TYPE_CONTROLLER			2
+#define IO_TYPE_DEVICE				3
+#define IO_TYPE_DRIVER				4
+#define IO_TYPE_FILE				5
+#define IO_TYPE_IRP				6
+#define IO_TYPE_DEVICE_OBJECT_EXTENSION		13
+
+struct irp;
+struct dev_obj_ext;
+struct driver_object;
+
+struct device_object {
+	CSHORT type;
+	USHORT size;
+	LONG ref_count;
+	struct driver_object *drv_obj;
+	struct device_object *next;
+	struct device_object *attached;
+	struct irp *current_irp;
+	void *io_timer;
+	ULONG flags;
+	ULONG characteristics;
+	void *vpb;
+	void *dev_ext;
+	CCHAR stack_count;
+	union {
+		struct nt_list queue_list;
+		struct wait_context_block wcb;
+	} queue;
+	ULONG align_req;
+	struct kdevice_queue dev_queue;
+	struct kdpc dpc;
+	ULONG active_threads;
+	void *security_desc;
+	struct nt_event lock;
+	USHORT sector_size;
+	USHORT spare1;
+	struct dev_obj_ext *dev_obj_ext;
+	void *reserved;
+};
+
+struct dev_obj_ext {
+	CSHORT type;
+	CSHORT size;
+	struct device_object *dev_obj;
+	struct device_object *attached_to;
+};
+
+struct io_status_block {
+	union {
+		NTSTATUS status;
+		void *pointer;
+	};
+	ULONG_PTR info;
+};
+
+#ifdef CONFIG_X86_64
+struct io_status_block32 {
+	NTSTATUS status;
+	ULONG info;
+};
+#endif
+
+#define DEVICE_TYPE ULONG
+
+struct driver_extension;
+
+typedef NTSTATUS driver_dispatch_t(struct device_object *dev_obj,
+				   struct irp *irp) wstdcall;
+
+struct driver_object {
+	CSHORT type;
+	CSHORT size;
+	struct device_object *dev_obj;
+	ULONG flags;
+	void *start;
+	ULONG driver_size;
+	void *section;
+	struct driver_extension *drv_ext;
+	struct unicode_string name;
+	struct unicode_string *hardware_database;
+	void *fast_io_dispatch;
+	void *init;
+	void *start_io;
+	void (*unload)(struct driver_object *driver) wstdcall;
+	driver_dispatch_t *major_func[IRP_MJ_MAXIMUM_FUNCTION + 1];
+};
+
+struct driver_extension {
+	struct driver_object *drv_obj;
+	NTSTATUS (*add_device)(struct driver_object *drv_obj,
+			       struct device_object *dev_obj);
+	ULONG count;
+	struct unicode_string service_key_name;
+	struct nt_list custom_ext;
+};
+
+struct custom_ext {
+	struct nt_list list;
+	void *client_id;
+};
+
+struct wrap_bin_file;
+
+struct file_object {
+	CSHORT type;
+	CSHORT size;
+	struct device_object *dev_obj;
+	void *volume_parameter_block;
+	void *fs_context;
+	void *fs_context2;
+	void *section_object_pointer;
+	void *private_cache_map;
+	NTSTATUS final_status;
+	union {
+		struct file_object *related_file_object;
+		struct wrap_bin_file *wrap_bin_file;
+	};
+	BOOLEAN lock_operation;
+	BOOLEAN delete_pending;
+	BOOLEAN read_access;
+	BOOLEAN write_access;
+	BOOLEAN delete_access;
+	BOOLEAN shared_read;
+	BOOLEAN shared_write;
+	BOOLEAN shared_delete;
+	ULONG flags;
+	struct unicode_string _name_;
+	LARGE_INTEGER current_byte_offset;
+	ULONG waiters;
+	ULONG busy;
+	void *last_lock;
+	struct nt_event lock;
+	struct nt_event event;
+	void *completion_context;
+};
+
+#ifdef CONFIG_X86_64
+#define POINTER_ALIGN __attribute__((aligned(8)))
+#else
+#define POINTER_ALIGN
+#endif
+
+#define CACHE_ALIGN __attribute__((aligned(128)))
+
+enum system_power_state {
+	PowerSystemUnspecified = 0,
+	PowerSystemWorking, PowerSystemSleeping1, PowerSystemSleeping2,
+	PowerSystemSleeping3, PowerSystemHibernate, PowerSystemShutdown,
+	PowerSystemMaximum,
+};
+
+enum device_power_state {
+	PowerDeviceUnspecified = 0,
+	PowerDeviceD0, PowerDeviceD1, PowerDeviceD2, PowerDeviceD3,
+	PowerDeviceMaximum,
+};
+
+union power_state {
+	enum system_power_state system_state;
+	enum device_power_state device_state;
+};
+
+enum power_state_type {
+	SystemPowerState = 0, DevicePowerState,
+};
+
+enum power_action {
+	PowerActionNone = 0,
+	PowerActionReserved, PowerActionSleep, PowerActionHibernate,
+	PowerActionShutdown, PowerActionShutdownReset, PowerActionShutdownOff,
+	PowerActionWarmEject,
+};
+
+struct guid {
+	ULONG data1;
+	USHORT data2;
+	USHORT data3;
+	UCHAR data4[8];
+};
+
+struct nt_interface {
+	USHORT size;
+	USHORT version;
+	void *context;
+	void (*reference)(void *context) wstdcall;
+	void (*dereference)(void *context) wstdcall;
+};
+
+enum interface_type {
+	InterfaceTypeUndefined = -1, Internal, Isa, Eisa, MicroChannel,
+	TurboChannel, PCIBus, VMEBus, NuBus, PCMCIABus, CBus, MPIBus,
+	MPSABus, ProcessorInternal, InternalPowerBus, PNPISABus,
+	PNPBus, MaximumInterfaceType,
+};
+
+#define CmResourceTypeNull		0
+#define CmResourceTypePort		1
+#define CmResourceTypeInterrupt		2
+#define CmResourceTypeMemory		3
+#define CmResourceTypeDma		4
+#define CmResourceTypeDeviceSpecific	5
+#define CmResourceTypeBusNumber		6
+#define CmResourceTypeMaximum		7
+
+#define CmResourceTypeNonArbitrated	128
+#define CmResourceTypeConfigData	128
+#define CmResourceTypeDevicePrivate	129
+#define CmResourceTypePcCardConfig	130
+#define CmResourceTypeMfCardConfig	131
+
+enum cm_share_disposition {
+	CmResourceShareUndetermined = 0, CmResourceShareDeviceExclusive,
+	CmResourceShareDriverExclusive, CmResourceShareShared
+};
+
+#define CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE	0
+#define CM_RESOURCE_INTERRUPT_LATCHED		1
+#define CM_RESOURCE_MEMORY_READ_WRITE		0x0000
+#define CM_RESOURCE_MEMORY_READ_ONLY		0x0001
+#define CM_RESOURCE_MEMORY_WRITE_ONLY		0x0002
+#define CM_RESOURCE_MEMORY_PREFETCHABLE		0x0004
+
+#define CM_RESOURCE_MEMORY_COMBINEDWRITE	0x0008
+#define CM_RESOURCE_MEMORY_24			0x0010
+#define CM_RESOURCE_MEMORY_CACHEABLE		0x0020
+
+#define CM_RESOURCE_PORT_MEMORY			0x0000
+#define CM_RESOURCE_PORT_IO			0x0001
+#define CM_RESOURCE_PORT_10_BIT_DECODE		0x0004
+#define CM_RESOURCE_PORT_12_BIT_DECODE		0x0008
+#define CM_RESOURCE_PORT_16_BIT_DECODE		0x0010
+#define CM_RESOURCE_PORT_POSITIVE_DECODE	0x0020
+#define CM_RESOURCE_PORT_PASSIVE_DECODE		0x0040
+#define CM_RESOURCE_PORT_WINDOW_DECODE		0x0080
+
+#define CM_RESOURCE_DMA_8			0x0000
+#define CM_RESOURCE_DMA_16			0x0001
+#define CM_RESOURCE_DMA_32			0x0002
+#define CM_RESOURCE_DMA_8_AND_16		0x0004
+#define CM_RESOURCE_DMA_BUS_MASTER		0x0008
+#define CM_RESOURCE_DMA_TYPE_A			0x0010
+#define CM_RESOURCE_DMA_TYPE_B			0x0020
+#define CM_RESOURCE_DMA_TYPE_F			0x0040
+
+#define MAX_RESOURCES 20
+
+#pragma pack(push,4)
+struct cm_partial_resource_descriptor {
+	UCHAR type;
+	UCHAR share;
+	USHORT flags;
+	union {
+		struct {
+			PHYSICAL_ADDRESS start;
+			ULONG length;
+		} generic;
+		struct {
+			PHYSICAL_ADDRESS start;
+			ULONG length;
+		} port;
+		struct {
+			ULONG level;
+			ULONG vector;
+			KAFFINITY affinity;
+		} interrupt;
+		struct {
+			PHYSICAL_ADDRESS start;
+			ULONG length;
+		} memory;
+		struct {
+			ULONG channel;
+			ULONG port;
+			ULONG reserved1;
+		} dma;
+		struct {
+			ULONG data[3];
+		} device_private;
+		struct {
+			ULONG start;
+			ULONG length;
+			ULONG reserved;
+		} bus_number;
+		struct {
+			ULONG data_size;
+			ULONG reserved1;
+			ULONG reserved2;
+		} device_specific_data;
+	} u;
+};
+#pragma pack(pop)
+
+struct cm_partial_resource_list {
+	USHORT version;
+	USHORT revision;
+	ULONG count;
+	struct cm_partial_resource_descriptor partial_descriptors[1];
+};
+
+struct cm_full_resource_descriptor {
+	enum interface_type interface_type;
+	ULONG bus_number;
+	struct cm_partial_resource_list partial_resource_list;
+};
+
+struct cm_resource_list {
+	ULONG count;
+	struct cm_full_resource_descriptor list[1];
+};
+
+enum file_info_class {
+	FileDirectoryInformation = 1,
+	FileBasicInformation = 4,
+	FileStandardInformation = 5,
+	FileNameInformation = 9,
+	FilePositionInformation = 14,
+	FileAlignmentInformation = 17,
+	FileNetworkOpenInformation = 34,
+	FileAttributeTagInformation = 35,
+	FileMaximumInformation = 41,
+};
+
+enum fs_info_class {
+	FileFsVolumeInformation = 1,
+	/* ... */
+	FileFsMaximumInformation = 9,
+};
+
+enum device_relation_type {
+	BusRelations, EjectionRelations, PowerRelations, RemovalRelations,
+	TargetDeviceRelation, SingleBusRelations,
+};
+
+enum bus_query_id_type {
+	BusQueryDeviceID = 0, BusQueryHardwareIDs = 1,
+	BusQueryCompatibleIDs = 2, BusQueryInstanceID = 3,
+	BusQueryDeviceSerialNumber = 4,
+};
+
+enum device_text_type {
+	DeviceTextDescription = 0, DeviceTextLocationInformation = 1,
+};
+
+enum device_usage_notification_type {
+	DeviceUsageTypeUndefined, DeviceUsageTypePaging,
+	DeviceUsageTypeHibernation, DevbiceUsageTypeDumpFile,
+};
+
+#define METHOD_BUFFERED		0
+#define METHOD_IN_DIRECT	1
+#define METHOD_OUT_DIRECT	2
+#define METHOD_NEITHER		3
+
+#define CTL_CODE(dev_type, func, method, access)			\
+	(((dev_type) << 16) | ((access) << 14) | ((func) << 2) | (method))
+
+#define IO_METHOD_FROM_CTL_CODE(code) (code & 0x3)
+
+#ifndef CONFIG_X86_64
+#pragma pack(push,4)
+#endif
+struct io_stack_location {
+	UCHAR major_fn;
+	UCHAR minor_fn;
+	UCHAR flags;
+	UCHAR control;
+	union {
+		struct {
+			void *security_context;
+			ULONG options;
+			USHORT POINTER_ALIGN file_attributes;
+			USHORT share_access;
+			ULONG POINTER_ALIGN ea_length;
+		} create;
+		struct {
+			ULONG length;
+			ULONG POINTER_ALIGN key;
+			LARGE_INTEGER byte_offset;
+		} read;
+		struct {
+			ULONG length;
+			ULONG POINTER_ALIGN key;
+			LARGE_INTEGER byte_offset;
+		} write;
+		struct {
+			ULONG length;
+			enum file_info_class POINTER_ALIGN file_info_class;
+		} query_file;
+		struct {
+			ULONG length;
+			enum file_info_class POINTER_ALIGN file_info_class;
+			struct file_object *file_object;
+			union {
+				struct {
+					BOOLEAN replace_if_exists;
+					BOOLEAN advance_only;
+				};
+				ULONG cluster_count;
+				void *delete_handle;
+			};
+		} set_file;
+		struct {
+			ULONG length;
+			enum fs_info_class POINTER_ALIGN fs_info_class;
+		} query_volume;
+		struct {
+			ULONG output_buf_len;
+			ULONG POINTER_ALIGN input_buf_len;
+			ULONG POINTER_ALIGN code;
+			void *type3_input_buf;
+		} dev_ioctl;
+		struct {
+			SECURITY_INFORMATION security_info;
+			ULONG POINTER_ALIGN length;
+		} query_security;
+		struct {
+			SECURITY_INFORMATION security_info;
+			void *security_descriptor;
+		} set_security;
+		struct {
+			void *vpb;
+			struct device_object *device_object;
+		} mount_volume;
+		struct {
+			void *vpb;
+			struct device_object *device_object;
+		} verify_volume;
+		struct {
+			void *srb;
+		} scsi;
+		struct {
+			enum device_relation_type type;
+		} query_device_relations;
+		struct {
+			const struct guid *type;
+			USHORT size;
+			USHORT version;
+			struct nt_interface *intf;
+			void *intf_data;
+		} query_intf;
+		struct {
+			void *capabilities;
+		} device_capabilities;
+		struct {
+			void *io_resource_requirement_list;
+		} filter_resource_requirements;
+		struct {
+			ULONG which_space;
+			void *buffer;
+			ULONG offset;
+			ULONG POINTER_ALIGN length;
+		} read_write_config;
+		struct {
+			BOOLEAN lock;
+		} set_lock;
+		struct {
+			enum bus_query_id_type id_type;
+		} query_id;
+		struct {
+			enum device_text_type device_text_type;
+			ULONG POINTER_ALIGN locale_id;
+		} query_device_text;
+		struct {
+			BOOLEAN in_path;
+			BOOLEAN reserved[3];
+			enum device_usage_notification_type POINTER_ALIGN type;
+		} usage_notification;
+		struct {
+			enum system_power_state power_state;
+		} wait_wake;
+		struct {
+			void *power_sequence;
+		} power_sequence;
+		struct {
+			ULONG sys_context;
+			enum power_state_type POINTER_ALIGN type;
+			union power_state POINTER_ALIGN state;
+			enum power_action POINTER_ALIGN shutdown_type;
+		} power;
+		struct {
+			struct cm_resource_list *allocated_resources;
+			struct cm_resource_list *allocated_resources_translated;
+		} start_device;
+		struct {
+			ULONG_PTR provider_id;
+			void *data_path;
+			ULONG buf_len;
+			void *buf;
+		} wmi;
+		struct {
+			void *arg1;
+			void *arg2;
+			void *arg3;
+			void *arg4;
+		} others;
+	} params;
+	struct device_object *dev_obj;
+	struct file_object *file_obj;
+	NTSTATUS (*completion_routine)(struct device_object *,
+				       struct irp *, void *) wstdcall;
+	void *context;
+};
+#ifndef CONFIG_X86_64
+#pragma pack(pop)
+#endif
+
+struct kapc {
+	CSHORT type;
+	CSHORT size;
+	ULONG spare0;
+	struct nt_thread *thread;
+	struct nt_list list;
+	void *kernele_routine;
+	void *rundown_routine;
+	void *normal_routine;
+	void *normal_context;
+	void *sys_arg1;
+	void *sys_arg2;
+	CCHAR apc_state_index;
+	KPROCESSOR_MODE apc_mode;
+	BOOLEAN inserted;
+};
+
+#define IRP_NOCACHE			0x00000001
+#define IRP_SYNCHRONOUS_API		0x00000004
+#define IRP_ASSOCIATED_IRP		0x00000008
+
+enum urb_state {
+	URB_INVALID = 1, URB_ALLOCATED, URB_SUBMITTED,
+	URB_COMPLETED, URB_FREE, URB_SUSPEND, URB_INT_UNLINKED };
+
+struct wrap_urb {
+	struct nt_list list;
+	enum urb_state state;
+	struct nt_list complete_list;
+	unsigned int flags;
+	struct urb *urb;
+	struct irp *irp;
+#ifdef USB_DEBUG
+	unsigned int id;
+#endif
+};
+
+struct irp {
+	SHORT type;
+	USHORT size;
+	struct mdl *mdl;
+	ULONG flags;
+	union {
+		struct irp *master_irp;
+		LONG irp_count;
+		void *system_buffer;
+	} associated_irp;
+	struct nt_list thread_list;
+	struct io_status_block io_status;
+	KPROCESSOR_MODE requestor_mode;
+	BOOLEAN pending_returned;
+	CHAR stack_count;
+	CHAR current_location;
+	BOOLEAN cancel;
+	KIRQL cancel_irql;
+	CCHAR apc_env;
+	UCHAR alloc_flags;
+	struct io_status_block *user_status;
+	struct nt_event *user_event;
+	union {
+		struct {
+			void *user_apc_routine;
+			void *user_apc_context;
+		} async_params;
+		LARGE_INTEGER alloc_size;
+	} overlay;
+	void (*cancel_routine)(struct device_object *, struct irp *) wstdcall;
+	void *user_buf;
+	union {
+		struct {
+			union {
+				struct kdevice_queue_entry dev_q_entry;
+				struct {
+					void *driver_context[4];
+				};
+			};
+			void *thread;
+			char *aux_buf;
+			struct {
+				struct nt_list list;
+				union {
+					struct io_stack_location *csl;
+					ULONG packet_type;
+				};
+			};
+			struct file_object *file_object;
+		} overlay;
+		union {
+			struct kapc apc;
+			/* space for apc is used for ndiswrapper
+			 * specific fields */
+			struct {
+				struct wrap_urb *wrap_urb;
+				struct wrap_device *wrap_device;
+			};
+		};
+		void *completion_key;
+	} tail;
+};
+
+#define IoSizeOfIrp(stack_count)					\
+	((USHORT)(sizeof(struct irp) +					\
+		  ((stack_count) * sizeof(struct io_stack_location))))
+#define IoGetCurrentIrpStackLocation(irp)	\
+	(irp)->tail.overlay.csl
+#define IoGetNextIrpStackLocation(irp)		\
+	(IoGetCurrentIrpStackLocation(irp) - 1)
+#define IoGetPreviousIrpStackLocation(irp)	\
+	(IoGetCurrentIrpStackLocation(irp) + 1)
+
+#define IoSetNextIrpStackLocation(irp)				\
+do {								\
+	KIRQL _irql_;						\
+	IoAcquireCancelSpinLock(&_irql_);			\
+	(irp)->current_location--;				\
+	IoGetCurrentIrpStackLocation(irp)--;			\
+	IoReleaseCancelSpinLock(_irql_);			\
+} while (0)
+
+#define IoSkipCurrentIrpStackLocation(irp)			\
+do {								\
+	KIRQL _irql_;						\
+	IoAcquireCancelSpinLock(&_irql_);			\
+	(irp)->current_location++;				\
+	IoGetCurrentIrpStackLocation(irp)++;			\
+	IoReleaseCancelSpinLock(_irql_);			\
+} while (0)
+
+static inline void
+IoCopyCurrentIrpStackLocationToNext(struct irp *irp)
+{
+	struct io_stack_location *next;
+	next = IoGetNextIrpStackLocation(irp);
+	memcpy(next, IoGetCurrentIrpStackLocation(irp),
+	       offsetof(struct io_stack_location, completion_routine));
+	next->control = 0;
+}
+
+static inline void
+IoSetCompletionRoutine(struct irp *irp, void *routine, void *context,
+		       BOOLEAN success, BOOLEAN error, BOOLEAN cancel)
+{
+	struct io_stack_location *irp_sl = IoGetNextIrpStackLocation(irp);
+	irp_sl->completion_routine = routine;
+	irp_sl->context = context;
+	irp_sl->control = 0;
+	if (success)
+		irp_sl->control |= SL_INVOKE_ON_SUCCESS;
+	if (error)
+		irp_sl->control |= SL_INVOKE_ON_ERROR;
+	if (cancel)
+		irp_sl->control |= SL_INVOKE_ON_CANCEL;
+}
+
+#define IoMarkIrpPending(irp)						\
+	(IoGetCurrentIrpStackLocation((irp))->control |= SL_PENDING_RETURNED)
+#define IoUnmarkIrpPending(irp)						\
+	(IoGetCurrentIrpStackLocation((irp))->control &= ~SL_PENDING_RETURNED)
+
+#define IRP_SL(irp, n) (((struct io_stack_location *)((irp) + 1)) + (n))
+#define IRP_DRIVER_CONTEXT(irp) (irp)->tail.overlay.driver_context
+#define IoIrpThread(irp) ((irp)->tail.overlay.thread)
+
+#define IRP_URB(irp)							\
+	(union nt_urb *)(IoGetCurrentIrpStackLocation(irp)->params.others.arg1)
+
+#define IRP_WRAP_DEVICE(irp) (irp)->tail.wrap_device
+#define IRP_WRAP_URB(irp) (irp)->tail.wrap_urb
+
+struct wmi_guid_reg_info {
+	struct guid *guid;
+	ULONG instance_count;
+	ULONG flags;
+};
+
+struct wmilib_context {
+	ULONG guid_count;
+	struct wmi_guid_reg_info *guid_list;
+	void *query_wmi_reg_info;
+	void *query_wmi_data_block;
+	void *set_wmi_data_block;
+	void *set_wmi_data_item;
+	void *execute_wmi_method;
+	void *wmi_function_control;
+};
+
+enum key_value_information_class {
+	KeyValueBasicInformation, KeyValueFullInformation,
+	KeyValuePartialInformation, KeyValueFullInformationAlign64,
+	KeyValuePartialInformationAlign64
+};
+
+struct file_name_info {
+	ULONG length;
+	wchar_t *name;
+};
+
+struct file_std_info {
+	LARGE_INTEGER alloc_size;
+	LARGE_INTEGER eof;
+	ULONG num_links;
+	BOOLEAN delete_pending;
+	BOOLEAN dir;
+};
+
+enum nt_obj_type {
+	NT_OBJ_EVENT = 10, NT_OBJ_MUTEX, NT_OBJ_THREAD, NT_OBJ_TIMER,
+	NT_OBJ_SEMAPHORE,
+};
+
+enum common_object_type {
+	OBJECT_TYPE_NONE, OBJECT_TYPE_DEVICE, OBJECT_TYPE_DRIVER,
+	OBJECT_TYPE_NT_THREAD, OBJECT_TYPE_FILE, OBJECT_TYPE_CALLBACK,
+};
+
+struct common_object_header {
+	struct nt_list list;
+	enum common_object_type type;
+	UINT size;
+	UINT ref_count;
+	BOOLEAN close_in_process;
+	BOOLEAN permanent;
+	struct unicode_string name;
+};
+
+#define OBJECT_TO_HEADER(object)					\
+	(struct common_object_header *)((void *)(object) -		\
+					sizeof(struct common_object_header))
+#define OBJECT_SIZE(size)				\
+	((size) + sizeof(struct common_object_header))
+#define HEADER_TO_OBJECT(hdr)					\
+	((void *)(hdr) + sizeof(struct common_object_header))
+#define HANDLE_TO_OBJECT(handle) HEADER_TO_OBJECT(handle)
+#define HANDLE_TO_HEADER(handle) (handle)
+
+enum work_queue_type {
+	CriticalWorkQueue, DelayedWorkQueue, HyperCriticalWorkQueue,
+	MaximumWorkQueue
+};
+
+typedef void (*NTOS_WORK_FUNC)(void *arg1, void *arg2) wstdcall;
+
+struct io_workitem {
+	enum work_queue_type type;
+	struct device_object *dev_obj;
+	NTOS_WORK_FUNC worker_routine;
+	void *context;
+};
+
+struct io_workitem_entry {
+	struct nt_list list;
+	struct io_workitem *io_workitem;
+};
+
+enum mm_page_priority {
+	LowPagePriority, NormalPagePriority = 16, HighPagePriority = 32
+};
+
+enum kinterrupt_mode {
+	LevelSensitive, Latched
+};
+
+enum ntos_wait_reason {
+	Executive, FreePage, PageIn, PoolAllocation, DelayExecution,
+	Suspended, UserRequest, WrExecutive, WrFreePage, WrPageIn,
+	WrPoolAllocation, WrDelayExecution, WrSuspended, WrUserRequest,
+	WrEventPair, WrQueue, WrLpcReceive, WrLpcReply, WrVirtualMemory,
+	WrPageOut, WrRendezvous, Spare2, Spare3, Spare4, Spare5, Spare6,
+	WrKernel, MaximumWaitReason
+};
+
+typedef enum ntos_wait_reason KWAIT_REASON;
+
+typedef void *LOOKASIDE_ALLOC_FUNC(enum pool_type pool_type,
+				   SIZE_T size, ULONG tag) wstdcall;
+typedef void LOOKASIDE_FREE_FUNC(void *) wstdcall;
+
+struct npaged_lookaside_list {
+	nt_slist_header head;
+	USHORT depth;
+	USHORT maxdepth;
+	ULONG totalallocs;
+	union {
+		ULONG allocmisses;
+		ULONG allochits;
+	} u1;
+	ULONG totalfrees;
+	union {
+		ULONG freemisses;
+		ULONG freehits;
+	} u2;
+	enum pool_type pool_type;
+	ULONG tag;
+	ULONG size;
+	LOOKASIDE_ALLOC_FUNC *alloc_func;
+	LOOKASIDE_FREE_FUNC *free_func;
+	struct nt_list list;
+	ULONG lasttotallocs;
+	union {
+		ULONG lastallocmisses;
+		ULONG lastallochits;
+	} u3;
+	ULONG pad[2];
+#ifndef CONFIG_X86_64
+	NT_SPIN_LOCK obsolete;
+#endif
+}
+#ifdef CONFIG_X86_64
+CACHE_ALIGN
+#endif
+;
+
+enum device_registry_property {
+	DevicePropertyDeviceDescription, DevicePropertyHardwareID,
+	DevicePropertyCompatibleIDs, DevicePropertyBootConfiguration,
+	DevicePropertyBootConfigurationTranslated,
+	DevicePropertyClassName, DevicePropertyClassGuid,
+	DevicePropertyDriverKeyName, DevicePropertyManufacturer,
+	DevicePropertyFriendlyName, DevicePropertyLocationInformation,
+	DevicePropertyPhysicalDeviceObjectName, DevicePropertyBusTypeGuid,
+	DevicePropertyLegacyBusType, DevicePropertyBusNumber,
+	DevicePropertyEnumeratorName, DevicePropertyAddress,
+	DevicePropertyUINumber, DevicePropertyInstallState,
+	DevicePropertyRemovalPolicy
+};
+
+enum trace_information_class {
+	TraceIdClass, TraceHandleClass, TraceEnableFlagsClass,
+	TraceEnableLevelClass, GlobalLoggerHandleClass, EventLoggerHandleClass,
+	AllLoggerHandlesClass, TraceHandleByNameClass
+};
+
+struct kinterrupt;
+typedef BOOLEAN (*PKSERVICE_ROUTINE)(struct kinterrupt *interrupt,
+				     void *context) wstdcall;
+typedef BOOLEAN (*PKSYNCHRONIZE_ROUTINE)(void *context) wstdcall;
+
+struct kinterrupt {
+	ULONG vector;
+	KAFFINITY cpu_mask;
+	NT_SPIN_LOCK lock;
+	NT_SPIN_LOCK *actual_lock;
+	BOOLEAN shared;
+	BOOLEAN save_fp;
+	union {
+		CHAR processor_number;
+#ifdef CONFIG_DEBUG_SHIRQ
+		CHAR enabled;
+#endif
+	} u;
+	PKSERVICE_ROUTINE isr;
+	void *isr_ctx;
+	struct nt_list list;
+	KIRQL irql;
+	KIRQL synch_irql;
+	enum kinterrupt_mode mode;
+};
+
+struct time_fields {
+	CSHORT year;
+	CSHORT month;
+	CSHORT day;
+	CSHORT hour;
+	CSHORT minute;
+	CSHORT second;
+	CSHORT milliseconds;
+	CSHORT weekday;
+};
+
+struct object_attributes {
+	ULONG length;
+	void *root_dir;
+	struct unicode_string *name;
+	ULONG attributes;
+	void *security_descr;
+	void *security_qos;
+};
+
+typedef void (*PCALLBACK_FUNCTION)(void *context, void *arg1,
+				   void *arg2) wstdcall;
+
+struct callback_object;
+struct callback_func {
+	PCALLBACK_FUNCTION func;
+	void *context;
+	struct nt_list list;
+	struct callback_object *object;
+};
+
+struct callback_object {
+	NT_SPIN_LOCK lock;
+	struct nt_list list;
+	struct nt_list callback_funcs;
+	BOOLEAN allow_multiple_callbacks;
+	struct object_attributes *attributes;
+};
+
+enum section_inherit {
+	ViewShare = 1, ViewUnmap = 2
+};
+
+struct ksystem_time {
+	ULONG low_part;
+	LONG high1_time;
+	LONG high2_time;
+};
+
+enum nt_product_type {
+	nt_product_win_nt = 1, nt_product_lan_man_nt, nt_product_server
+};
+
+enum alt_arch_type {
+	arch_type_standard, arch_type_nex98x86, end_alternatives
+};
+
+struct kuser_shared_data {
+	ULONG tick_count;
+	ULONG tick_count_multiplier;
+	volatile struct ksystem_time interrupt_time;
+	volatile struct ksystem_time system_time;
+	volatile struct ksystem_time time_zone_bias;
+	USHORT image_number_low;
+	USHORT image_number_high;
+	wchar_t nt_system_root[260];
+	ULONG max_stack_trace_depth;
+	ULONG crypto_exponent;
+	ULONG time_zone_id;
+	ULONG large_page_min;
+	ULONG reserved2[7];
+	enum nt_product_type nt_product_type;
+	BOOLEAN product_type_is_valid;
+	ULONG nt_major_version;
+	ULONG nt_minor_version;
+	BOOLEAN processor_features[PROCESSOR_FEATURE_MAX];
+	ULONG reserved1;
+	ULONG reserved3;
+	volatile LONG time_slip;
+	enum alt_arch_type alt_arch_type;
+	LARGE_INTEGER system_expiration_date;
+	ULONG suite_mask;
+	BOOLEAN kdbg_enabled;
+	volatile ULONG active_console;
+	volatile ULONG dismount_count;
+	ULONG com_plus_package;
+	ULONG last_system_rite_event_tick_count;
+	ULONG num_phys_pages;
+	BOOLEAN safe_boot_mode;
+	ULONG trace_log;
+	ULONGLONG fill0;
+	ULONGLONG sys_call[4];
+	union {
+		volatile struct ksystem_time tick_count;
+		volatile ULONG64 tick_count_quad;
+	} tick;
+};
+
+#define REG_NONE			(0)
+#define REG_SZ				(1)
+#define REG_EXPAND_SZ			(2)
+#define REG_BINARY			(3)
+#define REG_DWORD			(4)
+
+#define RTL_REGISTRY_ABSOLUTE		0
+#define RTL_REGISTRY_SERVICES		1
+#define RTL_REGISTRY_CONTROL		2
+#define RTL_REGISTRY_WINDOWS_NT		3
+#define RTL_REGISTRY_DEVICEMAP		4
+#define RTL_REGISTRY_USER		5
+#define RTL_REGISTRY_MAXIMUM		6
+#define RTL_REGISTRY_HANDLE		0x40000000
+#define RTL_REGISTRY_OPTIONAL		0x80000000
+
+#define RTL_QUERY_REGISTRY_SUBKEY	0x00000001
+#define RTL_QUERY_REGISTRY_TOPKEY	0x00000002
+#define RTL_QUERY_REGISTRY_REQUIRED	0x00000004
+#define RTL_QUERY_REGISTRY_NOVALUE	0x00000008
+#define RTL_QUERY_REGISTRY_NOEXPAND	0x00000010
+#define RTL_QUERY_REGISTRY_DIRECT	0x00000020
+#define RTL_QUERY_REGISTRY_DELETE	0x00000040
+
+typedef NTSTATUS (*PRTL_QUERY_REGISTRY_ROUTINE)(wchar_t *name, ULONG type,
+						void *data, ULONG length,
+						void *context,
+						void *entry) wstdcall;
+
+struct rtl_query_registry_table {
+	PRTL_QUERY_REGISTRY_ROUTINE query_func;
+	ULONG flags;
+	wchar_t *name;
+	void *context;
+	ULONG def_type;
+	void *def_data;
+	ULONG def_length;
+};
+
+struct io_remove_lock {
+	BOOLEAN removed;
+	BOOLEAN reserved[3];
+	LONG io_count;
+	struct nt_event remove_event;
+};
+
+struct io_error_log_packet {
+	UCHAR major_fn_code;
+	UCHAR retry_count;
+	USHORT dump_data_size;
+	USHORT nr_of_strings;
+	USHORT string_offset;
+	USHORT event_category;
+	NTSTATUS error_code;
+	ULONG unique_error_value;
+	NTSTATUS final_status;
+	ULONG sequence_number;
+	ULONG io_control_code;
+	LARGE_INTEGER device_offset;
+	ULONG dump_data[1];
+};
+
+/* some of the functions below are slightly different from DDK's
+ * implementation; e.g., Insert functions return appropriate
+ * pointer */
+
+/* instead of using Linux's lists, we implement list manipulation
+ * functions because nt_list is used by drivers and we don't want to
+ * worry about Linux's list being different from nt_list (right now
+ * they are same, but in future they could be different) */
+
+static inline void InitializeListHead(struct nt_list *head)
+{
+	head->next = head->prev = head;
+}
+
+static inline BOOLEAN IsListEmpty(struct nt_list *head)
+{
+	if (head == head->next)
+		return TRUE;
+	else
+		return FALSE;
+}
+
+static inline void RemoveEntryList(struct nt_list *entry)
+{
+	entry->prev->next = entry->next;
+	entry->next->prev = entry->prev;
+}
+
+static inline struct nt_list *RemoveHeadList(struct nt_list *head)
+{
+	struct nt_list *entry;
+
+	entry = head->next;
+	if (entry == head)
+		return NULL;
+	else {
+		RemoveEntryList(entry);
+		return entry;
+	}
+}
+
+static inline struct nt_list *RemoveTailList(struct nt_list *head)
+{
+	struct nt_list *entry;
+
+	entry = head->prev;
+	if (entry == head)
+		return NULL;
+	else {
+		RemoveEntryList(entry);
+		return entry;
+	}
+}
+
+static inline void InsertListEntry(struct nt_list *entry, struct nt_list *prev,
+				   struct nt_list *next)
+{
+	next->prev = entry;
+	entry->next = next;
+	entry->prev = prev;
+	prev->next = entry;
+}
+
+static inline struct nt_list *InsertHeadList(struct nt_list *head,
+					     struct nt_list *entry)
+{
+	struct nt_list *ret;
+
+	if (IsListEmpty(head))
+		ret = NULL;
+	else
+		ret = head->next;
+
+	InsertListEntry(entry, head, head->next);
+	return ret;
+}
+
+static inline struct nt_list *InsertTailList(struct nt_list *head,
+					     struct nt_list *entry)
+{
+	struct nt_list *ret;
+
+	if (IsListEmpty(head))
+		ret = NULL;
+	else
+		ret = head->prev;
+
+	InsertListEntry(entry, head->prev, head);
+	return ret;
+}
+
+#define nt_list_for_each(pos, head)					\
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#define nt_list_for_each_entry(pos, head, member)			\
+	for (pos = container_of((head)->next, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = container_of(pos->member.next, typeof(*pos), member))
+
+#define nt_list_for_each_safe(pos, n, head)			\
+	for (pos = (head)->next, n = pos->next; pos != (head);	\
+	     pos = n, n = pos->next)
+
+/* device object flags */
+#define DO_VERIFY_VOLUME		0x00000002
+#define DO_BUFFERED_IO			0x00000004
+#define DO_EXCLUSIVE			0x00000008
+#define DO_DIRECT_IO			0x00000010
+#define DO_MAP_IO_BUFFER		0x00000020
+#define DO_DEVICE_HAS_NAME		0x00000040
+#define DO_DEVICE_INITIALIZING		0x00000080
+#define DO_SYSTEM_BOOT_PARTITION	0x00000100
+#define DO_LONG_TERM_REQUESTS		0x00000200
+#define DO_NEVER_LAST_DEVICE		0x00000400
+#define DO_SHUTDOWN_REGISTERED		0x00000800
+#define DO_BUS_ENUMERATED_DEVICE	0x00001000
+#define DO_POWER_PAGABLE		0x00002000
+#define DO_POWER_INRUSH			0x00004000
+#define DO_LOW_PRIORITY_FILESYSTEM	0x00010000
+
+/* Various supported device types (used with IoCreateDevice()) */
+
+#define FILE_DEVICE_BEEP		0x00000001
+#define FILE_DEVICE_CD_ROM		0x00000002
+#define FILE_DEVICE_CD_ROM_FILE_SYSTEM	0x00000003
+#define FILE_DEVICE_CONTROLLER		0x00000004
+#define FILE_DEVICE_DATALINK		0x00000005
+#define FILE_DEVICE_DFS			0x00000006
+#define FILE_DEVICE_DISK		0x00000007
+#define FILE_DEVICE_DISK_FILE_SYSTEM	0x00000008
+#define FILE_DEVICE_FILE_SYSTEM		0x00000009
+#define FILE_DEVICE_INPORT_PORT		0x0000000A
+#define FILE_DEVICE_KEYBOARD		0x0000000B
+#define FILE_DEVICE_MAILSLOT		0x0000000C
+#define FILE_DEVICE_MIDI_IN		0x0000000D
+#define FILE_DEVICE_MIDI_OUT		0x0000000E
+#define FILE_DEVICE_MOUSE		0x0000000F
+#define FILE_DEVICE_MULTI_UNC_PROVIDER	0x00000010
+#define FILE_DEVICE_NAMED_PIPE		0x00000011
+#define FILE_DEVICE_NETWORK		0x00000012
+#define FILE_DEVICE_NETWORK_BROWSER	0x00000013
+#define FILE_DEVICE_NETWORK_FILE_SYSTEM	0x00000014
+#define FILE_DEVICE_NULL		0x00000015
+#define FILE_DEVICE_PARALLEL_PORT	0x00000016
+#define FILE_DEVICE_PHYSICAL_NETCARD	0x00000017
+#define FILE_DEVICE_PRINTER		0x00000018
+#define FILE_DEVICE_SCANNER		0x00000019
+#define FILE_DEVICE_SERIAL_MOUSE_PORT	0x0000001A
+#define FILE_DEVICE_SERIAL_PORT		0x0000001B
+#define FILE_DEVICE_SCREEN		0x0000001C
+#define FILE_DEVICE_SOUND		0x0000001D
+#define FILE_DEVICE_STREAMS		0x0000001E
+#define FILE_DEVICE_TAPE		0x0000001F
+#define FILE_DEVICE_TAPE_FILE_SYSTEM	0x00000020
+#define FILE_DEVICE_TRANSPORT		0x00000021
+#define FILE_DEVICE_UNKNOWN		0x00000022
+#define FILE_DEVICE_VIDEO		0x00000023
+#define FILE_DEVICE_VIRTUAL_DISK	0x00000024
+#define FILE_DEVICE_WAVE_IN		0x00000025
+#define FILE_DEVICE_WAVE_OUT		0x00000026
+#define FILE_DEVICE_8042_PORT		0x00000027
+#define FILE_DEVICE_NETWORK_REDIRECTOR	0x00000028
+#define FILE_DEVICE_BATTERY		0x00000029
+#define FILE_DEVICE_BUS_EXTENDER	0x0000002A
+#define FILE_DEVICE_MODEM		0x0000002B
+#define FILE_DEVICE_VDM			0x0000002C
+#define FILE_DEVICE_MASS_STORAGE	0x0000002D
+#define FILE_DEVICE_SMB			0x0000002E
+#define FILE_DEVICE_KS			0x0000002F
+#define FILE_DEVICE_CHANGER		0x00000030
+#define FILE_DEVICE_SMARTCARD		0x00000031
+#define FILE_DEVICE_ACPI		0x00000032
+#define FILE_DEVICE_DVD			0x00000033
+#define FILE_DEVICE_FULLSCREEN_VIDEO	0x00000034
+#define FILE_DEVICE_DFS_FILE_SYSTEM	0x00000035
+#define FILE_DEVICE_DFS_VOLUME		0x00000036
+#define FILE_DEVICE_SERENUM		0x00000037
+#define FILE_DEVICE_TERMSRV		0x00000038
+#define FILE_DEVICE_KSEC		0x00000039
+#define FILE_DEVICE_FIPS		0x0000003A
+
+/* Device characteristics */
+
+#define FILE_REMOVABLE_MEDIA		0x00000001
+#define FILE_READ_ONLY_DEVICE		0x00000002
+#define FILE_FLOPPY_DISKETTE		0x00000004
+#define FILE_WRITE_ONCE_MEDIA		0x00000008
+#define FILE_REMOTE_DEVICE		0x00000010
+#define FILE_DEVICE_IS_MOUNTED		0x00000020
+#define FILE_VIRTUAL_VOLUME		0x00000040
+#define FILE_AUTOGENERATED_DEVICE_NAME	0x00000080
+#define FILE_DEVICE_SECURE_OPEN		0x00000100
+
+#define FILE_READ_DATA			0x0001
+#define FILE_WRITE_DATA			0x0002
+
+#define FILE_SUPERSEDED			0x00000000
+#define FILE_OPENED			0x00000001
+#define FILE_CREATED			0x00000002
+#define FILE_OVERWRITTEN		0x00000003
+#define FILE_EXISTS			0x00000004
+#define FILE_DOES_NOT_EXIST		0x00000005
+
+
+#endif /* WINNT_TYPES_H */
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/workqueue.c linux-5.6.11-ndis/3rdparty/ndiswrapper/workqueue.c
--- linux-5.6.11/3rdparty/ndiswrapper/workqueue.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/workqueue.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,279 @@
+/*
+ *  Copyright (C) 2006 Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include "ntoskernel.h"
+
+struct workqueue_thread {
+	spinlock_t lock;
+	struct task_struct *task;
+	struct completion *completion;
+	char name[16];
+	int pid;
+	/* whether any work_structs pending? <0 implies quit */
+	s8 pending;
+	/* list of work_structs pending */
+	struct list_head work_list;
+};
+
+struct workq_thread_data {
+	struct workqueue_struct *workq;
+	int index;
+};
+
+struct wrap_workqueue_struct {
+	u8 singlethread;
+	u8 qon;
+	int num_cpus;
+	struct workqueue_thread threads[0];
+};
+
+static void wrap_destroy_wq_on(struct workqueue_struct *workq, int cpu);
+
+static int workq_thread(void *data)
+{
+	struct workq_thread_data *thread_data = data;
+	struct workqueue_thread *thread;
+	struct workqueue_struct *workq;
+	struct work_struct *work;
+
+	workq = thread_data->workq;
+	thread = &workq->threads[thread_data->index];
+	WORKTRACE("%p, %d, %p", workq, thread_data->index, thread);
+	strncpy(thread->name, current->comm, sizeof(thread->name));
+
+	daemonize(thread->name);
+	set_user_nice(current, -5);
+
+	if (thread->task != current) {
+		WARNING("invalid task: %p, %p", thread->task, current);
+		thread->task = current;
+	}
+	thread->pid = current->pid;
+	complete(xchg(&thread->completion, NULL));
+	WORKTRACE("%s (%d) started", thread->name, thread->pid);
+	while (1) {
+		if (wait_condition(thread->pending, 0, TASK_INTERRUPTIBLE) < 0) {
+			/* TODO: deal with signal */
+			WARNING("signal not blocked?");
+			flush_signals(current);
+			continue;
+		}
+		while (1) {
+			struct list_head *entry;
+			unsigned long flags;
+
+			spin_lock_irqsave(&thread->lock, flags);
+			if (list_empty(&thread->work_list)) {
+				struct completion *completion;
+				if (thread->pending < 0) {
+					spin_unlock_irqrestore(&thread->lock,
+							       flags);
+					goto out;
+				}
+				thread->pending = 0;
+				completion = thread->completion;
+				thread->completion = NULL;
+				spin_unlock_irqrestore(&thread->lock, flags);
+				if (completion)
+					complete(completion);
+				break;
+			}
+			entry = thread->work_list.next;
+			work = list_entry(entry, struct work_struct, list);
+			if (xchg(&work->thread, NULL))
+				list_del(entry);
+			else
+				work = NULL;
+			spin_unlock_irqrestore(&thread->lock, flags);
+			DBG_BLOCK(4) {
+				WORKTRACE("%p, %p", work, thread);
+			}
+			if (work)
+				work->func(work->data);
+		}
+	}
+
+out:
+	WORKTRACE("%s exiting", thread->name);
+	thread->pid = 0;
+	return 0;
+}
+
+static int wrap_queue_work_on(struct workqueue_struct *workq,
+			      struct work_struct *work, int cpu)
+{
+	struct workqueue_thread *thread = &workq->threads[cpu];
+	unsigned long flags;
+	int ret;
+
+	assert(thread->pid > 0);
+	DBG_BLOCK(4) {
+		WORKTRACE("%p, %d", workq, cpu);
+	}
+	spin_lock_irqsave(&thread->lock, flags);
+	if (work->thread)
+		ret = 0;
+	else {
+		work->thread = thread;
+		list_add_tail(&work->list, &thread->work_list);
+		thread->pending = 1;
+		wake_up_process(thread->task);
+		ret = 1;
+	}
+	spin_unlock_irqrestore(&thread->lock, flags);
+	return ret;
+}
+
+int wrap_queue_work(struct workqueue_struct *workq, struct work_struct *work)
+{
+	if (num_online_cpus() == 1 || workq->singlethread)
+		return wrap_queue_work_on(workq, work, 0);
+	else {
+		typeof(workq->qon) qon;
+		/* work is queued on threads in a round-robin fashion */
+		do {
+			qon = workq->qon % workq->num_cpus;
+			atomic_inc_var(workq->qon);
+		} while (!workq->threads[qon].pid);
+		return wrap_queue_work_on(workq, work, qon);
+	}
+}
+
+void wrap_cancel_work(struct work_struct *work)
+{
+	struct workqueue_thread *thread;
+	unsigned long flags;
+
+	WORKTRACE("%p", work);
+	if ((thread = xchg(&work->thread, NULL))) {
+		WORKTRACE("%p", thread);
+		spin_lock_irqsave(&thread->lock, flags);
+		list_del(&work->list);
+		spin_unlock_irqrestore(&thread->lock, flags);
+	}
+}
+
+struct workqueue_struct *wrap_create_wq(const char *name, u8 singlethread,
+					u8 freeze)
+{
+	struct completion started;
+	struct workqueue_struct *workq;
+	int i, n;
+
+	if (singlethread)
+		n = 1;
+	else
+		n = num_online_cpus();
+	workq = kzalloc(sizeof(*workq) + n * sizeof(workq->threads[0]),
+			GFP_KERNEL);
+	if (!workq) {
+		WARNING("couldn't allocate memory");
+		return NULL;
+	}
+	WORKTRACE("%p", workq);
+	workq->singlethread = singlethread;
+	init_completion(&started);
+	for_each_online_cpu(i) {
+		struct workq_thread_data thread_data;
+		spin_lock_init(&workq->threads[i].lock);
+		INIT_LIST_HEAD(&workq->threads[i].work_list);
+		reinit_completion(&started);
+		workq->threads[i].completion = &started;
+		thread_data.workq = workq;
+		thread_data.index = i;
+		WORKTRACE("%p, %d, %p", workq, i, &workq->threads[i]);
+		workq->threads[i].task =
+			kthread_create(workq_thread, &thread_data,
+				       "%s/%d", name, i);
+		if (IS_ERR(workq->threads[i].task)) {
+			int j;
+			for (j = 0; j < i; j++)
+				wrap_destroy_wq_on(workq, j);
+			kfree(workq);
+			WARNING("couldn't start thread %s", name);
+			return NULL;
+		}
+#ifdef PF_NOFREEZE
+		if (!freeze)
+			workq->threads[i].task->flags |= PF_NOFREEZE;
+#endif
+		kthread_bind(workq->threads[i].task, i);
+		workq->num_cpus = max(workq->num_cpus, i);
+		wake_up_process(workq->threads[i].task);
+		wait_for_completion(&started);
+		WORKTRACE("%s, %d: %p, %d", name, i,
+			  workq, workq->threads[i].pid);
+		if (singlethread)
+			break;
+	}
+	workq->num_cpus++;
+	return workq;
+}
+
+static void wrap_flush_wq_on(struct workqueue_struct *workq, int cpu)
+{
+	struct workqueue_thread *thread = &workq->threads[cpu];
+	struct completion done;
+
+	WORKTRACE("%p: %d, %s", workq, cpu, thread->name);
+	init_completion(&done);
+	thread->completion = &done;
+	thread->pending = 1;
+	wake_up_process(thread->task);
+	wait_for_completion(&done);
+	return;
+}
+
+void wrap_flush_wq(struct workqueue_struct *workq)
+{
+	int i, n;
+
+	WORKTRACE("%p", workq);
+	if (workq->singlethread)
+		n = 1;
+	else
+		n = num_online_cpus();
+	for (i = 0; i < n; i++)
+		wrap_flush_wq_on(workq, i);
+}
+
+static void wrap_destroy_wq_on(struct workqueue_struct *workq, int cpu)
+{
+	struct workqueue_thread *thread = &workq->threads[cpu];
+
+	WORKTRACE("%p: %d, %s", workq, cpu, thread->name);
+	if (!thread->pid)
+		return;
+	thread->pending = -1;
+	wake_up_process(thread->task);
+	while (thread->pid) {
+		WORKTRACE("%d", thread->pid);
+		schedule();
+	}
+}
+
+void wrap_destroy_wq(struct workqueue_struct *workq)
+{
+	int i, n;
+
+	WORKTRACE("%p", workq);
+	if (workq->singlethread)
+		n = 1;
+	else
+		n = num_online_cpus();
+	for (i = 0; i < n; i++)
+		wrap_destroy_wq_on(workq, i);
+	kfree(workq);
+}
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/wrapmem.c linux-5.6.11-ndis/3rdparty/ndiswrapper/wrapmem.c
--- linux-5.6.11/3rdparty/ndiswrapper/wrapmem.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/wrapmem.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,366 @@
+/*
+ *  Copyright (C) 2006 Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#define _WRAPMEM_C_
+
+#include "ntoskernel.h"
+#include "wrapmem.h"
+
+struct slack_alloc_info {
+	struct nt_list list;
+	size_t size;
+};
+
+#if ALLOC_DEBUG > 1
+static struct nt_list allocs;
+#endif
+
+static struct nt_list slack_allocs;
+static spinlock_t alloc_lock;
+
+#if ALLOC_DEBUG
+const char *alloc_type_name[ALLOC_TYPE_MAX] = {
+	"kmalloc_atomic",
+	"kmalloc_nonatomic",
+	"vmalloc_atomic",
+	"vmalloc_nonatomic",
+	"kmalloc_slack",
+	"pages"
+};
+
+struct alloc_info {
+	enum alloc_type type;
+	size_t size;
+#if ALLOC_DEBUG > 1
+	struct nt_list list;
+	const char *file;
+	int line;
+	ULONG tag;
+#endif
+};
+
+static atomic_t alloc_sizes[ALLOC_TYPE_MAX];
+#endif
+
+/* allocate memory and add it to list of allocated pointers; if a
+ * driver doesn't free this memory for any reason (buggy driver or we
+ * allocate space behind driver's back since we need more space than
+ * corresponding Windows structure provides etc.), this gets freed
+ * automatically when module is unloaded
+ */
+void *slack_kmalloc(size_t size)
+{
+	struct slack_alloc_info *info;
+
+	ENTER4("size = %zu", size);
+	info = kmalloc(size + sizeof(*info), irql_gfp());
+	if (!info)
+		return NULL;
+	info->size = size;
+	spin_lock_bh(&alloc_lock);
+	InsertTailList(&slack_allocs, &info->list);
+	spin_unlock_bh(&alloc_lock);
+#if ALLOC_DEBUG
+	atomic_add(size, &alloc_sizes[ALLOC_TYPE_SLACK]);
+#endif
+	TRACE4("%p, %p", info, info + 1);
+	EXIT4(return info + 1);
+}
+
+/* free pointer and remove from list of allocated pointers */
+void slack_kfree(void *ptr)
+{
+	struct slack_alloc_info *info;
+
+	ENTER4("%p", ptr);
+	info = ptr - sizeof(*info);
+	spin_lock_bh(&alloc_lock);
+	RemoveEntryList(&info->list);
+	spin_unlock_bh(&alloc_lock);
+#if ALLOC_DEBUG
+	atomic_sub(info->size, &alloc_sizes[ALLOC_TYPE_SLACK]);
+#endif
+	kfree(info);
+	EXIT4(return);
+}
+
+void *slack_kzalloc(size_t size)
+{
+	void *ptr = slack_kmalloc(size);
+	if (ptr)
+		memset(ptr, 0, size);
+	return ptr;
+}
+
+#if ALLOC_DEBUG
+void *wrap_kmalloc(size_t size, gfp_t flags, const char *file, int line)
+{
+	struct alloc_info *info;
+
+	info = kmalloc(size + sizeof(*info), flags);
+	if (!info)
+		return NULL;
+	if (flags & GFP_ATOMIC)
+		info->type = ALLOC_TYPE_KMALLOC_ATOMIC;
+	else
+		info->type = ALLOC_TYPE_KMALLOC_NON_ATOMIC;
+	info->size = size;
+	atomic_add(size, &alloc_sizes[info->type]);
+#if ALLOC_DEBUG > 1
+	info->file = file;
+	info->line = line;
+	info->tag = 0;
+	spin_lock_bh(&alloc_lock);
+	InsertTailList(&allocs, &info->list);
+	spin_unlock_bh(&alloc_lock);
+#endif
+	TRACE4("%p", info + 1);
+	return info + 1;
+}
+
+void *wrap_kzalloc(size_t size, gfp_t flags, const char *file, int line)
+{
+	void *ptr = wrap_kmalloc(size, flags, file, line);
+	if (ptr)
+		memset(ptr, 0, size);
+	return ptr;
+}
+
+void wrap_kfree(void *ptr)
+{
+	struct alloc_info *info;
+
+	TRACE4("%p", ptr);
+	if (!ptr)
+		return;
+	info = ptr - sizeof(*info);
+	atomic_sub(info->size, &alloc_sizes[info->type]);
+#if ALLOC_DEBUG > 1
+	spin_lock_bh(&alloc_lock);
+	RemoveEntryList(&info->list);
+	spin_unlock_bh(&alloc_lock);
+	if (!(info->type == ALLOC_TYPE_KMALLOC_ATOMIC ||
+	      info->type == ALLOC_TYPE_KMALLOC_NON_ATOMIC)) {
+		WARNING("invalid type: %d", info->type);
+		return;
+	}
+#endif
+	kfree(info);
+}
+
+void *wrap_vmalloc(unsigned long size, const char *file, int line)
+{
+	struct alloc_info *info;
+
+	info = vmalloc(size + sizeof(*info));
+	if (!info)
+		return NULL;
+	info->type = ALLOC_TYPE_VMALLOC_NON_ATOMIC;
+	info->size = size;
+	atomic_add(size, &alloc_sizes[info->type]);
+#if ALLOC_DEBUG > 1
+	info->file = file;
+	info->line = line;
+	info->tag = 0;
+	spin_lock_bh(&alloc_lock);
+	InsertTailList(&allocs, &info->list);
+	spin_unlock_bh(&alloc_lock);
+#endif
+	return info + 1;
+}
+
+void *wrap__vmalloc(unsigned long size, gfp_t gfp_mask,
+		    const char *file, int line)
+{
+	struct alloc_info *info;
+
+	info = __vmalloc(size + sizeof(*info), gfp_mask);
+	if (!info)
+		return NULL;
+	if (gfp_mask & GFP_ATOMIC)
+		info->type = ALLOC_TYPE_VMALLOC_ATOMIC;
+	else
+		info->type = ALLOC_TYPE_VMALLOC_NON_ATOMIC;
+	info->size = size;
+	atomic_add(size, &alloc_sizes[info->type]);
+#if ALLOC_DEBUG > 1
+	info->file = file;
+	info->line = line;
+	info->tag = 0;
+	spin_lock_bh(&alloc_lock);
+	InsertTailList(&allocs, &info->list);
+	spin_unlock_bh(&alloc_lock);
+#endif
+	return info + 1;
+}
+
+void wrap_vfree(void *ptr)
+{
+	struct alloc_info *info;
+
+	info = ptr - sizeof(*info);
+	atomic_sub(info->size, &alloc_sizes[info->type]);
+#if ALLOC_DEBUG > 1
+	spin_lock_bh(&alloc_lock);
+	RemoveEntryList(&info->list);
+	spin_unlock_bh(&alloc_lock);
+	if (!(info->type == ALLOC_TYPE_VMALLOC_ATOMIC ||
+	      info->type == ALLOC_TYPE_VMALLOC_NON_ATOMIC)) {
+		WARNING("invalid type: %d", info->type);
+		return;
+	}
+#endif
+	vfree(info);
+}
+
+void *wrap_alloc_pages(gfp_t flags, unsigned int size,
+		       const char *file, int line)
+{
+	struct alloc_info *info;
+
+	size += sizeof(*info);
+	info = (struct alloc_info *)__get_free_pages(flags, get_order(size));
+	if (!info)
+		return NULL;
+	info->type = ALLOC_TYPE_PAGES;
+	info->size = size;
+	atomic_add(size, &alloc_sizes[info->type]);
+#if ALLOC_DEBUG > 1
+	info->file = file;
+	info->line = line;
+	info->tag = 0;
+	spin_lock_bh(&alloc_lock);
+	InsertTailList(&allocs, &info->list);
+	spin_unlock_bh(&alloc_lock);
+#endif
+	return info + 1;
+}
+
+void wrap_free_pages(unsigned long ptr, int order)
+{
+	struct alloc_info *info;
+
+	info = (void *)ptr - sizeof(*info);
+	atomic_sub(info->size, &alloc_sizes[info->type]);
+#if ALLOC_DEBUG > 1
+	spin_lock_bh(&alloc_lock);
+	RemoveEntryList(&info->list);
+	spin_unlock_bh(&alloc_lock);
+	if (info->type != ALLOC_TYPE_PAGES) {
+		WARNING("invalid type: %d", info->type);
+		return;
+	}
+#endif
+	free_pages((unsigned long)info, get_order(info->size));
+}
+
+#if ALLOC_DEBUG > 1
+void *wrap_ExAllocatePoolWithTag(enum pool_type pool_type, SIZE_T size,
+				 ULONG tag, const char *file, int line)
+{
+	void *addr;
+	struct alloc_info *info;
+
+	ENTER4("pool_type: %d, size: %zu, tag: %u", pool_type, size, tag);
+	addr = (ExAllocatePoolWithTag)(pool_type, size, tag);
+	if (!addr)
+		return NULL;
+	info = addr - sizeof(*info);
+	info->file = file;
+	info->line = line;
+	info->tag = tag;
+	EXIT4(return addr);
+}
+#endif
+
+int alloc_size(enum alloc_type type)
+{
+	if ((int)type >= 0 && type < ALLOC_TYPE_MAX)
+		return atomic_read(&alloc_sizes[type]);
+	else
+		return -EINVAL;
+}
+
+#endif // ALLOC_DEBUG
+
+int wrapmem_init(void)
+{
+#if ALLOC_DEBUG > 1
+	InitializeListHead(&allocs);
+#endif
+	InitializeListHead(&slack_allocs);
+	spin_lock_init(&alloc_lock);
+	return 0;
+}
+
+void wrapmem_exit(void)
+{
+#if ALLOC_DEBUG
+	enum alloc_type type;
+#endif
+	struct nt_list *ent;
+
+	/* free all pointers on the slack list */
+	while (1) {
+		struct slack_alloc_info *info;
+		spin_lock_bh(&alloc_lock);
+		ent = RemoveHeadList(&slack_allocs);
+		spin_unlock_bh(&alloc_lock);
+		if (!ent)
+			break;
+		info = container_of(ent, struct slack_alloc_info, list);
+#if ALLOC_DEBUG
+		atomic_sub(info->size, &alloc_sizes[ALLOC_TYPE_SLACK]);
+#endif
+		kfree(info);
+	}
+#if ALLOC_DEBUG
+	for (type = 0; type < ALLOC_TYPE_MAX; type++) {
+		int n = atomic_read(&alloc_sizes[type]);
+		if (n)
+			WARNING("%d bytes of memory in %s leaking", n,
+				alloc_type_name[type]);
+	}
+
+#if ALLOC_DEBUG > 1
+	while (1) {
+		struct alloc_info *info;
+
+		spin_lock_bh(&alloc_lock);
+		ent = RemoveHeadList(&allocs);
+		spin_unlock_bh(&alloc_lock);
+		if (!ent)
+			break;
+		info = container_of(ent, struct alloc_info, list);
+		atomic_sub(info->size, &alloc_sizes[ALLOC_TYPE_SLACK]);
+		printk(KERN_DEBUG DRIVER_NAME
+		       ": %s:%d leaked %zd bytes at %p (%s, tag 0x%08X)\n",
+		       info->file, info->line, info->size, info + 1,
+		       alloc_type_name[info->type], info->tag);
+		if (info->type == ALLOC_TYPE_KMALLOC_ATOMIC ||
+		    info->type == ALLOC_TYPE_KMALLOC_NON_ATOMIC)
+			kfree(info);
+		else if (info->type == ALLOC_TYPE_VMALLOC_ATOMIC ||
+			 info->type == ALLOC_TYPE_VMALLOC_NON_ATOMIC)
+			vfree(info);
+		else if (info->type == ALLOC_TYPE_PAGES)
+			free_pages((unsigned long)info, get_order(info->size));
+		else
+			WARNING("invalid type: %d; not freed", info->type);
+	}
+#endif
+#endif
+	return;
+}
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/wrapmem.h linux-5.6.11-ndis/3rdparty/ndiswrapper/wrapmem.h
--- linux-5.6.11/3rdparty/ndiswrapper/wrapmem.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/wrapmem.h	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,97 @@
+/*
+ *  Copyright (C) 2006 Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#ifndef _WRAPMEM_H_
+#define _WRAPMEM_H_
+
+/*
+ * Set ALLOC_DEBUG to 1 to show information about memory used by both
+ * ndiswrapper and Windows driver in /proc/net/ndiswrapper/debug
+ * This will also show memory leaks (memory allocated but not freed) when
+ * ndiswrapper module is unloaded.
+ *
+ * Set ALLOC_DEBUG to 2 to see details about every leaking allocation.
+*/
+
+#ifndef ALLOC_DEBUG
+#define ALLOC_DEBUG 0
+#endif
+
+int wrapmem_init(void);
+void wrapmem_exit(void);
+void *slack_kmalloc(size_t size);
+void *slack_kzalloc(size_t size);
+void slack_kfree(void *ptr);
+
+#if ALLOC_DEBUG
+enum alloc_type { ALLOC_TYPE_KMALLOC_ATOMIC,
+		  ALLOC_TYPE_KMALLOC_NON_ATOMIC,
+		  ALLOC_TYPE_VMALLOC_ATOMIC, ALLOC_TYPE_VMALLOC_NON_ATOMIC,
+		  ALLOC_TYPE_SLACK, ALLOC_TYPE_PAGES, ALLOC_TYPE_MAX };
+
+extern const char *alloc_type_name[ALLOC_TYPE_MAX];
+
+void *wrap_kmalloc(size_t size, gfp_t flags, const char *file, int line);
+void *wrap_kzalloc(size_t size, gfp_t flags, const char *file, int line);
+void wrap_kfree(void *ptr);
+void *wrap_vmalloc(unsigned long size, const char *file, int line);
+void *wrap__vmalloc(unsigned long size, gfp_t flags, 
+		    const char *file, int line);
+void wrap_vfree(void *ptr);
+void *wrap_alloc_pages(gfp_t flags, unsigned int size,
+		       const char *file, int line);
+void wrap_free_pages(unsigned long ptr, int order);
+int alloc_size(enum alloc_type type);
+
+#if ALLOC_DEBUG > 1
+void *wrap_ExAllocatePoolWithTag(enum pool_type pool_type, SIZE_T size,
+				 ULONG tag, const char *file, int line);
+#define ExAllocatePoolWithTag(pool_type, size, tag)			\
+	wrap_ExAllocatePoolWithTag(pool_type, size, tag, __FILE__, __LINE__)
+#endif
+
+#ifndef _WRAPMEM_C_
+#undef kmalloc
+#undef kzalloc
+#undef kfree
+#undef vmalloc
+#undef __vmalloc
+#undef vfree
+#define kmalloc(size, flags)				\
+	wrap_kmalloc(size, flags, __FILE__, __LINE__)
+#define kzalloc(size, flags)				\
+	wrap_kzalloc(size, flags, __FILE__, __LINE__)
+#define vmalloc(size)				\
+	wrap_vmalloc(size, __FILE__, __LINE__)
+#define __vmalloc(size, flags)				\
+	wrap__vmalloc(size, flags, __FILE__, __LINE__)
+#define kfree(ptr) wrap_kfree(ptr)
+#define vfree(ptr) wrap_vfree(ptr)
+
+#define wrap_get_free_pages(flags, size)			\
+	wrap_alloc_pages(flags, size, __FILE__, __LINE__)
+#undef free_pages
+#define free_pages(ptr, order) wrap_free_pages(ptr, order)
+
+#endif // _WRAPMEM_C_
+
+#else
+
+#define wrap_get_free_pages(flags, size)			\
+	(void *)__get_free_pages(flags, get_order(size))
+
+#endif // ALLOC_DEBUG
+
+#endif
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/wrapndis.c linux-5.6.11-ndis/3rdparty/ndiswrapper/wrapndis.c
--- linux-5.6.11/3rdparty/ndiswrapper/wrapndis.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/wrapndis.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,2223 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/proc_fs.h>
+#include "ndis.h"
+#include "iw_ndis.h"
+#include "pnp.h"
+#include "loader.h"
+#include "wrapndis.h"
+#include "wrapper.h"
+
+/* Functions callable from the NDIS driver */
+wstdcall NTSTATUS NdisDispatchDeviceControl(struct device_object *fdo,
+					    struct irp *irp);
+wstdcall NTSTATUS NdisDispatchPnp(struct device_object *fdo, struct irp *irp);
+wstdcall NTSTATUS NdisDispatchPower(struct device_object *fdo, struct irp *irp);
+
+struct workqueue_struct *wrapndis_wq;
+
+static int set_packet_filter(struct ndis_device *wnd,
+			     ULONG packet_filter);
+static void add_iw_stats_timer(struct ndis_device *wnd);
+static void del_iw_stats_timer(struct ndis_device *wnd);
+static NDIS_STATUS ndis_start_device(struct ndis_device *wnd);
+static int ndis_remove_device(struct ndis_device *wnd);
+static void set_multicast_list(struct ndis_device *wnd);
+static int ndis_net_dev_open(struct net_device *net_dev);
+static int ndis_net_dev_close(struct net_device *net_dev);
+
+/* MiniportReset */
+NDIS_STATUS mp_reset(struct ndis_device *wnd)
+{
+	NDIS_STATUS res;
+	struct miniport *mp;
+	BOOLEAN reset_address;
+	KIRQL irql;
+
+	ENTER2("wnd: %p", wnd);
+	mutex_lock(&wnd->tx_ring_mutex);
+	mutex_lock(&wnd->ndis_req_mutex);
+	mp = &wnd->wd->driver->ndis_driver->mp;
+	prepare_wait_condition(wnd->ndis_req_task, wnd->ndis_req_done, 0);
+	WARNING("%s is being reset", wnd->net_dev->name);
+	irql = serialize_lock_irql(wnd);
+	assert_irql(_irql_ == DISPATCH_LEVEL);
+	res = LIN2WIN2(mp->reset, &reset_address, wnd->nmb->mp_ctx);
+	serialize_unlock_irql(wnd, irql);
+
+	TRACE2("%08X, %08X", res, reset_address);
+	if (res == NDIS_STATUS_PENDING) {
+		/* wait for NdisMResetComplete */
+		if (wait_condition((wnd->ndis_req_done > 0), 0,
+				   TASK_INTERRUPTIBLE) < 0)
+			res = NDIS_STATUS_FAILURE;
+		else {
+			res = wnd->ndis_req_status;
+			reset_address = wnd->ndis_req_done - 1;
+		}
+		TRACE2("%08X, %08X", res, reset_address);
+	}
+	mutex_unlock(&wnd->ndis_req_mutex);
+	if (res == NDIS_STATUS_SUCCESS && reset_address) {
+		set_packet_filter(wnd, wnd->packet_filter);
+		set_multicast_list(wnd);
+	}
+	mutex_unlock(&wnd->tx_ring_mutex);
+	EXIT3(return res);
+}
+
+/* MiniportRequest(Query/Set)Information */
+NDIS_STATUS mp_request(enum ndis_request_type request,
+		       struct ndis_device *wnd, ndis_oid oid,
+		       void *buf, ULONG buflen, ULONG *written, ULONG *needed)
+{
+	NDIS_STATUS res;
+	ULONG w, n;
+	struct miniport *mp;
+	KIRQL irql;
+
+	mutex_lock(&wnd->ndis_req_mutex);
+	if (!written)
+		written = &w;
+	if (!needed)
+		needed = &n;
+	mp = &wnd->wd->driver->ndis_driver->mp;
+	prepare_wait_condition(wnd->ndis_req_task, wnd->ndis_req_done, 0);
+	irql = serialize_lock_irql(wnd);
+	assert_irql(_irql_ == DISPATCH_LEVEL);
+	switch (request) {
+	case NdisRequestQueryInformation:
+		TRACE2("%p, %08X, %p", mp->queryinfo, oid, wnd->nmb->mp_ctx);
+		res = LIN2WIN6(mp->queryinfo, wnd->nmb->mp_ctx, oid, buf,
+			       buflen, written, needed);
+		break;
+	case NdisRequestSetInformation:
+		TRACE2("%p, %08X, %p", mp->setinfo, oid, wnd->nmb->mp_ctx);
+		res = LIN2WIN6(mp->setinfo, wnd->nmb->mp_ctx, oid, buf,
+			       buflen, written, needed);
+		break;
+	default:
+		WARNING("invalid request %d, %08X", request, oid);
+		res = NDIS_STATUS_NOT_SUPPORTED;
+		break;
+	}
+	serialize_unlock_irql(wnd, irql);
+	TRACE2("%08X, %08X", res, oid);
+	if (res == NDIS_STATUS_PENDING) {
+		/* wait for NdisMQueryInformationComplete */
+		if (wait_condition((wnd->ndis_req_done > 0), 0,
+				   TASK_INTERRUPTIBLE) < 0)
+			res = NDIS_STATUS_FAILURE;
+		else
+			res = wnd->ndis_req_status;
+		TRACE2("%08X, %08X", res, oid);
+	}
+	mutex_unlock(&wnd->ndis_req_mutex);
+	DBG_BLOCK(2) {
+		if (res || needed)
+			TRACE2("%08X, %d, %d, %d", res, buflen, *written,
+			       *needed);
+	}
+	EXIT3(return res);
+}
+
+/* MiniportPnPEventNotify */
+static NDIS_STATUS mp_pnp_event(struct ndis_device *wnd,
+				enum ndis_device_pnp_event event,
+				ULONG power_profile)
+{
+	struct miniport *mp;
+
+	ENTER1("%p, %d", wnd, event);
+	mp = &wnd->wd->driver->ndis_driver->mp;
+	if (!mp->pnp_event_notify) {
+		TRACE1("Windows driver %s doesn't support "
+		       "MiniportPnpEventNotify", wnd->wd->driver->name);
+		return NDIS_STATUS_FAILURE;
+	}
+	/* RNDIS driver doesn't like to be notified if device is
+	 * already halted */
+	if (!test_bit(HW_INITIALIZED, &wnd->wd->hw_status))
+		EXIT1(return NDIS_STATUS_SUCCESS);
+	switch (event) {
+	case NdisDevicePnPEventSurpriseRemoved:
+		TRACE1("%u, %p",
+		       (wnd->attributes & NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK),
+		       mp->pnp_event_notify);
+		if ((wnd->attributes & NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK) &&
+		    !test_bit(HW_DISABLED, &wnd->wd->hw_status) &&
+		    mp->pnp_event_notify) {
+			TRACE1("calling surprise_removed");
+			LIN2WIN4(mp->pnp_event_notify, wnd->nmb->mp_ctx,
+				 NdisDevicePnPEventSurpriseRemoved, NULL, 0);
+		} else
+			TRACE1("Windows driver %s doesn't support "
+			       "MiniportPnpEventNotify for safe unplugging",
+			       wnd->wd->driver->name);
+		return NDIS_STATUS_SUCCESS;
+	case NdisDevicePnPEventPowerProfileChanged:
+		if (power_profile)
+			power_profile = NdisPowerProfileAcOnLine;
+		LIN2WIN4(mp->pnp_event_notify, wnd->nmb->mp_ctx,
+			 NdisDevicePnPEventPowerProfileChanged,
+			 &power_profile, sizeof(power_profile));
+		return NDIS_STATUS_SUCCESS;
+	default:
+		WARNING("event %d not yet implemented", event);
+		return NDIS_STATUS_SUCCESS;
+	}
+}
+
+/* MiniportInitialize */
+static NDIS_STATUS mp_init(struct ndis_device *wnd)
+{
+	NDIS_STATUS error_status, status;
+	UINT medium_index;
+	enum ndis_medium medium_array[] = {NdisMedium802_3};
+	struct miniport *mp;
+
+	ENTER1("irql: %d", current_irql());
+	if (test_bit(HW_INITIALIZED, &wnd->wd->hw_status)) {
+		WARNING("device %p already initialized!", wnd);
+		return NDIS_STATUS_FAILURE;
+	}
+
+	if (!wnd->wd->driver->ndis_driver ||
+	    !wnd->wd->driver->ndis_driver->mp.init) {
+		WARNING("assuming WDM (non-NDIS) driver");
+		EXIT1(return NDIS_STATUS_NOT_RECOGNIZED);
+	}
+	mp = &wnd->wd->driver->ndis_driver->mp;
+	status = LIN2WIN6(mp->init, &error_status, &medium_index, medium_array,
+			  ARRAY_SIZE(medium_array), wnd->nmb, wnd->nmb);
+	TRACE1("init returns: %08X, irql: %d", status, current_irql());
+	if (status != NDIS_STATUS_SUCCESS) {
+		WARNING("couldn't initialize device: %08X", status);
+		EXIT1(return NDIS_STATUS_FAILURE);
+	}
+
+	/* Wait a little to let card power up otherwise ifup might
+	 * fail after boot */
+	sleep_hz(HZ / 5);
+	status = mp_pnp_event(wnd, NdisDevicePnPEventPowerProfileChanged,
+			      NdisPowerProfileAcOnLine);
+	if (status != NDIS_STATUS_SUCCESS)
+		TRACE1("setting power failed: %08X", status);
+	set_bit(HW_INITIALIZED, &wnd->wd->hw_status);
+	/* the description about NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND is
+	 * misleading/confusing */
+	status = mp_query(wnd, OID_PNP_CAPABILITIES,
+			  &wnd->pnp_capa, sizeof(wnd->pnp_capa));
+	if (status == NDIS_STATUS_SUCCESS) {
+		TRACE1("%d, %d", wnd->pnp_capa.wakeup.min_magic_packet_wakeup,
+		       wnd->pnp_capa.wakeup.min_pattern_wakeup);
+		wnd->attributes |= NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND;
+		status = mp_query_int(wnd, OID_PNP_ENABLE_WAKE_UP,
+				      &wnd->ndis_wolopts);
+		TRACE1("%08X, %x", status, wnd->ndis_wolopts);
+	} else if (status == NDIS_STATUS_NOT_SUPPORTED)
+		wnd->attributes &= ~NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND;
+	TRACE1("%d", wnd->pnp_capa.wakeup.min_magic_packet_wakeup);
+	/* although some NDIS drivers support suspend, Linux kernel
+	 * has issues with suspending USB devices */
+	if (wrap_is_usb_bus(wnd->wd->dev_bus)) {
+		wnd->attributes &= ~NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND;
+		wnd->ndis_wolopts = 0;
+	}
+	mp_set_int(wnd, OID_802_11_POWER_MODE, NDIS_POWER_OFF);
+	EXIT1(return NDIS_STATUS_SUCCESS);
+}
+
+/* MiniportHalt */
+static void mp_halt(struct ndis_device *wnd)
+{
+	struct miniport *mp;
+
+	ENTER1("%p", wnd);
+	if (!test_and_clear_bit(HW_INITIALIZED, &wnd->wd->hw_status)) {
+		WARNING("device %p is not initialized - not halting", wnd);
+		return;
+	}
+	hangcheck_del(wnd);
+	del_iw_stats_timer(wnd);
+#ifdef CONFIG_WIRELESS_EXT
+	if (wnd->physical_medium == NdisPhysicalMediumWirelessLan &&
+	    wrap_is_pci_bus(wnd->wd->dev_bus)) {
+		mutex_unlock(&wnd->ndis_req_mutex);
+		disassociate(wnd, 0);
+		mutex_lock(&wnd->ndis_req_mutex);
+	}
+#endif
+	mp = &wnd->wd->driver->ndis_driver->mp;
+	TRACE1("halt: %p", mp->mp_halt);
+	LIN2WIN1(mp->mp_halt, wnd->nmb->mp_ctx);
+	/* if a driver doesn't call NdisMDeregisterInterrupt during
+	 * halt, deregister it now */
+	if (wnd->mp_interrupt)
+		NdisMDeregisterInterrupt(wnd->mp_interrupt);
+	/* cancel any timers left by buggy windows driver; also free
+	 * the memory for timers */
+	while (1) {
+		struct nt_slist *slist;
+		struct wrap_timer *wrap_timer;
+
+		spin_lock_bh(&ntoskernel_lock);
+		if ((slist = wnd->wrap_timer_slist.next))
+			wnd->wrap_timer_slist.next = slist->next;
+		spin_unlock_bh(&ntoskernel_lock);
+		TIMERTRACE("%p", slist);
+		if (!slist)
+			break;
+		wrap_timer = container_of(slist, struct wrap_timer, slist);
+		wrap_timer->repeat = 0;
+		/* ktimer that this wrap_timer is associated to can't
+		 * be touched, as it may have been freed by the driver
+		 * already */
+		if (del_timer_sync(&wrap_timer->timer))
+			WARNING("Buggy Windows driver left timer %p "
+				"running", wrap_timer->nt_timer);
+		memset(wrap_timer, 0, sizeof(*wrap_timer));
+		kfree(wrap_timer);
+	}
+	EXIT1(return);
+}
+
+static NDIS_STATUS mp_set_power_state(struct ndis_device *wnd,
+				      enum ndis_power_state state)
+{
+	NDIS_STATUS status;
+
+	TRACE1("%d", state);
+	if (state == NdisDeviceStateD0) {
+		status = NDIS_STATUS_SUCCESS;
+		mutex_unlock(&wnd->ndis_req_mutex);
+		if (test_and_clear_bit(HW_HALTED, &wnd->wd->hw_status)) {
+			status = mp_init(wnd);
+			if (status == NDIS_STATUS_SUCCESS) {
+				set_packet_filter(wnd, wnd->packet_filter);
+				set_multicast_list(wnd);
+			}
+		} else if (test_and_clear_bit(HW_SUSPENDED,
+					      &wnd->wd->hw_status)) {
+			status = mp_set_int(wnd, OID_PNP_SET_POWER, state);
+			if (status != NDIS_STATUS_SUCCESS)
+				WARNING("%s: setting power to state %d failed? "
+					"%08X", wnd->net_dev->name, state,
+					status);
+		} else
+			return NDIS_STATUS_FAILURE;
+
+		if (wrap_is_pci_bus(wnd->wd->dev_bus)) {
+			pci_enable_wake(wnd->wd->pci.pdev, PCI_D3hot, 0);
+			pci_enable_wake(wnd->wd->pci.pdev, PCI_D3cold, 0);
+		}
+		if (status == NDIS_STATUS_SUCCESS) {
+			mutex_unlock(&wnd->tx_ring_mutex);
+			netif_device_attach(wnd->net_dev);
+			hangcheck_add(wnd);
+			add_iw_stats_timer(wnd);
+		} else
+			WARNING("%s: couldn't set power to state %d; device not"
+				" resumed", wnd->net_dev->name, state);
+		EXIT1(return status);
+	} else {
+		mutex_lock(&wnd->tx_ring_mutex);
+		netif_device_detach(wnd->net_dev);
+		hangcheck_del(wnd);
+		del_iw_stats_timer(wnd);
+		status = NDIS_STATUS_NOT_SUPPORTED;
+		if (wnd->attributes & NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND) {
+			status = mp_set_int(wnd, OID_PNP_ENABLE_WAKE_UP,
+					    wnd->ndis_wolopts);
+			TRACE2("0x%x, 0x%x", status, wnd->ndis_wolopts);
+			if (status == NDIS_STATUS_SUCCESS &&
+			    wrap_is_pci_bus(wnd->wd->dev_bus)) {
+				if (wnd->ndis_wolopts)
+					wnd->wd->pci.wake_state =
+						PowerDeviceD3;
+				else
+					wnd->wd->pci.wake_state =
+						PowerDeviceUnspecified;
+			} else
+				WARNING("couldn't set wake-on-lan options: "
+					"0x%x, %08X", wnd->ndis_wolopts, status);
+			status = mp_set_int(wnd, OID_PNP_SET_POWER, state);
+			if (status == NDIS_STATUS_SUCCESS)
+				set_bit(HW_SUSPENDED, &wnd->wd->hw_status);
+			else
+				WARNING("suspend failed: %08X", status);
+		}
+		if (status != NDIS_STATUS_SUCCESS) {
+			WARNING("%s does not support power management; "
+				"halting the device", wnd->net_dev->name);
+			mp_halt(wnd);
+			set_bit(HW_HALTED, &wnd->wd->hw_status);
+			status = STATUS_SUCCESS;
+		}
+		mutex_lock(&wnd->ndis_req_mutex);
+		EXIT1(return status);
+	}
+}
+
+static int ndis_set_mac_address(struct net_device *dev, void *p)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	struct sockaddr *addr = p;
+	struct ndis_configuration_parameter param;
+	struct unicode_string key;
+	struct ansi_string ansi;
+	NDIS_STATUS res;
+	unsigned char mac_string[2 * ETH_ALEN + 1];
+	mac_address mac;
+
+	memcpy(mac, addr->sa_data, sizeof(mac));
+	memset(mac_string, 0, sizeof(mac_string));
+	res = snprintf(mac_string, sizeof(mac_string), MACSTR, MAC2STR(mac));
+	if (res != (sizeof(mac_string) - 1))
+		EXIT1(return -EINVAL);
+	TRACE1("new mac: %s", mac_string);
+
+	RtlInitAnsiString(&ansi, mac_string);
+	if (RtlAnsiStringToUnicodeString(&param.data.string, &ansi,
+					 TRUE) != STATUS_SUCCESS)
+		EXIT1(return -EINVAL);
+
+	param.type = NdisParameterString;
+	RtlInitAnsiString(&ansi, "NetworkAddress");
+	if (RtlAnsiStringToUnicodeString(&key, &ansi, TRUE) != STATUS_SUCCESS) {
+		RtlFreeUnicodeString(&param.data.string);
+		EXIT1(return -EINVAL);
+	}
+	NdisWriteConfiguration(&res, wnd->nmb, &key, &param);
+	RtlFreeUnicodeString(&key);
+	RtlFreeUnicodeString(&param.data.string);
+
+	if (res != NDIS_STATUS_SUCCESS)
+		EXIT1(return -EFAULT);
+	if (ndis_reinit(wnd) == NDIS_STATUS_SUCCESS) {
+		res = mp_query(wnd, OID_802_3_CURRENT_ADDRESS,
+			       mac, sizeof(mac));
+		if (res == NDIS_STATUS_SUCCESS) {
+			TRACE1("mac:" MACSTRSEP, MAC2STR(mac));
+			memcpy(dev->dev_addr, mac, sizeof(mac));
+		} else
+			ERROR("couldn't get mac address: %08X", res);
+	}
+	EXIT1(return 0);
+}
+
+static int setup_tx_sg_list(struct ndis_device *wnd, struct sk_buff *skb,
+			    struct ndis_packet_oob_data *oob_data)
+{
+	struct ndis_sg_element *sg_element;
+	struct ndis_sg_list *sg_list;
+	int i;
+
+	ENTER3("%p, %d", skb, skb_shinfo(skb)->nr_frags);
+	if (skb_shinfo(skb)->nr_frags <= 1) {
+		sg_element = &oob_data->wrap_tx_sg_list.elements[0];
+		sg_element->address =
+			PCI_DMA_MAP_SINGLE(wnd->wd->pci.pdev, skb->data,
+					   skb->len, PCI_DMA_TODEVICE);
+		sg_element->length = skb->len;
+		oob_data->wrap_tx_sg_list.nent = 1;
+		oob_data->ext.info[ScatterGatherListPacketInfo] =
+			&oob_data->wrap_tx_sg_list;
+		TRACE3("%llx, %u", sg_element->address, sg_element->length);
+		return 0;
+	}
+	sg_list = kmalloc(sizeof(*sg_list) +
+			  (skb_shinfo(skb)->nr_frags + 1) * sizeof(*sg_element),
+			  GFP_ATOMIC);
+	if (!sg_list)
+		return -ENOMEM;
+	sg_list->nent = skb_shinfo(skb)->nr_frags + 1;
+	TRACE3("%p, %d", sg_list, sg_list->nent);
+	sg_element = sg_list->elements;
+	sg_element->length = skb_headlen(skb);
+	sg_element->address =
+		PCI_DMA_MAP_SINGLE(wnd->wd->pci.pdev, skb->data,
+				   skb_headlen(skb), PCI_DMA_TODEVICE);
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+		sg_element++;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0)
+		sg_element->length = skb_frag_size(frag);
+#else
+		sg_element->length = frag->size;
+#endif
+		sg_element->address =
+			pci_map_page(wnd->wd->pci.pdev, skb_frag_page(frag),
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0)
+				     skb_frag_off(frag), skb_frag_size(frag),
+#else
+				     frag->page_offset, frag->size,
+#endif
+				     PCI_DMA_TODEVICE);
+		TRACE3("%llx, %u", sg_element->address, sg_element->length);
+	}
+	oob_data->ext.info[ScatterGatherListPacketInfo] = sg_list;
+	return 0;
+}
+
+static void free_tx_sg_list(struct ndis_device *wnd,
+			    struct ndis_packet_oob_data *oob_data)
+{
+	int i;
+	struct ndis_sg_element *sg_element;
+	struct ndis_sg_list *sg_list =
+		oob_data->ext.info[ScatterGatherListPacketInfo];
+	sg_element = sg_list->elements;
+	TRACE3("%p, %d", sg_list, sg_list->nent);
+	PCI_DMA_UNMAP_SINGLE(wnd->wd->pci.pdev, sg_element->address,
+			     sg_element->length, PCI_DMA_TODEVICE);
+	if (sg_list->nent == 1)
+		EXIT3(return);
+	for (i = 1; i < sg_list->nent; i++, sg_element++) {
+		TRACE3("%llx, %u", sg_element->address, sg_element->length);
+		pci_unmap_page(wnd->wd->pci.pdev, sg_element->address,
+			       sg_element->length, PCI_DMA_TODEVICE);
+	}
+	TRACE3("%p", sg_list);
+	kfree(sg_list);
+}
+
+static struct ndis_packet *alloc_tx_packet(struct ndis_device *wnd,
+					   struct sk_buff *skb)
+{
+	struct ndis_packet *packet;
+	ndis_buffer *buffer;
+	struct ndis_packet_oob_data *oob_data;
+	NDIS_STATUS status;
+
+	NdisAllocatePacket(&status, &packet, wnd->tx_packet_pool);
+	if (status != NDIS_STATUS_SUCCESS)
+		return NULL;
+	NdisAllocateBuffer(&status, &buffer, wnd->tx_buffer_pool,
+			   skb->data, skb->len);
+	if (status != NDIS_STATUS_SUCCESS) {
+		NdisFreePacket(packet);
+		return NULL;
+	}
+	packet->private.buffer_head = buffer;
+	packet->private.buffer_tail = buffer;
+
+	oob_data = NDIS_PACKET_OOB_DATA(packet);
+	oob_data->tx_skb = skb;
+	if (wnd->sg_dma_size) {
+		if (setup_tx_sg_list(wnd, skb, oob_data)) {
+			NdisFreeBuffer(buffer);
+			NdisFreePacket(packet);
+			return NULL;
+		}
+	}
+	if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		struct ndis_tcp_ip_checksum_packet_info csum;
+		int protocol;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21)
+		protocol = ntohs(skb->protocol);
+#else
+		protocol = skb->nh.iph->protocol;
+#endif
+		csum.value = 0;
+		csum.tx.v4 = 1;
+		if (protocol == IPPROTO_TCP)
+			csum.tx.tcp = 1;
+		else if (protocol == IPPROTO_UDP)
+			csum.tx.udp = 1;
+//		csum->tx.ip = 1;
+		packet->private.flags |= NDIS_PROTOCOL_ID_TCP_IP;
+		oob_data->ext.info[TcpIpChecksumPacketInfo] =
+			(void *)(ULONG_PTR)csum.value;
+	}
+	DBG_BLOCK(4) {
+		dump_bytes(__func__, skb->data, skb->len);
+	}
+	TRACE4("%p, %p, %p", packet, buffer, skb);
+	return packet;
+}
+
+void free_tx_packet(struct ndis_device *wnd, struct ndis_packet *packet,
+		    NDIS_STATUS status)
+{
+	ndis_buffer *buffer;
+	struct ndis_packet_oob_data *oob_data;
+	struct sk_buff *skb;
+	struct ndis_packet_pool *pool;
+
+	assert_irql(_irql_ <= DISPATCH_LEVEL);
+	assert(packet->private.packet_flags);
+	oob_data = NDIS_PACKET_OOB_DATA(packet);
+	skb = oob_data->tx_skb;
+	buffer = packet->private.buffer_head;
+	TRACE4("%p, %p, %p, %08X", packet, buffer, skb, status);
+	if (status == NDIS_STATUS_SUCCESS) {
+		pre_atomic_add(wnd->net_stats.tx_bytes, packet->private.len);
+		atomic_inc_var(wnd->net_stats.tx_packets);
+	} else {
+		TRACE1("packet dropped: %08X", status);
+		atomic_inc_var(wnd->net_stats.tx_dropped);
+	}
+	if (wnd->sg_dma_size)
+		free_tx_sg_list(wnd, oob_data);
+	NdisFreeBuffer(buffer);
+	dev_kfree_skb_any(skb);
+	pool = packet->private.pool;
+	NdisFreePacket(packet);
+	if (netif_queue_stopped(wnd->net_dev) &&
+	    ((pool->max_descr - pool->num_used_descr) >=
+	     (wnd->max_tx_packets / 4))) {
+		set_bit(NETIF_WAKEQ, &wnd->ndis_pending_work);
+		queue_work(wrapndis_wq, &wnd->ndis_work);
+	}
+	EXIT4(return);
+}
+
+/* MiniportSend and MiniportSendPackets */
+/* this function is called holding tx_ring_mutex. start and n are such
+ * that start + n < TX_RING_SIZE; i.e., packets don't wrap around
+ * ring */
+static u8 mp_tx_packets(struct ndis_device *wnd, u8 start, u8 n)
+{
+	NDIS_STATUS res;
+	struct miniport *mp;
+	struct ndis_packet *packet;
+	u8 sent;
+	KIRQL irql;
+
+	ENTER3("%d, %d", start, n);
+	mp = &wnd->wd->driver->ndis_driver->mp;
+	if (mp->send_packets) {
+		if (deserialized_driver(wnd)) {
+			LIN2WIN3(mp->send_packets, wnd->nmb->mp_ctx,
+				 &wnd->tx_ring[start], n);
+			sent = n;
+		} else {
+			irql = serialize_lock_irql(wnd);
+			LIN2WIN3(mp->send_packets, wnd->nmb->mp_ctx,
+				 &wnd->tx_ring[start], n);
+			serialize_unlock_irql(wnd, irql);
+			for (sent = 0; sent < n && wnd->tx_ok; sent++) {
+				struct ndis_packet_oob_data *oob_data;
+				packet = wnd->tx_ring[start + sent];
+				oob_data = NDIS_PACKET_OOB_DATA(packet);
+				switch ((res =
+					 xchg(&oob_data->status,
+					      NDIS_STATUS_NOT_RECOGNIZED))) {
+				case NDIS_STATUS_SUCCESS:
+					free_tx_packet(wnd, packet,
+						       NDIS_STATUS_SUCCESS);
+					break;
+				case NDIS_STATUS_PENDING:
+					break;
+				case NDIS_STATUS_RESOURCES:
+					wnd->tx_ok = 0;
+					/* resubmit this packet and
+					 * the rest when resources
+					 * become available */
+					sent--;
+					break;
+				case NDIS_STATUS_FAILURE:
+					free_tx_packet(wnd, packet,
+						       NDIS_STATUS_FAILURE);
+					break;
+				default:
+					ERROR("%p: invalid status: %08X",
+					      packet, res);
+					free_tx_packet(wnd, packet,
+						       oob_data->status);
+					break;
+				}
+				TRACE3("%p, %d", packet, res);
+			}
+		}
+		TRACE3("sent: %d(%d)", sent, n);
+	} else {
+		for (sent = 0; sent < n && wnd->tx_ok; sent++) {
+			struct ndis_packet_oob_data *oob_data;
+			packet = wnd->tx_ring[start + sent];
+			oob_data = NDIS_PACKET_OOB_DATA(packet);
+			oob_data->status = NDIS_STATUS_NOT_RECOGNIZED;
+			irql = serialize_lock_irql(wnd);
+			res = LIN2WIN3(mp->send, wnd->nmb->mp_ctx,
+				       packet, packet->private.flags);
+			serialize_unlock_irql(wnd, irql);
+			switch (res) {
+			case NDIS_STATUS_SUCCESS:
+				free_tx_packet(wnd, packet, res);
+				break;
+			case NDIS_STATUS_PENDING:
+				break;
+			case NDIS_STATUS_RESOURCES:
+				wnd->tx_ok = 0;
+				/* resend this packet when resources
+				 * become available */
+				sent--;
+				break;
+			case NDIS_STATUS_FAILURE:
+				free_tx_packet(wnd, packet, res);
+				break;
+			default:
+				ERROR("packet %p: invalid status: %08X",
+				      packet, res);
+				break;
+			}
+		}
+	}
+	EXIT3(return sent);
+}
+
+static void tx_worker(struct work_struct *work)
+{
+	struct ndis_device *wnd;
+	s8 n;
+
+	wnd = container_of(work, struct ndis_device, tx_work);
+	ENTER3("tx_ok %d", wnd->tx_ok);
+	while (wnd->tx_ok) {
+		mutex_lock(&wnd->tx_ring_mutex);
+		spin_lock_bh(&wnd->tx_ring_lock);
+		n = wnd->tx_ring_end - wnd->tx_ring_start;
+		TRACE3("%d, %d, %d", wnd->tx_ring_start, wnd->tx_ring_end, n);
+		/* end == start if either ring is empty or full; in
+		 * the latter case is_tx_ring_full is set */
+		if (n == 0) {
+			if (wnd->is_tx_ring_full)
+				n = TX_RING_SIZE - wnd->tx_ring_start;
+			else {
+				spin_unlock_bh(&wnd->tx_ring_lock);
+				mutex_unlock(&wnd->tx_ring_mutex);
+				break;
+			}
+		} else if (n < 0)
+			n = TX_RING_SIZE - wnd->tx_ring_start;
+		spin_unlock_bh(&wnd->tx_ring_lock);
+		if (unlikely(n > wnd->max_tx_packets))
+			n = wnd->max_tx_packets;
+		n = mp_tx_packets(wnd, wnd->tx_ring_start, n);
+		if (n) {
+			netif_trans_update(wnd->net_dev);
+			wnd->tx_ring_start =
+				(wnd->tx_ring_start + n) % TX_RING_SIZE;
+			wnd->is_tx_ring_full = 0;
+		}
+		mutex_unlock(&wnd->tx_ring_mutex);
+		TRACE3("%d, %d, %d", wnd->tx_ring_start, wnd->tx_ring_end, n);
+	}
+	EXIT3(return);
+}
+
+static int tx_skbuff(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	struct ndis_packet *packet;
+
+	packet = alloc_tx_packet(wnd, skb);
+	if (!packet) {
+		TRACE2("couldn't allocate packet");
+		netif_tx_lock(dev);
+		netif_stop_queue(dev);
+		netif_tx_unlock(dev);
+		return NETDEV_TX_BUSY;
+	}
+	spin_lock(&wnd->tx_ring_lock);
+	wnd->tx_ring[wnd->tx_ring_end++] = packet;
+	if (wnd->tx_ring_end == TX_RING_SIZE)
+		wnd->tx_ring_end = 0;
+	if (wnd->tx_ring_end == wnd->tx_ring_start) {
+		netif_tx_lock(dev);
+		wnd->is_tx_ring_full = 1;
+		netif_stop_queue(dev);
+		netif_tx_unlock(dev);
+	}
+	spin_unlock(&wnd->tx_ring_lock);
+	TRACE4("ring: %d, %d", wnd->tx_ring_start, wnd->tx_ring_end);
+	queue_work(wrapndis_wq, &wnd->tx_work);
+	return NETDEV_TX_OK;
+}
+
+static int set_packet_filter(struct ndis_device *wnd, ULONG packet_filter)
+{
+	NDIS_STATUS res;
+
+	while (1) {
+		res = mp_set_int(wnd, OID_GEN_CURRENT_PACKET_FILTER,
+				 packet_filter);
+		if (res == NDIS_STATUS_SUCCESS)
+			break;
+		TRACE2("couldn't set filter 0x%08x", packet_filter);
+		/* NDIS_PACKET_TYPE_PROMISCUOUS may not work with 802.11 */
+		if (packet_filter & NDIS_PACKET_TYPE_PROMISCUOUS) {
+			packet_filter &= ~NDIS_PACKET_TYPE_PROMISCUOUS;
+			continue;
+		}
+		if (packet_filter & NDIS_PACKET_TYPE_ALL_LOCAL) {
+			packet_filter &= ~NDIS_PACKET_TYPE_ALL_LOCAL;
+			continue;
+		}
+		if (packet_filter & NDIS_PACKET_TYPE_ALL_FUNCTIONAL) {
+			packet_filter &= ~NDIS_PACKET_TYPE_ALL_FUNCTIONAL;
+			continue;
+		}
+		if (packet_filter & NDIS_PACKET_TYPE_MULTICAST) {
+			packet_filter &= ~NDIS_PACKET_TYPE_MULTICAST;
+			packet_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
+			continue;
+		}
+		if (packet_filter & NDIS_PACKET_TYPE_ALL_MULTICAST) {
+			packet_filter &= ~NDIS_PACKET_TYPE_ALL_MULTICAST;
+			continue;
+		}
+		break;
+	}
+
+	wnd->packet_filter = packet_filter;
+	res = mp_query_int(wnd, OID_GEN_CURRENT_PACKET_FILTER, &packet_filter);
+	if (packet_filter != wnd->packet_filter) {
+		WARNING("filter not set: 0x%08x, 0x%08x",
+			packet_filter, wnd->packet_filter);
+		wnd->packet_filter = packet_filter;
+	}
+	if (wnd->packet_filter)
+		EXIT3(return 0);
+	else
+		EXIT3(return -1);
+}
+
+void set_media_state(struct ndis_device *wnd, enum ndis_media_state state)
+{
+	struct net_device *net_dev = wnd->net_dev;
+
+	ENTER2("state: 0x%x, carrier %d", state, netif_carrier_ok(net_dev));
+	switch (state) {
+	case NdisMediaStateConnected:
+		if (netif_carrier_ok(net_dev))
+			return;
+		netif_carrier_on(net_dev);
+		wnd->tx_ok = 1;
+		if (netif_queue_stopped(net_dev))
+			netif_wake_queue(net_dev);
+		if (wnd->physical_medium == NdisPhysicalMediumWirelessLan) {
+			set_bit(LINK_STATUS_ON, &wnd->ndis_pending_work);
+			queue_work(wrapndis_wq, &wnd->ndis_work);
+		}
+		break;
+	case NdisMediaStateDisconnected:
+		if (!netif_carrier_ok(net_dev))
+			return;
+		netif_carrier_off(net_dev);
+		netif_stop_queue(net_dev);
+		wnd->tx_ok = 0;
+		if (wnd->physical_medium == NdisPhysicalMediumWirelessLan) {
+			memset(&wnd->essid, 0, sizeof(wnd->essid));
+			set_bit(LINK_STATUS_OFF, &wnd->ndis_pending_work);
+			queue_work(wrapndis_wq, &wnd->ndis_work);
+		}
+		break;
+	default:
+		WARNING("invalid media state: 0x%x", state);
+		break;
+	}
+}
+
+static int ndis_net_dev_init(struct net_device *net_dev)
+{
+	struct ndis_device *wnd = netdev_priv(net_dev);
+
+	ENTER1("%p", wnd);
+	wrap_procfs_add_ndis_device(wnd);
+	EXIT1(return 0);
+}
+
+static void ndis_net_dev_uninit(struct net_device *net_dev)
+{
+	struct ndis_device *wnd = netdev_priv(net_dev);
+
+	ENTER1("%p", wnd);
+	wrap_procfs_remove_ndis_device(wnd);
+	EXIT1(return);
+}
+
+static int ndis_net_dev_open(struct net_device *net_dev)
+{
+	int status, res;
+	struct ndis_device *wnd = netdev_priv(net_dev);
+
+	ENTER1("%p", wnd);
+	res = mp_query_int(wnd, OID_GEN_MEDIA_CONNECT_STATUS, &status);
+	if (res == NDIS_STATUS_SUCCESS && status >= NdisMediaStateConnected &&
+	    status <= NdisMediaStateDisconnected)
+		set_media_state(wnd, status);
+	netif_start_queue(net_dev);
+	netif_poll_enable(net_dev);
+	EXIT1(return 0);
+}
+
+static int ndis_net_dev_close(struct net_device *net_dev)
+{
+	ENTER1("%p", netdev_priv(net_dev));
+	netif_poll_disable(net_dev);
+	netif_tx_disable(net_dev);
+	EXIT1(return 0);
+}
+
+static int ndis_change_mtu(struct net_device *net_dev, int mtu)
+{
+	struct ndis_device *wnd = netdev_priv(net_dev);
+	int max;
+
+	if (mtu < ETH_ZLEN)
+		return -EINVAL;
+	if (mp_query_int(wnd, OID_GEN_MAXIMUM_TOTAL_SIZE, &max) !=
+	    NDIS_STATUS_SUCCESS)
+		return -EOPNOTSUPP;
+	TRACE1("%d", max);
+	max -= ETH_HLEN;
+	if (max <= ETH_ZLEN)
+		return -EINVAL;
+	if (mtu + ETH_HLEN > max)
+		return -EINVAL;
+	net_dev->mtu = mtu;
+	return 0;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void ndis_poll_controller(struct net_device *dev)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+
+	disable_irq(dev->irq);
+	ndis_isr(wnd->mp_interrupt->kinterrupt, wnd->mp_interrupt);
+	enable_irq(dev->irq);
+}
+#endif
+
+/* called from BH context */
+static struct net_device_stats *ndis_get_stats(struct net_device *dev)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	return &wnd->net_stats;
+}
+
+/* called from BH context */
+static void ndis_set_multicast_list(struct net_device *dev)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	set_bit(SET_MULTICAST_LIST, &wnd->ndis_pending_work);
+	queue_work(wrapndis_wq, &wnd->ndis_work);
+}
+
+/* called from BH context */
+struct iw_statistics *get_iw_stats(struct net_device *dev)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	return &wnd->iw_stats;
+}
+
+static void update_iw_stats(struct ndis_device *wnd)
+{
+	struct iw_statistics *iw_stats = &wnd->iw_stats;
+	struct ndis_wireless_stats ndis_stats;
+	NDIS_STATUS res;
+	ndis_rssi rssi;
+	int qual;
+
+	ENTER2("%p", wnd);
+	if (wnd->iw_stats_enabled == FALSE || !netif_carrier_ok(wnd->net_dev)) {
+		memset(iw_stats, 0, sizeof(*iw_stats));
+		EXIT2(return);
+	}
+	res = mp_query(wnd, OID_802_11_RSSI, &rssi, sizeof(rssi));
+	if (res == NDIS_STATUS_SUCCESS)
+		iw_stats->qual.level = rssi;
+
+	qual = 100 * (rssi - WL_NOISE) / (WL_SIGMAX - WL_NOISE);
+	if (qual < 0)
+		qual = 0;
+	else if (qual > 100)
+		qual = 100;
+
+	iw_stats->qual.noise = WL_NOISE;
+	iw_stats->qual.qual = qual;
+
+	res = mp_query(wnd, OID_802_11_STATISTICS,
+		       &ndis_stats, sizeof(ndis_stats));
+	if (res != NDIS_STATUS_SUCCESS)
+		EXIT2(return);
+	iw_stats->discard.retries = (unsigned long)ndis_stats.retry +
+		(unsigned long)ndis_stats.multi_retry;
+	iw_stats->discard.misc = (unsigned long)ndis_stats.fcs_err +
+		(unsigned long)ndis_stats.rtss_fail +
+		(unsigned long)ndis_stats.ack_fail +
+		(unsigned long)ndis_stats.frame_dup;
+
+	EXIT2(return);
+}
+
+static void set_multicast_list(struct ndis_device *wnd)
+{
+	struct net_device *net_dev;
+	ULONG packet_filter;
+	NDIS_STATUS res;
+
+	net_dev = wnd->net_dev;
+	packet_filter = wnd->packet_filter;
+
+	TRACE2("0x%08x", packet_filter);
+	if (net_dev->flags & IFF_PROMISC) {
+		packet_filter |= NDIS_PACKET_TYPE_PROMISCUOUS |
+			NDIS_PACKET_TYPE_ALL_LOCAL;
+	} else if (net_dev->flags & IFF_ALLMULTI ||
+		   netdev_mc_count(net_dev) > wnd->multicast_size) {
+		packet_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
+		TRACE2("0x%08x", packet_filter);
+	} else if (netdev_mc_count(net_dev) > 0) {
+		int i, size;
+		char *buf;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+		struct netdev_hw_addr *ha;
+#else
+		struct dev_mc_list *mclist;
+#endif
+		size = min(wnd->multicast_size, netdev_mc_count(net_dev));
+		TRACE2("%d, %d", wnd->multicast_size, netdev_mc_count(net_dev));
+		buf = kmalloc(size * ETH_ALEN, GFP_KERNEL);
+		if (!buf) {
+			WARNING("couldn't allocate memory");
+			EXIT2(return);
+		}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+		i = 0;
+		netdev_for_each_mc_addr(ha, net_dev) {
+			if (i >= size)
+				break;
+			memcpy(buf + i * ETH_ALEN, ha->addr, ETH_ALEN);
+			TRACE2(MACSTRSEP, MAC2STR(ha->addr));
+			i++;
+		}
+#else
+		mclist = net_dev->mc_list;
+		for (i = 0; i < size && mclist; mclist = mclist->next) {
+			if (mclist->dmi_addrlen != ETH_ALEN)
+				continue;
+			memcpy(buf + i * ETH_ALEN, mclist->dmi_addr, ETH_ALEN);
+			TRACE2(MACSTRSEP, MAC2STR(mclist->dmi_addr));
+			i++;
+		}
+#endif
+		res = mp_set(wnd, OID_802_3_MULTICAST_LIST, buf, i * ETH_ALEN);
+		if (res == NDIS_STATUS_SUCCESS && i > 0)
+			packet_filter |= NDIS_PACKET_TYPE_MULTICAST;
+		else
+			packet_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
+		kfree(buf);
+	}
+	TRACE2("0x%08x", packet_filter);
+	res = set_packet_filter(wnd, packet_filter);
+	if (res)
+		TRACE1("couldn't set packet filter (%08X)", res);
+	EXIT2(return);
+}
+
+static void link_status_off(struct ndis_device *wnd)
+{
+#ifdef CONFIG_WIRELESS_EXT
+	union iwreq_data wrqu;
+
+	memset(&wrqu, 0, sizeof(wrqu));
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	wireless_send_event(wnd->net_dev, SIOCGIWAP, &wrqu, NULL);
+#endif
+	EXIT2(return);
+}
+
+static void link_status_on(struct ndis_device *wnd)
+{
+#ifdef CONFIG_WIRELESS_EXT
+	struct ndis_assoc_info *ndis_assoc_info;
+	union iwreq_data wrqu;
+	NDIS_STATUS res;
+	const int assoc_size = sizeof(*ndis_assoc_info) + IW_CUSTOM_MAX + 32;
+#endif
+
+	ENTER2("");
+#ifdef CONFIG_WIRELESS_EXT
+	memset(&wrqu, 0, sizeof(wrqu));
+	ndis_assoc_info = kzalloc(assoc_size, GFP_KERNEL);
+	if (!ndis_assoc_info) {
+		ERROR("couldn't allocate memory");
+		goto send_assoc_event;
+	}
+	res = mp_query(wnd, OID_802_11_ASSOCIATION_INFORMATION,
+		       ndis_assoc_info, assoc_size);
+	if (res) {
+		TRACE2("query assoc_info failed (%08X)", res);
+		kfree(ndis_assoc_info);
+		goto send_assoc_event;
+	}
+	TRACE2("%u, 0x%x, %u, 0x%x, %u", ndis_assoc_info->length,
+	       ndis_assoc_info->req_ies, ndis_assoc_info->req_ie_length,
+	       ndis_assoc_info->resp_ies, ndis_assoc_info->resp_ie_length);
+	if (ndis_assoc_info->req_ie_length > 0) {
+		wrqu.data.length = ndis_assoc_info->req_ie_length;
+		wireless_send_event(wnd->net_dev, IWEVASSOCREQIE, &wrqu,
+				    ((char *)ndis_assoc_info) +
+				    ndis_assoc_info->offset_req_ies);
+	}
+	if (ndis_assoc_info->resp_ie_length > 0) {
+		wrqu.data.length = ndis_assoc_info->resp_ie_length;
+		wireless_send_event(wnd->net_dev, IWEVASSOCRESPIE, &wrqu,
+				    ((char *)ndis_assoc_info) +
+				    ndis_assoc_info->offset_resp_ies);
+	}
+	kfree(ndis_assoc_info);
+
+send_assoc_event:
+	get_ap_address(wnd, wrqu.ap_addr.sa_data);
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	TRACE2(MACSTRSEP, MAC2STR(wrqu.ap_addr.sa_data));
+	wireless_send_event(wnd->net_dev, SIOCGIWAP, &wrqu, NULL);
+#endif
+	EXIT2(return);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+static void iw_stats_timer_proc(struct timer_list *tl)
+#else
+static void iw_stats_timer_proc(unsigned long data)
+#endif
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+	struct ndis_device *wnd = from_timer(wnd, tl, iw_stats_timer);
+#else
+	struct ndis_device *wnd = (struct ndis_device *)data;
+#endif
+
+	ENTER2("%d", wnd->iw_stats_interval);
+	if (wnd->iw_stats_interval > 0) {
+		set_bit(COLLECT_IW_STATS, &wnd->ndis_pending_work);
+		queue_work(wrapndis_wq, &wnd->ndis_work);
+	}
+	mod_timer(&wnd->iw_stats_timer, jiffies + wnd->iw_stats_interval);
+}
+
+static void add_iw_stats_timer(struct ndis_device *wnd)
+{
+	if (wnd->physical_medium != NdisPhysicalMediumWirelessLan)
+		return;
+	if (wnd->iw_stats_interval < 0)
+		wnd->iw_stats_interval *= -1;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+	timer_setup(&wnd->iw_stats_timer, iw_stats_timer_proc, 0);
+#else
+	wnd->iw_stats_timer.data = (unsigned long)wnd;
+	wnd->iw_stats_timer.function = iw_stats_timer_proc;
+#endif
+	mod_timer(&wnd->iw_stats_timer, jiffies + wnd->iw_stats_interval);
+}
+
+static void del_iw_stats_timer(struct ndis_device *wnd)
+{
+	ENTER2("%d", wnd->iw_stats_interval);
+	wnd->iw_stats_interval *= -1;
+	del_timer_sync(&wnd->iw_stats_timer);
+	EXIT2(return);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+static void hangcheck_proc(struct timer_list *tl)
+#else
+static void hangcheck_proc(unsigned long data)
+#endif
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+	struct ndis_device *wnd = from_timer(wnd, tl, hangcheck_timer);
+#else
+	struct ndis_device *wnd = (struct ndis_device *)data;
+#endif
+
+	ENTER3("%d", wnd->hangcheck_interval);
+	if (wnd->hangcheck_interval > 0) {
+		set_bit(HANGCHECK, &wnd->ndis_pending_work);
+		queue_work(wrapndis_wq, &wnd->ndis_work);
+	}
+	mod_timer(&wnd->hangcheck_timer, jiffies + wnd->hangcheck_interval);
+	EXIT3(return);
+}
+
+void hangcheck_add(struct ndis_device *wnd)
+{
+	if (!wnd->wd->driver->ndis_driver->mp.hangcheck ||
+	    hangcheck_interval < 0)
+		EXIT2(return);
+
+	if (hangcheck_interval > 0)
+		wnd->hangcheck_interval = hangcheck_interval * HZ;
+	if (wnd->hangcheck_interval < 0)
+		wnd->hangcheck_interval *= -1;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+	timer_setup(&wnd->hangcheck_timer, hangcheck_proc, 0);
+#else
+	wnd->hangcheck_timer.data = (unsigned long)wnd;
+	wnd->hangcheck_timer.function = hangcheck_proc;
+#endif
+	mod_timer(&wnd->hangcheck_timer, jiffies + wnd->hangcheck_interval);
+	EXIT2(return);
+}
+
+void hangcheck_del(struct ndis_device *wnd)
+{
+	ENTER2("%d", wnd->hangcheck_interval);
+	if (wnd->hangcheck_interval > 0)
+		wnd->hangcheck_interval *= -1;
+	del_timer_sync(&wnd->hangcheck_timer);
+	EXIT2(return);
+}
+
+/* worker procedure to take care of setting/checking various states */
+static void wrapndis_worker(struct work_struct *work)
+{
+	struct ndis_device *wnd;
+
+	wnd = container_of(work, struct ndis_device, ndis_work);
+	WORKTRACE("0x%lx", wnd->ndis_pending_work);
+
+	if (test_and_clear_bit(NETIF_WAKEQ, &wnd->ndis_pending_work)) {
+		netif_tx_lock_bh(wnd->net_dev);
+		netif_wake_queue(wnd->net_dev);
+		netif_tx_unlock_bh(wnd->net_dev);
+	}
+
+	if (test_and_clear_bit(LINK_STATUS_OFF, &wnd->ndis_pending_work))
+		link_status_off(wnd);
+
+	if (test_and_clear_bit(LINK_STATUS_ON, &wnd->ndis_pending_work))
+		link_status_on(wnd);
+
+	if (test_and_clear_bit(COLLECT_IW_STATS, &wnd->ndis_pending_work))
+		update_iw_stats(wnd);
+
+	if (test_and_clear_bit(SET_MULTICAST_LIST,
+			       &wnd->ndis_pending_work))
+		set_multicast_list(wnd);
+
+	if (test_and_clear_bit(HANGCHECK, &wnd->ndis_pending_work)) {
+		struct miniport *mp;
+		BOOLEAN reset;
+		KIRQL irql;
+
+		mp = &wnd->wd->driver->ndis_driver->mp;
+		irql = serialize_lock_irql(wnd);
+		reset = LIN2WIN1(mp->hangcheck, wnd->nmb->mp_ctx);
+		serialize_unlock_irql(wnd, irql);
+		if (reset) {
+			TRACE2("%s needs reset", wnd->net_dev->name);
+			mp_reset(wnd);
+		}
+	}
+	WORKEXIT(return);
+}
+
+NDIS_STATUS ndis_reinit(struct ndis_device *wnd)
+{
+	NDIS_STATUS status;
+
+	wnd->attributes &= ~NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND;
+	status = mp_set_power_state(wnd, NdisDeviceStateD3);
+	if (status != NDIS_STATUS_SUCCESS) {
+		ERROR("halting device %s failed: %08X", wnd->net_dev->name,
+		      status);
+		return status;
+	}
+	status = mp_set_power_state(wnd, NdisDeviceStateD0);
+	if (status != NDIS_STATUS_SUCCESS)
+		ERROR("starting device %s failed: %08X", wnd->net_dev->name,
+		      status);
+	return status;
+}
+
+#ifdef CONFIG_WIRELESS_EXT
+static void get_encryption_capa(struct ndis_device *wnd, char *buf,
+				const int buf_len)
+{
+	int i, mode;
+	NDIS_STATUS res;
+	struct ndis_assoc_info ndis_assoc_info;
+	struct ndis_add_key ndis_key;
+	struct ndis_capability *c;
+
+	ENTER1("%p", wnd);
+	/* set network type to g, b, or a, in that order */
+	res = mp_query(wnd, OID_802_11_NETWORK_TYPES_SUPPORTED, buf, buf_len);
+	if (res == NDIS_STATUS_SUCCESS) {
+		struct network_type_list *net_types;
+		unsigned long types = 0;
+		net_types = (typeof(net_types))buf;
+		for (i = 0; i < net_types->num; i++) {
+			TRACE2("%d", net_types->types[i]);
+			set_bit(net_types->types[i], &types);
+		}
+		if (test_bit(Ndis802_11OFDM24, &types))
+			mode = Ndis802_11OFDM24;
+		else if (test_bit(Ndis802_11DS, &types))
+			mode = Ndis802_11DS;
+		else if (test_bit(Ndis802_11OFDM5, &types))
+			mode = Ndis802_11OFDM5;
+		else
+			mode = Ndis802_11DS;
+		mp_set_int(wnd, OID_802_11_NETWORK_TYPE_IN_USE, mode);
+	}
+	/* check if WEP is supported */
+	if (set_iw_encr_mode(wnd, IW_AUTH_CIPHER_WEP104,
+			     IW_AUTH_CIPHER_NONE) == 0 &&
+	    get_ndis_encr_mode(wnd) == Ndis802_11Encryption1KeyAbsent)
+		set_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr);
+
+	/* check if WPA is supported */
+	if (set_ndis_auth_mode(wnd, Ndis802_11AuthModeWPA) == 0 &&
+	    get_ndis_auth_mode(wnd) == Ndis802_11AuthModeWPA)
+		set_bit(Ndis802_11AuthModeWPA, &wnd->capa.encr);
+	else
+		EXIT1(return);
+
+	if (set_ndis_auth_mode(wnd, Ndis802_11AuthModeWPAPSK) == 0 &&
+	    get_ndis_auth_mode(wnd) == Ndis802_11AuthModeWPAPSK)
+		set_bit(Ndis802_11AuthModeWPAPSK, &wnd->capa.encr);
+
+	/* check for highest encryption */
+	mode = 0;
+	if (set_iw_encr_mode(wnd, IW_AUTH_CIPHER_CCMP,
+			     IW_AUTH_CIPHER_NONE) == 0 &&
+	    (i = get_ndis_encr_mode(wnd)) > 0 &&
+	    (i == Ndis802_11Encryption3KeyAbsent ||
+	     i == Ndis802_11Encryption3Enabled))
+		mode = Ndis802_11Encryption3Enabled;
+	else if (set_iw_encr_mode(wnd, IW_AUTH_CIPHER_TKIP,
+				  IW_AUTH_CIPHER_NONE) == 0 &&
+		 (i = get_ndis_encr_mode(wnd)) > 0 &&
+		 (i == Ndis802_11Encryption2KeyAbsent ||
+		  i == Ndis802_11Encryption2Enabled))
+		mode = Ndis802_11Encryption2Enabled;
+	else if (set_iw_encr_mode(wnd, IW_AUTH_CIPHER_WEP104,
+				  IW_AUTH_CIPHER_NONE) == 0 &&
+		 (i = get_ndis_encr_mode(wnd)) > 0 &&
+		 (i == Ndis802_11Encryption1KeyAbsent ||
+		  i == Ndis802_11Encryption1Enabled))
+		mode = Ndis802_11Encryption1Enabled;
+
+	TRACE1("mode: %d", mode);
+	if (mode == 0)
+		EXIT1(return);
+	set_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr);
+	if (mode == Ndis802_11Encryption1Enabled)
+		EXIT1(return);
+
+	ndis_key.length = 32;
+	ndis_key.index = 0xC0000001;
+	ndis_key.struct_size = sizeof(ndis_key);
+	res = mp_set(wnd, OID_802_11_ADD_KEY, &ndis_key, ndis_key.struct_size);
+	TRACE2("%08X, %zu", res, sizeof(ndis_key));
+	if (res && res != NDIS_STATUS_INVALID_DATA)
+		EXIT1(return);
+	res = mp_query(wnd, OID_802_11_ASSOCIATION_INFORMATION,
+		       &ndis_assoc_info, sizeof(ndis_assoc_info));
+	TRACE1("%08X", res);
+	if (res == NDIS_STATUS_NOT_SUPPORTED)
+		EXIT1(return);
+
+	set_bit(Ndis802_11Encryption2Enabled, &wnd->capa.encr);
+	if (mode == Ndis802_11Encryption3Enabled)
+		set_bit(Ndis802_11Encryption3Enabled, &wnd->capa.encr);
+	/* not all drivers support OID_802_11_CAPABILITY, so we don't
+	 * know for sure if driver support WPA or WPAPSK; assume
+	 * WPAPSK */
+	set_bit(Ndis802_11AuthModeWPAPSK, &wnd->capa.auth);
+	wnd->max_pmkids = 1;
+
+	memset(buf, 0, buf_len);
+	c = (struct ndis_capability *)buf;
+	res = mp_query(wnd, OID_802_11_CAPABILITY, buf, buf_len);
+	if (!(res == NDIS_STATUS_SUCCESS && c->version == 2))
+		EXIT1(return);
+	wnd->max_pmkids = c->num_PMKIDs;
+
+	for (i = 0; i < c->num_auth_encr_pair; i++) {
+		struct ndis_auth_encr_pair *ae;
+
+		ae = &c->auth_encr_pair[i];
+		if ((char *)(ae + 1) > buf + buf_len)
+			break;
+		switch (ae->auth_mode) {
+		case Ndis802_11AuthModeOpen:
+		case Ndis802_11AuthModeShared:
+		case Ndis802_11AuthModeWPA:
+		case Ndis802_11AuthModeWPAPSK:
+		case Ndis802_11AuthModeWPANone:
+		case Ndis802_11AuthModeWPA2:
+		case Ndis802_11AuthModeWPA2PSK:
+			set_bit(ae->auth_mode, &wnd->capa.auth);
+			break;
+		default:
+			WARNING("unknown auth_mode: %d", ae->auth_mode);
+			break;
+		}
+		switch (ae->encr_mode) {
+		case Ndis802_11EncryptionDisabled:
+		case Ndis802_11Encryption1Enabled:
+		case Ndis802_11Encryption2Enabled:
+		case Ndis802_11Encryption3Enabled:
+			set_bit(ae->encr_mode, &wnd->capa.encr);
+			break;
+		default:
+			WARNING("unknown encr_mode: %d", ae->encr_mode);
+			break;
+		}
+	}
+	EXIT1(return);
+}
+#endif
+
+wstdcall NTSTATUS NdisDispatchDeviceControl(struct device_object *fdo,
+					    struct irp *irp)
+{
+	struct ndis_device *wnd;
+
+	TRACE3("fdo: %p", fdo);
+	/* for now, we don't have anything interesting here, so pass it
+	 * down to bus driver */
+	wnd = fdo->reserved;
+	return IoPassIrpDown(wnd->nmb->pdo, irp);
+}
+WIN_FUNC_DECL(NdisDispatchDeviceControl,2)
+
+wstdcall NTSTATUS NdisDispatchPower(struct device_object *fdo, struct irp *irp)
+{
+	struct io_stack_location *irp_sl;
+	struct ndis_device *wnd;
+	enum ndis_power_state state;
+	NTSTATUS status;
+	NDIS_STATUS ndis_status;
+
+	irp_sl = IoGetCurrentIrpStackLocation(irp);
+	wnd = fdo->reserved;
+	IOTRACE("fdo: %p, fn: %d:%d, wnd: %p", fdo, irp_sl->major_fn,
+		irp_sl->minor_fn, wnd);
+	if ((irp_sl->params.power.type == SystemPowerState &&
+	     irp_sl->params.power.state.system_state > PowerSystemWorking) ||
+	    (irp_sl->params.power.type == DevicePowerState &&
+	     irp_sl->params.power.state.device_state > PowerDeviceD0))
+		state = NdisDeviceStateD3;
+	else
+		state = NdisDeviceStateD0;
+	switch (irp_sl->minor_fn) {
+	case IRP_MN_SET_POWER:
+		if (state == NdisDeviceStateD0) {
+			status = IoSyncForwardIrp(wnd->nmb->pdo, irp);
+			if (status != STATUS_SUCCESS)
+				break;
+			ndis_status = mp_set_power_state(wnd, state);
+			if (ndis_status != NDIS_STATUS_SUCCESS)
+				WARNING("couldn't set power to %d: %08X",
+					state, ndis_status);
+			TRACE2("%s: device resumed", wnd->net_dev->name);
+			irp->io_status.status = status = STATUS_SUCCESS;
+			IoCompleteRequest(irp, IO_NO_INCREMENT);
+			break;
+		} else {
+			ndis_status = mp_set_power_state(wnd, state);
+			/* TODO: handle error case */
+			if (ndis_status != NDIS_STATUS_SUCCESS)
+				WARNING("setting power to %d failed: %08X",
+					state, ndis_status);
+			status = IoAsyncForwardIrp(wnd->nmb->pdo, irp);
+		}
+		break;
+	case IRP_MN_QUERY_POWER:
+		if (wnd->attributes & NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND) {
+			ndis_status = mp_query(wnd, OID_PNP_QUERY_POWER,
+					       &state, sizeof(state));
+			TRACE2("%d, %08X", state, ndis_status);
+			/* this OID must always succeed */
+			if (ndis_status != NDIS_STATUS_SUCCESS)
+				TRACE1("query power returns %08X", ndis_status);
+			irp->io_status.status = STATUS_SUCCESS;
+		} else
+			irp->io_status.status = STATUS_SUCCESS;
+		status = IoPassIrpDown(wnd->nmb->pdo, irp);
+		break;
+	case IRP_MN_WAIT_WAKE:
+	case IRP_MN_POWER_SEQUENCE:
+		/* TODO: implement WAIT_WAKE */
+		status = IoPassIrpDown(wnd->nmb->pdo, irp);
+		break;
+	default:
+		status = IoPassIrpDown(wnd->nmb->pdo, irp);
+		break;
+	}
+	IOEXIT(return status);
+}
+WIN_FUNC_DECL(NdisDispatchPower,2)
+
+wstdcall NTSTATUS NdisDispatchPnp(struct device_object *fdo, struct irp *irp)
+{
+	struct io_stack_location *irp_sl;
+	struct ndis_device *wnd;
+	struct device_object *pdo;
+	NTSTATUS status;
+
+	IOTRACE("fdo: %p, irp: %p", fdo, irp);
+	irp_sl = IoGetCurrentIrpStackLocation(irp);
+	wnd = fdo->reserved;
+	pdo = wnd->nmb->pdo;
+	switch (irp_sl->minor_fn) {
+	case IRP_MN_START_DEVICE:
+		status = IoSyncForwardIrp(pdo, irp);
+		if (status != STATUS_SUCCESS)
+			break;
+		if (ndis_start_device(wnd) == NDIS_STATUS_SUCCESS)
+			status = STATUS_SUCCESS;
+		else
+			status = STATUS_FAILURE;
+		irp->io_status.status = status;
+		IoCompleteRequest(irp, IO_NO_INCREMENT);
+		break;
+	case IRP_MN_QUERY_STOP_DEVICE:
+		/* TODO: implement in NDIS */
+		status = IoPassIrpDown(wnd->nmb->pdo, irp);
+		break;
+	case IRP_MN_STOP_DEVICE:
+		mp_halt(wnd);
+		irp->io_status.status = STATUS_SUCCESS;
+		status = IoAsyncForwardIrp(pdo, irp);
+		break;
+	case IRP_MN_REMOVE_DEVICE:
+		TRACE1("%s", wnd->net_dev->name);
+		mp_pnp_event(wnd, NdisDevicePnPEventSurpriseRemoved, 0);
+		if (ndis_remove_device(wnd)) {
+			status = STATUS_FAILURE;
+			break;
+		}
+		/* wnd is already freed */
+		status = IoAsyncForwardIrp(pdo, irp);
+		IoDetachDevice(fdo);
+		IoDeleteDevice(fdo);
+		break;
+	default:
+		status = IoAsyncForwardIrp(pdo, irp);
+		break;
+	}
+	IOTRACE("status: %08X", status);
+	IOEXIT(return status);
+}
+WIN_FUNC_DECL(NdisDispatchPnp,2)
+
+static void set_task_offload(struct ndis_device *wnd, void *buf,
+			     const int buf_size)
+{
+	struct ndis_task_offload_header *task_offload_header;
+	struct ndis_task_offload *task_offload;
+	struct ndis_task_tcp_ip_checksum *csum = NULL;
+	struct ndis_task_tcp_large_send *tso = NULL;
+	NDIS_STATUS status;
+
+	memset(buf, 0, buf_size);
+	task_offload_header = buf;
+	task_offload_header->version = NDIS_TASK_OFFLOAD_VERSION;
+	task_offload_header->size = sizeof(*task_offload_header);
+	task_offload_header->encap_format.flags.fixed_header_size = 1;
+	task_offload_header->encap_format.header_size = sizeof(struct ethhdr);
+	task_offload_header->encap_format.encap = IEEE_802_3_Encapsulation;
+	status = mp_query(wnd, OID_TCP_TASK_OFFLOAD, buf, buf_size);
+	TRACE1("%08X", status);
+	if (status != NDIS_STATUS_SUCCESS)
+		EXIT1(return);
+	if (task_offload_header->offset_first_task == 0)
+		EXIT1(return);
+	task_offload = ((void *)task_offload_header +
+			task_offload_header->offset_first_task);
+	while (1) {
+		TRACE1("%d, %d", task_offload->version, task_offload->task);
+		switch (task_offload->task) {
+		case TcpIpChecksumNdisTask:
+			csum = (void *)task_offload->task_buf;
+			break;
+		case TcpLargeSendNdisTask:
+			tso = (void *)task_offload->task_buf;
+			break;
+		default:
+			TRACE1("%d", task_offload->task);
+			break;
+		}
+		if (task_offload->offset_next_task == 0)
+			break;
+		task_offload = (void *)task_offload +
+			task_offload->offset_next_task;
+	}
+	if (tso)
+		TRACE1("%u, %u, %d, %d", tso->max_size, tso->min_seg_count,
+		       tso->tcp_opts, tso->ip_opts);
+	if (!csum)
+		EXIT1(return);
+	TRACE1("%08x, %08x", csum->v4_tx.value, csum->v4_rx.value);
+	task_offload_header->encap_format.flags.fixed_header_size = 1;
+	task_offload_header->encap_format.header_size = sizeof(struct ethhdr);
+	task_offload_header->offset_first_task = sizeof(*task_offload_header);
+	task_offload = ((void *)task_offload_header +
+			task_offload_header->offset_first_task);
+	task_offload->offset_next_task = 0;
+	task_offload->size = sizeof(*task_offload);
+	task_offload->task = TcpIpChecksumNdisTask;
+	memcpy(task_offload->task_buf, csum, sizeof(*csum));
+	task_offload->task_buf_length = sizeof(*csum);
+	status = mp_set(wnd, OID_TCP_TASK_OFFLOAD, task_offload_header,
+			sizeof(*task_offload_header) +
+			sizeof(*task_offload) + sizeof(*csum));
+	TRACE1("%08X", status);
+	if (status != NDIS_STATUS_SUCCESS)
+		EXIT2(return);
+	wnd->tx_csum = csum->v4_tx;
+	if (csum->v4_tx.tcp_csum && csum->v4_tx.udp_csum) {
+		if (csum->v4_tx.ip_csum) {
+			wnd->net_dev->features |= NETIF_F_HW_CSUM;
+			TRACE1("hw checksum enabled");
+		} else {
+			wnd->net_dev->features |= NETIF_F_IP_CSUM;
+			TRACE1("IP checksum enabled");
+		}
+		if (wnd->sg_dma_size)
+			wnd->net_dev->features |= NETIF_F_SG;
+	}
+	wnd->rx_csum = csum->v4_rx;
+	EXIT1(return);
+}
+
+static void get_supported_oids(struct ndis_device *wnd)
+{
+	NDIS_STATUS res;
+	int i, n, needed;
+	ndis_oid *oids;
+
+	res = mp_query_info(wnd, OID_GEN_SUPPORTED_LIST, NULL, 0, NULL,
+			    &needed);
+	if (!(res == NDIS_STATUS_BUFFER_TOO_SHORT ||
+	      res == NDIS_STATUS_INVALID_LENGTH))
+		EXIT1(return);
+	oids = kmalloc(needed, GFP_KERNEL);
+	if (!oids) {
+		TRACE1("couldn't allocate memory");
+		EXIT1(return);
+	}
+	res = mp_query(wnd, OID_GEN_SUPPORTED_LIST, oids, needed);
+	if (res) {
+		TRACE1("failed: %08X", res);
+		kfree(oids);
+		EXIT1(return);
+	}
+	for (i = 0, n = needed / sizeof(*oids); i < n; i++) {
+		TRACE1("oid: %08X", oids[i]);
+		/* if a wireless device didn't say so for
+		 * OID_GEN_PHYSICAL_MEDIUM (they should, but in case) */
+		if (wnd->physical_medium != NdisPhysicalMediumWirelessLan &&
+		    oids[i] == OID_802_11_SSID)
+			wnd->physical_medium = NdisPhysicalMediumWirelessLan;
+	}
+	kfree(oids);
+	EXIT1(return);
+}
+
+static void ndis_get_drvinfo(struct net_device *dev,
+			     struct ethtool_drvinfo *info)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 2);
+	strcat(info->driver, "+");
+	strncat(info->driver, wnd->wd->driver->name,
+		sizeof(info->driver) - strlen(DRIVER_NAME) - 1);
+	strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 2);
+	strcat(info->version, "+");
+	strncat(info->version, wnd->wd->driver->version,
+		sizeof(info->version) - strlen(DRIVER_VERSION) - 1);
+	if (wrap_is_pci_bus(wnd->wd->dev_bus))
+		strncpy(info->bus_info, pci_name(wnd->wd->pci.pdev),
+			sizeof(info->bus_info) - 1);
+#ifdef ENABLE_USB
+	else
+		usb_make_path(wnd->wd->usb.udev, info->bus_info,
+			      sizeof(info->bus_info) - 1);
+#endif
+	return;
+}
+
+static u32 ndis_get_link(struct net_device *dev)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	return netif_carrier_ok(wnd->net_dev);
+}
+
+static void ndis_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+
+	wol->supported = 0;
+	wol->wolopts = 0;
+	if (!(wnd->attributes & NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND))
+		EXIT2(return);
+	if (!wrap_is_pci_bus(wnd->wd->dev_bus))
+		EXIT2(return);
+	/* we always suspend to D3 */
+	if (wnd->pnp_capa.wakeup.min_magic_packet_wakeup < NdisDeviceStateD3)
+		return;
+	wol->supported |= WAKE_MAGIC;
+	if (wnd->ndis_wolopts & NDIS_PNP_WAKE_UP_MAGIC_PACKET)
+		wol->wolopts |= WAKE_MAGIC;
+	return;
+}
+
+static int ndis_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+
+	if (!(wnd->attributes & NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND))
+		return -EOPNOTSUPP;
+	if (wnd->pnp_capa.wakeup.min_magic_packet_wakeup < NdisDeviceStateD3)
+		EXIT2(return -EOPNOTSUPP);
+	TRACE2("0x%x", wol->wolopts);
+	if (wol->wolopts & WAKE_MAGIC) {
+		wnd->ndis_wolopts |= NDIS_PNP_WAKE_UP_MAGIC_PACKET;
+		if (wol->wolopts != WAKE_MAGIC)
+			WARNING("ignored wake-on-lan options: 0x%x",
+				wol->wolopts & ~WAKE_MAGIC);
+	} else if (!wol->wolopts)
+		wnd->ndis_wolopts = 0;
+	else
+		return -EOPNOTSUPP;
+	TRACE2("0x%x", wnd->ndis_wolopts);
+	return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
+static u32 ndis_get_tx_csum(struct net_device *dev)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	if (wnd->tx_csum.tcp_csum && wnd->tx_csum.udp_csum)
+		return 1;
+	else
+		return 0;
+}
+
+static u32 ndis_get_rx_csum(struct net_device *dev)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	if (wnd->rx_csum.value)
+		return 1;
+	else
+		return 0;
+}
+
+static int ndis_set_tx_csum(struct net_device *dev, u32 data)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+
+	if (data && (wnd->tx_csum.value == 0))
+		return -EOPNOTSUPP;
+
+	if (wnd->tx_csum.ip_csum)
+		ethtool_op_set_tx_hw_csum(dev, data);
+	else
+		ethtool_op_set_tx_csum(dev, data);
+	return 0;
+}
+
+static int ndis_set_rx_csum(struct net_device *dev, u32 data)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+
+	if (data && (wnd->tx_csum.value == 0))
+		return -EOPNOTSUPP;
+
+	/* TODO: enable/disable rx csum through NDIS */
+	return 0;
+}
+
+static u32 ndis_get_sg(struct net_device *dev)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	if (wnd->sg_dma_size)
+		return ethtool_op_get_sg(dev);
+	else
+		return 0;
+}
+
+static int ndis_set_sg(struct net_device *dev, u32 data)
+{
+	struct ndis_device *wnd = netdev_priv(dev);
+	if (wnd->sg_dma_size)
+		return ethtool_op_set_sg(dev, data);
+	else
+		return -EOPNOTSUPP;
+}
+#endif
+
+static struct ethtool_ops ndis_ethtool_ops = {
+	.get_drvinfo	= ndis_get_drvinfo,
+	.get_link	= ndis_get_link,
+	.get_wol	= ndis_get_wol,
+	.set_wol	= ndis_set_wol,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
+	.get_tx_csum	= ndis_get_tx_csum,
+	.get_rx_csum	= ndis_get_rx_csum,
+	.set_tx_csum	= ndis_set_tx_csum,
+	.set_rx_csum	= ndis_set_rx_csum,
+	.get_sg		= ndis_get_sg,
+	.set_sg		= ndis_set_sg,
+#endif
+};
+
+static int notifier_event(struct notifier_block *notifier, unsigned long event,
+			  void *ptr)
+{
+	struct net_device *net_dev = netdev_notifier_info_to_dev(ptr);
+
+	ENTER2("0x%lx", event);
+	if (net_dev->ethtool_ops == &ndis_ethtool_ops
+	    && event == NETDEV_CHANGENAME) {
+		struct ndis_device *wnd = netdev_priv(net_dev);
+
+		/* called with rtnl lock held, so no need to lock */
+		if (likely(wnd->procfs_iface)) {
+			printk(KERN_INFO "%s: interface renamed to '%s'\n",
+			       DRIVER_NAME, net_dev->name);
+			wrap_procfs_remove_ndis_device(wnd);
+			wrap_procfs_add_ndis_device(wnd);
+		}
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block netdev_notifier = {
+	.notifier_call = notifier_event,
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+static const struct net_device_ops ndis_netdev_ops = {
+	.ndo_init = ndis_net_dev_init,
+	.ndo_uninit = ndis_net_dev_uninit,
+	.ndo_open = ndis_net_dev_open,
+	.ndo_stop = ndis_net_dev_close,
+	.ndo_start_xmit = tx_skbuff,
+	.ndo_change_mtu = ndis_change_mtu,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+	.ndo_set_rx_mode = ndis_set_multicast_list,
+#else
+	.ndo_set_multicast_list = ndis_set_multicast_list,
+#endif
+	.ndo_set_mac_address = ndis_set_mac_address,
+	.ndo_get_stats = ndis_get_stats,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller = ndis_poll_controller,
+#endif
+};
+#endif
+
+static NDIS_STATUS ndis_start_device(struct ndis_device *wnd)
+{
+	struct wrap_device *wd;
+	struct net_device *net_dev;
+	NDIS_STATUS status;
+	char *buf;
+	const int buf_len = 256;
+	mac_address mac;
+	struct transport_header_offset *tx_header_offset;
+	int n;
+
+	ENTER2("%d", in_atomic());
+	status = mp_init(wnd);
+	if (status == NDIS_STATUS_NOT_RECOGNIZED)
+		EXIT1(return NDIS_STATUS_SUCCESS);
+	if (status != NDIS_STATUS_SUCCESS)
+		EXIT1(return status);
+	wd = wnd->wd;
+	net_dev = wnd->net_dev;
+
+	get_supported_oids(wnd);
+	memset(mac, 0, sizeof(mac));
+	status = mp_query(wnd, OID_802_3_CURRENT_ADDRESS, mac, sizeof(mac));
+	if (memcmp(mac, "\x00\x00\x00\x00\x00\x00", sizeof(mac)) == 0) {
+		status = mp_query(wnd, OID_802_3_PERMANENT_ADDRESS, mac,
+				  sizeof(mac));
+		if (status != NDIS_STATUS_SUCCESS) {
+			ERROR("couldn't get mac address: %08X", status);
+			goto err_start;
+		}
+	}
+	TRACE1("mac:" MACSTRSEP, MAC2STR(mac));
+	memcpy(net_dev->dev_addr, mac, ETH_ALEN);
+
+	strncpy(net_dev->name, if_name, IFNAMSIZ - 1);
+	net_dev->name[IFNAMSIZ - 1] = 0;
+
+	wnd->packet_filter = NDIS_PACKET_TYPE_DIRECTED |
+		NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_MULTICAST;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+	net_dev->netdev_ops = &ndis_netdev_ops;
+#else
+	net_dev->init = ndis_net_dev_init;
+	net_dev->uninit = ndis_net_dev_uninit;
+	net_dev->open = ndis_net_dev_open;
+	net_dev->hard_start_xmit = tx_skbuff;
+	net_dev->stop = ndis_net_dev_close;
+	net_dev->get_stats = ndis_get_stats;
+	net_dev->change_mtu = ndis_change_mtu;
+	net_dev->set_multicast_list = ndis_set_multicast_list;
+	net_dev->set_mac_address = ndis_set_mac_address;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	net_dev->poll_controller = ndis_poll_controller;
+#endif
+#endif
+#ifdef CONFIG_WIRELESS_EXT
+	if (wnd->physical_medium == NdisPhysicalMediumWirelessLan) {
+		net_dev->wireless_handlers = &ndis_handler_def;
+	}
+#endif
+	net_dev->ethtool_ops = &ndis_ethtool_ops;
+	if (wnd->mp_interrupt)
+		net_dev->irq = wnd->mp_interrupt->irq;
+	net_dev->mem_start = wnd->mem_start;
+	net_dev->mem_end = wnd->mem_end;
+	status = mp_query_int(wnd, OID_802_3_MAXIMUM_LIST_SIZE,
+			      &wnd->multicast_size);
+	if (status != NDIS_STATUS_SUCCESS || wnd->multicast_size < 0)
+		wnd->multicast_size = 0;
+	if (wnd->multicast_size > 0)
+		net_dev->flags |= IFF_MULTICAST;
+	else
+		net_dev->flags &= ~IFF_MULTICAST;
+
+	buf = kmalloc(buf_len, GFP_KERNEL);
+	if (!buf) {
+		WARNING("couldn't allocate memory");
+		goto err_start;
+	}
+
+	set_task_offload(wnd, buf, buf_len);
+#ifdef NETIF_F_LLTX
+	net_dev->features |= NETIF_F_LLTX;
+#endif
+
+	if (register_netdev(net_dev)) {
+		ERROR("cannot register net device %s", net_dev->name);
+		goto err_register;
+	}
+	memset(buf, 0, buf_len);
+	status = mp_query(wnd, OID_GEN_VENDOR_DESCRIPTION, buf, buf_len);
+	if (status != NDIS_STATUS_SUCCESS) {
+		WARNING("couldn't get vendor information: 0x%x", status);
+		buf[0] = 0;
+	}
+	wnd->drv_ndis_version = n = 0;
+	mp_query_int(wnd, OID_GEN_DRIVER_VERSION, &wnd->drv_ndis_version);
+	mp_query_int(wnd, OID_GEN_VENDOR_DRIVER_VERSION, &n);
+
+	printk(KERN_INFO "%s: ethernet device " MACSTRSEP " using %sNDIS "
+	       "driver: %s, version: 0x%x, NDIS version: 0x%x, vendor: '%s', "
+	       "%s\n", net_dev->name, MAC2STR(net_dev->dev_addr),
+	       deserialized_driver(wnd) ? "" : "serialized ",
+	       wd->driver->name, n, wnd->drv_ndis_version, buf,
+	       wd->conf_file_name);
+
+	if (deserialized_driver(wnd)) {
+		/* deserialized drivers don't have a limit, but we
+		 * keep max at TX_RING_SIZE */
+		wnd->max_tx_packets = TX_RING_SIZE;
+	} else {
+		status = mp_query_int(wnd, OID_GEN_MAXIMUM_SEND_PACKETS,
+				      &wnd->max_tx_packets);
+		if (status != NDIS_STATUS_SUCCESS)
+			wnd->max_tx_packets = 1;
+		if (wnd->max_tx_packets > TX_RING_SIZE)
+			wnd->max_tx_packets = TX_RING_SIZE;
+	}
+	TRACE2("maximum send packets: %d", wnd->max_tx_packets);
+	NdisAllocatePacketPoolEx(&status, &wnd->tx_packet_pool,
+				 wnd->max_tx_packets, 0,
+				 PROTOCOL_RESERVED_SIZE_IN_PACKET);
+	if (status != NDIS_STATUS_SUCCESS) {
+		ERROR("couldn't allocate packet pool");
+		goto packet_pool_err;
+	}
+	NdisAllocateBufferPool(&status, &wnd->tx_buffer_pool,
+			       wnd->max_tx_packets + 4);
+	if (status != NDIS_STATUS_SUCCESS) {
+		ERROR("couldn't allocate buffer pool");
+		goto buffer_pool_err;
+	}
+	TRACE1("pool: %p", wnd->tx_buffer_pool);
+
+	if (mp_query_int(wnd, OID_GEN_MAXIMUM_TOTAL_SIZE, &n) ==
+	    NDIS_STATUS_SUCCESS && n > ETH_HLEN)
+		ndis_change_mtu(wnd->net_dev, n - ETH_HLEN);
+
+	if (mp_query_int(wnd, OID_GEN_MAC_OPTIONS, &n) == NDIS_STATUS_SUCCESS)
+		TRACE2("mac options supported: 0x%x", n);
+
+	tx_header_offset = (typeof(tx_header_offset))buf;
+	tx_header_offset->protocol_type = NDIS_PROTOCOL_ID_TCP_IP;
+	tx_header_offset->header_offset = sizeof(ETH_HLEN);
+	status = mp_set(wnd, OID_GEN_TRANSPORT_HEADER_OFFSET,
+			tx_header_offset, sizeof(*tx_header_offset));
+	TRACE2("%08X", status);
+
+	status = mp_query_int(wnd, OID_GEN_PHYSICAL_MEDIUM,
+			      &wnd->physical_medium);
+	if (status != NDIS_STATUS_SUCCESS)
+		wnd->physical_medium = NdisPhysicalMediumUnspecified;
+
+#ifdef CONFIG_WIRELESS_EXT
+	if (wnd->physical_medium == NdisPhysicalMediumWirelessLan) {
+		mp_set_int(wnd, OID_802_11_POWER_MODE, NDIS_POWER_OFF);
+		get_encryption_capa(wnd, buf, buf_len);
+		TRACE1("capabilities = %ld", wnd->capa.encr);
+		printk(KERN_INFO "%s: encryption modes supported: "
+		       "%s%s%s%s%s%s%s\n", net_dev->name,
+		       test_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr) ?
+		       "WEP" : "none",
+
+		       test_bit(Ndis802_11Encryption2Enabled, &wnd->capa.encr) ?
+		       "; TKIP with WPA" : "",
+		       test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ?
+		       ", WPA2" : "",
+		       test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth) ?
+		       ", WPA2-PSK" : "",
+
+		       test_bit(Ndis802_11Encryption3Enabled, &wnd->capa.encr) ?
+		       "; AES/CCMP with WPA" : "",
+		       test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) ?
+		       ", WPA2" : "",
+		       test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth) ?
+		       ", WPA2-PSK" : "");
+
+		set_default_iw_params(wnd);
+	}
+#endif
+	kfree(buf);
+	hangcheck_add(wnd);
+	add_iw_stats_timer(wnd);
+	EXIT1(return NDIS_STATUS_SUCCESS);
+
+buffer_pool_err:
+	wnd->tx_buffer_pool = NULL;
+	if (wnd->tx_packet_pool) {
+		NdisFreePacketPool(wnd->tx_packet_pool);
+		wnd->tx_packet_pool = NULL;
+	}
+packet_pool_err:
+	unregister_netdev(net_dev);
+	wnd->max_tx_packets = 0;
+err_register:
+	kfree(buf);
+err_start:
+	mp_halt(wnd);
+	EXIT1(return NDIS_STATUS_FAILURE);
+}
+
+static int ndis_remove_device(struct ndis_device *wnd)
+{
+	s8 tx_pending;
+	int our_mutex;
+
+	/* prevent setting essid during disassociation */
+	memset(&wnd->essid, 0, sizeof(wnd->essid));
+	wnd->tx_ok = 0;
+	netif_carrier_off(wnd->net_dev);
+	if (wnd->max_tx_packets)
+		unregister_netdev(wnd->net_dev);
+	/* if device is suspended, but resume failed, tx_ring_mutex
+	 * may already be locked */
+	our_mutex = mutex_trylock(&wnd->tx_ring_mutex);
+	if (!our_mutex)
+		WARNING("couldn't obtain tx_ring_mutex");
+	spin_lock_bh(&wnd->tx_ring_lock);
+	tx_pending = wnd->tx_ring_end - wnd->tx_ring_start;
+	if (tx_pending < 0)
+		tx_pending += TX_RING_SIZE;
+	else if (tx_pending == 0 && wnd->is_tx_ring_full)
+		tx_pending = TX_RING_SIZE - 1;
+	wnd->is_tx_ring_full = 0;
+	/* throw away pending packets */
+	while (tx_pending-- > 0) {
+		struct ndis_packet *packet;
+
+		packet = wnd->tx_ring[wnd->tx_ring_start];
+		free_tx_packet(wnd, packet, NDIS_STATUS_CLOSING);
+		wnd->tx_ring_start = (wnd->tx_ring_start + 1) % TX_RING_SIZE;
+	}
+	spin_unlock_bh(&wnd->tx_ring_lock);
+	if (our_mutex)
+		mutex_unlock(&wnd->tx_ring_mutex);
+	mp_halt(wnd);
+	ndis_exit_device(wnd);
+
+	if (wnd->tx_packet_pool) {
+		NdisFreePacketPool(wnd->tx_packet_pool);
+		wnd->tx_packet_pool = NULL;
+	}
+	if (wnd->tx_buffer_pool) {
+		NdisFreeBufferPool(wnd->tx_buffer_pool);
+		wnd->tx_buffer_pool = NULL;
+	}
+	kfree(wnd->pmkids);
+	printk(KERN_INFO "%s: device %s removed\n", DRIVER_NAME,
+	       wnd->net_dev->name);
+	kfree(wnd->nmb);
+	free_netdev(wnd->net_dev);
+	EXIT2(return 0);
+}
+
+static NTSTATUS ndis_add_device(struct driver_object *drv_obj,
+				struct device_object *pdo)
+{
+	struct device_object *fdo;
+	struct ndis_mp_block *nmb;
+	NTSTATUS status;
+	struct ndis_device *wnd;
+	struct net_device *net_dev;
+	struct wrap_device *wd;
+	unsigned long i;
+
+	ENTER2("%p, %p", drv_obj, pdo);
+	if (strlen(if_name) >= IFNAMSIZ) {
+		ERROR("interface name '%s' is too long", if_name);
+		return STATUS_INVALID_PARAMETER;
+	}
+	net_dev = alloc_etherdev(sizeof(*wnd));
+	if (!net_dev) {
+		ERROR("couldn't allocate device");
+		return STATUS_RESOURCES;
+	}
+	wd = pdo->reserved;
+	if (wrap_is_pci_bus(wd->dev_bus))
+		SET_NETDEV_DEV(net_dev, &wd->pci.pdev->dev);
+	if (wrap_is_usb_bus(wd->dev_bus))
+		SET_NETDEV_DEV(net_dev, &wd->usb.intf->dev);
+	status = IoCreateDevice(drv_obj, 0, NULL, FILE_DEVICE_UNKNOWN, 0,
+				FALSE, &fdo);
+	if (status != STATUS_SUCCESS) {
+		free_netdev(net_dev);
+		EXIT2(return status);
+	}
+	wnd = netdev_priv(net_dev);
+	TRACE1("wnd: %p", wnd);
+
+	nmb = kmalloc(sizeof(*nmb), GFP_KERNEL);
+	if (!nmb) {
+		WARNING("couldn't allocate memory");
+		IoDeleteDevice(fdo);
+		free_netdev(net_dev);
+		return STATUS_RESOURCES;
+	}
+#if DEBUG >= 6
+	/* poison nmb so if a driver accesses uninitialized pointers, we
+	 * know what it is */
+	for (i = 0; i < sizeof(*nmb) / sizeof(unsigned long); i++)
+		((unsigned long *)nmb)[i] = i + 0x8a3fc1;
+#endif
+
+	wnd->nmb = nmb;
+	nmb->wnd = wnd;
+	nmb->pdo = pdo;
+	wnd->wd = wd;
+	wnd->net_dev = net_dev;
+	fdo->reserved = wnd;
+	nmb->fdo = fdo;
+	if (ndis_init_device(wnd)) {
+		IoDeleteDevice(fdo);
+		kfree(nmb);
+		free_netdev(net_dev);
+		EXIT1(return STATUS_RESOURCES);
+	}
+	nmb->next_device = IoAttachDeviceToDeviceStack(fdo, pdo);
+	spin_lock_init(&wnd->tx_ring_lock);
+	mutex_init(&wnd->tx_ring_mutex);
+	mutex_init(&wnd->ndis_req_mutex);
+	wnd->ndis_req_done = 0;
+	INIT_WORK(&wnd->tx_work, tx_worker);
+	wnd->tx_ring_start = 0;
+	wnd->tx_ring_end = 0;
+	wnd->is_tx_ring_full = 0;
+	wnd->capa.encr = 0;
+	wnd->capa.auth = 0;
+	wnd->attributes = 0;
+	wnd->dma_map_count = 0;
+	wnd->dma_map_addr = NULL;
+	wnd->nick[0] = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+	timer_setup(&wnd->hangcheck_timer, hangcheck_proc, 0);
+	timer_setup(&wnd->iw_stats_timer, hangcheck_proc, 0);
+#else
+	init_timer(&wnd->hangcheck_timer);
+	init_timer(&wnd->iw_stats_timer);
+#endif
+	wnd->scan_timestamp = 0;
+	wnd->iw_stats_interval = 10 * HZ;
+	wnd->ndis_pending_work = 0;
+	memset(&wnd->essid, 0, sizeof(wnd->essid));
+	memset(&wnd->encr_info, 0, sizeof(wnd->encr_info));
+	wnd->infrastructure_mode = Ndis802_11Infrastructure;
+	INIT_WORK(&wnd->ndis_work, wrapndis_worker);
+	wnd->iw_stats_enabled = TRUE;
+
+	TRACE1("nmb: %p, pdo: %p, fdo: %p, attached: %p, next: %p",
+	       nmb, pdo, fdo, fdo->attached, nmb->next_device);
+
+	/* dispatch routines are called as Windows functions */
+	for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
+		drv_obj->major_func[i] = WIN_FUNC_PTR(IoPassIrpDown,2);
+
+	drv_obj->major_func[IRP_MJ_PNP] = WIN_FUNC_PTR(NdisDispatchPnp,2);
+	drv_obj->major_func[IRP_MJ_POWER] = WIN_FUNC_PTR(NdisDispatchPower,2);
+	drv_obj->major_func[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
+		WIN_FUNC_PTR(NdisDispatchDeviceControl,2);
+//	drv_obj->major_func[IRP_MJ_DEVICE_CONTROL] =
+//		WIN_FUNC_PTR(NdisDispatchDeviceControl,2);
+	EXIT2(return STATUS_SUCCESS);
+}
+
+int init_ndis_driver(struct driver_object *drv_obj)
+{
+	ENTER1("%p", drv_obj);
+	drv_obj->drv_ext->add_device = ndis_add_device;
+	return 0;
+}
+
+int wrapndis_init(void)
+{
+	wrapndis_wq = create_singlethread_workqueue("wrapndis_wq");
+	if (!wrapndis_wq)
+		EXIT1(return -ENOMEM);
+	TRACE1("wrapndis_wq: %p", wrapndis_wq);
+	register_netdevice_notifier(&netdev_notifier);
+	return 0;
+}
+
+void wrapndis_exit(void)
+{
+	unregister_netdevice_notifier(&netdev_notifier);
+	if (wrapndis_wq)
+		destroy_workqueue(wrapndis_wq);
+}
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/wrapndis.h linux-5.6.11-ndis/3rdparty/ndiswrapper/wrapndis.h
--- linux-5.6.11/3rdparty/ndiswrapper/wrapndis.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/wrapndis.h	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,86 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#ifndef _WRAPNDIS_H_
+#define _WRAPNDIS_H_
+
+#include "ndis.h"
+#include "pnp.h"
+
+int wrapndis_init(void);
+void wrapndis_exit(void);
+
+NDIS_STATUS mp_reset(struct ndis_device *wnd);
+
+NDIS_STATUS mp_request(enum ndis_request_type request,
+		       struct ndis_device *wnd, ndis_oid oid,
+		       void *buf, ULONG buflen, ULONG *written, ULONG *needed);
+
+static inline NDIS_STATUS mp_query_info(struct ndis_device *wnd,
+					ndis_oid oid, void *buf, ULONG buflen,
+					ULONG *written, ULONG *needed)
+{
+	return mp_request(NdisRequestQueryInformation, wnd, oid,
+			  buf, buflen, written, needed);
+}
+
+static inline NDIS_STATUS mp_set_info(struct ndis_device *wnd,
+				      ndis_oid oid, void *buf, ULONG buflen,
+				      ULONG *written, ULONG *needed)
+{
+	return mp_request(NdisRequestSetInformation, wnd, oid,
+			  buf, buflen, written, needed);
+}
+
+static inline NDIS_STATUS mp_query(struct ndis_device *wnd, ndis_oid oid,
+				   void *buf, ULONG buflen)
+{
+	return mp_request(NdisRequestQueryInformation, wnd, oid,
+			  buf, buflen, NULL, NULL);
+}
+
+static inline NDIS_STATUS mp_query_int(struct ndis_device *wnd,
+				       ndis_oid oid, ULONG *data)
+{
+	return mp_request(NdisRequestQueryInformation, wnd, oid,
+			  data, sizeof(ULONG), NULL, NULL);
+}
+
+static inline NDIS_STATUS mp_set(struct ndis_device *wnd, ndis_oid oid,
+				 void *buf, ULONG buflen)
+{
+	return mp_request(NdisRequestSetInformation, wnd, oid,
+			  buf, buflen, NULL, NULL);
+}
+
+static inline NDIS_STATUS mp_set_int(struct ndis_device *wnd,
+				     ndis_oid oid, ULONG data)
+{
+	return mp_request(NdisRequestSetInformation, wnd, oid,
+			  &data, sizeof(ULONG), NULL, NULL);
+}
+
+void free_tx_packet(struct ndis_device *wnd, struct ndis_packet *packet,
+		    NDIS_STATUS status);
+int init_ndis_driver(struct driver_object *drv_obj);
+NDIS_STATUS ndis_reinit(struct ndis_device *wnd);
+void set_media_state(struct ndis_device *wnd, enum ndis_media_state state);
+
+void hangcheck_add(struct ndis_device *wnd);
+void hangcheck_del(struct ndis_device *wnd);
+
+struct iw_statistics *get_iw_stats(struct net_device *dev);
+
+#endif
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/wrapper.c linux-5.6.11-ndis/3rdparty/ndiswrapper/wrapper.c
--- linux-5.6.11/3rdparty/ndiswrapper/wrapper.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/wrapper.c	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,111 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#include "ndis.h"
+#include "iw_ndis.h"
+#include "loader.h"
+#include "pnp.h"
+#include "wrapper.h"
+
+char *if_name = "wlan%d";
+int proc_uid, proc_gid;
+int hangcheck_interval;
+static char *utils_version = UTILS_VERSION;
+int debug = DEBUG;
+
+module_param(if_name, charp, 0400);
+MODULE_PARM_DESC(if_name, "Network interface name or template "
+		 "(default: wlan%d)");
+module_param(proc_uid, int, 0600);
+MODULE_PARM_DESC(proc_uid, "The uid of the files created in /proc "
+		 "(default: 0).");
+module_param(proc_gid, int, 0600);
+MODULE_PARM_DESC(proc_gid, "The gid of the files created in /proc "
+		 "(default: 0).");
+module_param(debug, int, 0600);
+MODULE_PARM_DESC(debug, "debug level");
+
+/* 0 - default value provided by NDIS driver,
+ * positive value - force hangcheck interval to that many seconds
+ * negative value - disable hangcheck
+ */
+module_param(hangcheck_interval, int, 0600);
+MODULE_PARM_DESC(hangcheck_interval, "The interval, in seconds, for checking"
+		 " if driver is hung. (default: 0)");
+
+module_param(utils_version, charp, 0400);
+MODULE_PARM_DESC(utils_version, "Compatible version of utils "
+		 "(read only: " UTILS_VERSION ")");
+
+MODULE_AUTHOR("ndiswrapper team <ndiswrapper-general@lists.sourceforge.net>");
+#ifdef MODULE_DESCRIPTION
+MODULE_DESCRIPTION("NDIS wrapper driver");
+#endif
+#ifdef MODULE_VERSION
+MODULE_VERSION(DRIVER_VERSION);
+#endif
+MODULE_LICENSE("GPL");
+
+static void module_cleanup(void)
+{
+	loader_exit();
+	usb_exit();
+	wrap_procfs_remove();
+	wrapndis_exit();
+	ndis_exit();
+	ntoskernel_exit();
+	wrapmem_exit();
+}
+
+static int __init wrapper_init(void)
+{
+#ifdef TAINT_OOT_MODULE
+	add_taint(TAINT_OOT_MODULE, LOCKDEP_NOW_UNRELIABLE);
+#endif
+	printk(KERN_INFO "%s version %s loaded (smp=%s, preempt=%s)\n",
+	       DRIVER_NAME, DRIVER_VERSION,
+#ifdef CONFIG_SMP
+	       "yes"
+#else
+	       "no"
+#endif
+		,
+#ifdef CONFIG_PREEMPT_RT
+		"rt"
+#elif defined(CONFIG_PREEMPT)
+		"yes"
+#else
+		"no"
+#endif
+		);
+
+	if (wrapmem_init() || ntoskernel_init() || ndis_init() ||
+	    wrapndis_init() || usb_init() || wrap_procfs_init() ||
+	    loader_init()) {
+		module_cleanup();
+		ERROR("%s: initialization failed", DRIVER_NAME);
+		return -EINVAL;
+	}
+	EXIT1(return 0);
+}
+
+static void __exit wrapper_exit(void)
+{
+	ENTER1("");
+	module_cleanup();
+}
+
+module_init(wrapper_init);
+module_exit(wrapper_exit);
diff -Nurp linux-5.6.11/3rdparty/ndiswrapper/wrapper.h linux-5.6.11-ndis/3rdparty/ndiswrapper/wrapper.h
--- linux-5.6.11/3rdparty/ndiswrapper/wrapper.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-5.6.11-ndis/3rdparty/ndiswrapper/wrapper.h	2020-05-03 15:18:33.000000000 +0300
@@ -0,0 +1,24 @@
+/*
+ *  Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ */
+
+#ifndef WRAPPER_H
+#define WRAPPER_H
+
+extern char *if_name;
+extern int proc_uid;
+extern int proc_gid;
+extern int hangcheck_interval;
+
+#endif /* WRAPPER_H */