UBUNTU: ubuntu: ndiswrapper -- version 1.55
authorAndy Whitcroft <apw@canonical.com>
Mon, 29 Jun 2009 10:36:42 +0000 (11:36 +0100)
committerLeann Ogasawara <leann.ogasawara@canonical.com>
Mon, 28 Mar 2011 13:48:03 +0000 (06:48 -0700)
ExternalDriver: ndiswrapper
Description: Windows network driver wrapper
Url: http://sourceforge.net/project/showfiles.php?group_id=93482
Version: 1.55

Update ndiswrapper to the latest version (v1.55) from sourceforge,
and renable.

Signed-off-by: Andy Whitcroft <apw@canonical.com>

38 files changed:
ubuntu/Kconfig
ubuntu/Makefile
ubuntu/ndiswrapper/BOM [new file with mode: 0644]
ubuntu/ndiswrapper/Kconfig [new file with mode: 0644]
ubuntu/ndiswrapper/Makefile [new file with mode: 0644]
ubuntu/ndiswrapper/crt.c [new file with mode: 0644]
ubuntu/ndiswrapper/divdi3.c [new file with mode: 0644]
ubuntu/ndiswrapper/hal.c [new file with mode: 0644]
ubuntu/ndiswrapper/iw_ndis.c [new file with mode: 0644]
ubuntu/ndiswrapper/iw_ndis.h [new file with mode: 0644]
ubuntu/ndiswrapper/lin2win.h [new file with mode: 0644]
ubuntu/ndiswrapper/loader.c [new file with mode: 0644]
ubuntu/ndiswrapper/loader.h [new file with mode: 0644]
ubuntu/ndiswrapper/longlong.h [new file with mode: 0644]
ubuntu/ndiswrapper/mkexport.sh [new file with mode: 0644]
ubuntu/ndiswrapper/mkstubs.sh [new file with mode: 0644]
ubuntu/ndiswrapper/ndis.c [new file with mode: 0644]
ubuntu/ndiswrapper/ndis.h [new file with mode: 0644]
ubuntu/ndiswrapper/ndiswrapper.h [new file with mode: 0644]
ubuntu/ndiswrapper/ntoskernel.c [new file with mode: 0644]
ubuntu/ndiswrapper/ntoskernel.h [new file with mode: 0644]
ubuntu/ndiswrapper/ntoskernel_io.c [new file with mode: 0644]
ubuntu/ndiswrapper/pe_linker.c [new file with mode: 0644]
ubuntu/ndiswrapper/pe_linker.h [new file with mode: 0644]
ubuntu/ndiswrapper/pnp.c [new file with mode: 0644]
ubuntu/ndiswrapper/pnp.h [new file with mode: 0644]
ubuntu/ndiswrapper/proc.c [new file with mode: 0644]
ubuntu/ndiswrapper/rtl.c [new file with mode: 0644]
ubuntu/ndiswrapper/usb.c [new file with mode: 0644]
ubuntu/ndiswrapper/usb.h [new file with mode: 0644]
ubuntu/ndiswrapper/win2lin_stubs.S [new file with mode: 0644]
ubuntu/ndiswrapper/winnt_types.h [new file with mode: 0644]
ubuntu/ndiswrapper/wrapmem.c [new file with mode: 0644]
ubuntu/ndiswrapper/wrapmem.h [new file with mode: 0644]
ubuntu/ndiswrapper/wrapndis.c [new file with mode: 0644]
ubuntu/ndiswrapper/wrapndis.h [new file with mode: 0644]
ubuntu/ndiswrapper/wrapper.c [new file with mode: 0644]
ubuntu/ndiswrapper/wrapper.h [new file with mode: 0644]

index 5127808..1aa8160 100644 (file)
@@ -11,6 +11,10 @@ source "ubuntu/dm-raid4-5/Kconfig"
 ##
 ##
 ##
+source "ubuntu/ndiswrapper/Kconfig"
+##
+##
+##
 ##
 ##
 ##
index ccbd923..20c41e5 100644 (file)
@@ -13,6 +13,10 @@ obj-$(CONFIG_DM_RAID45)              += dm-raid4-5/
 ##
 ##
 ##
+obj-$(CONFIG_NDISWRAPPER)      += ndiswrapper/
+##
+##
+##
 ##
 ##
 ##
diff --git a/ubuntu/ndiswrapper/BOM b/ubuntu/ndiswrapper/BOM
new file mode 100644 (file)
index 0000000..c5dc0e4
--- /dev/null
@@ -0,0 +1,2 @@
+Downloaded from:       http://sourceforge.net/project/showfiles.php?group_id=93482
+Current Version:       1.55
diff --git a/ubuntu/ndiswrapper/Kconfig b/ubuntu/ndiswrapper/Kconfig
new file mode 100644 (file)
index 0000000..554d557
--- /dev/null
@@ -0,0 +1,4 @@
+config NDISWRAPPER
+       tristate "Wrapper for Windows NDIS network drivers"
+       depends on NET
+       default m
diff --git a/ubuntu/ndiswrapper/Makefile b/ubuntu/ndiswrapper/Makefile
new file mode 100644 (file)
index 0000000..2a8fe4e
--- /dev/null
@@ -0,0 +1,38 @@
+ndiswrapper-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 usb.o
+
+EXPORTS = crt_exports.h hal_exports.h ndis_exports.h ntoskernel_exports.h \
+       ntoskernel_io_exports.h rtl_exports.h usb_exports.h
+
+STUB_SRCS = crt.c hal.c ndis.c ntoskernel.c ntoskernel_io.c \
+       pnp.c rtl.c wrapndis.c usb.c
+
+
+EXTRA_CFLAGS += -DENABLE_USB -I$(obj)
+EXTRA_AFLAGS += -I$(obj)
+
+# generate exports symbol table from C files
+quiet_cmd_mkexport = MKEXPORT $@
+cmd_mkexport = $(SHELL) $(srctree)/$(src)/mkexport.sh $< $@
+
+%_exports.h: %.c $(srctree)/$(src)/mkexport.sh FORCE
+       $(call if_changed,mkexport)
+
+$(addprefix $(obj)/,$(EXPORTS:_exports.h=.o)): %.o: %_exports.h
+extra-y += $(EXPORTS)
+
+ifeq ($(CONFIG_X86_64),y)
+quiet_cmd_mkstubs = MKSTUBS $@
+cmd_mkstubs = $(SHELL) $(srctree)/$(src)/mkstubs.sh $(addprefix $(srctree)/$(src)/,$(STUB_SRCS)) >$@
+
+$(obj)/win2lin_stubs.h: $(addprefix $(srctree)/$(src)/,$(STUB_SRCS)) FORCE
+       $(call if_changed,mkstubs)
+
+$(obj)/win2lin_stubs.o: $(obj)/win2lin_stubs.h
+extra-y += win2lin_stubs.h
+ndiswrapper-objs += win2lin_stubs.o
+else
+ndiswrapper-objs += divdi3.o
+endif
+
+obj-$(CONFIG_NDISWRAPPER) := ndiswrapper.o
diff --git a/ubuntu/ndiswrapper/crt.c b/ubuntu/ndiswrapper/crt.c
new file mode 100644 (file)
index 0000000..ee0cbe1
--- /dev/null
@@ -0,0 +1,578 @@
+/*
+ *  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, GFP_KERNEL); \
+       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 */
+
+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);
+}
+
+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__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 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)
+{
+       net_srandom(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_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;
+}
+
+__attribute__((regparm(3))) s64 WIN_FUNC(_allshl,2)
+       (s64 a, u8 b)
+{
+       return a << b;
+}
+
+__attribute__((regparm(3))) u64 WIN_FUNC(_aullshl,2)
+       (u64 a, u8 b)
+{
+       return a << b;
+}
+
+__attribute__((regparm(3))) s64 WIN_FUNC(_allshr,2)
+       (s64 a, u8 b)
+{
+       return a >> b;
+}
+
+__attribute__((regparm(3))) 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);
+}
+
+int crt_init(void)
+{
+       return 0;
+}
+
+/* called when module is being removed */
+void crt_exit(void)
+{
+       EXIT4(return);
+}
diff --git a/ubuntu/ndiswrapper/divdi3.c b/ubuntu/ndiswrapper/divdi3.c
new file mode 100644 (file)
index 0000000..f8cb92e
--- /dev/null
@@ -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 != 0)
+       {
+         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 != 0)
+       {
+         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 != 0)
+           {
+             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 != 0)
+               {
+                 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 != 0)
+               {
+                 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 --git a/ubuntu/ndiswrapper/hal.c b/ubuntu/ndiswrapper/hal.c
new file mode 100644 (file)
index 0000000..1467d08
--- /dev/null
@@ -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 --git a/ubuntu/ndiswrapper/iw_ndis.c b/ubuntu/ndiswrapper/iw_ndis.c
new file mode 100644 (file)
index 0000000..434260e
--- /dev/null
@@ -0,0 +1,1973 @@
+ /*
+ *  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/version.h>
+#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"
+
+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"};
+
+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_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);
+       char ssid[NDIS_ESSID_MAX_SIZE];
+       int length;
+
+       ENTER2("");
+       memset(ssid, 0, sizeof(ssid));
+       /* 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) {
+               /* wireless-tools prior to version 20 add extra 1, and
+                * later than 20 don't! Deal with that mess */
+               length = wrqu->essid.length - 1;
+               if (length > 0)
+                       length--;
+               while (length < wrqu->essid.length && extra[length])
+                       length++;
+               TRACE2("%d", length);
+               if (length <= 0 || length > NDIS_ESSID_MAX_SIZE)
+                       EXIT2(return -EINVAL);
+       } else
+               length = 0;
+
+       set_assoc_params(wnd);
+
+       memcpy(ssid, extra, length);
+       if (set_essid(wnd, ssid, 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);
+}
+
+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 to 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 < (sizeof(network_names)/sizeof(network_names[0])))
+               return network_names[net_type];
+       else
+               return network_names[sizeof(network_names) /
+                                    sizeof(network_names[0]) - 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 <= (sizeof(freq_chan) / sizeof(freq_chan[0])))
+                       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;
+       ndis_rates_ex rates;
+
+       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_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);
+}
+
+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;
+       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 set_auth_mode(struct ndis_device *wnd)
+{
+       return set_iw_auth_mode(wnd, wnd->iw_auth_wpa_version,
+                               wnd->iw_auth_80211_alg);
+}
+
+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 set_encr_mode(struct ndis_device *wnd)
+{
+       return set_iw_encr_mode(wnd, wnd->iw_auth_cipher_pairwise,
+                               wnd->iw_auth_cipher_group);
+}
+
+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);
+}
+
+/* index must be 0 - N, as per NDIS  */
+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);
+}
+
+/* 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 remove_key;
+               remove_key.struct_size = sizeof(remove_key);
+               remove_key.index = index;
+               if (bssid) {
+                       /* pairwise key */
+                       if (memcmp(bssid, "\xff\xff\xff\xff\xff\xff",
+                                  ETH_ALEN) != 0)
+                               remove_key.index |= (1 << 30);
+                       memcpy(remove_key.bssid, bssid,
+                              sizeof(remove_key.bssid));
+               } else
+                       memset(remove_key.bssid, 0xff,
+                              sizeof(remove_key.bssid));
+               if (mp_set(wnd, OID_802_11_REMOVE_KEY,
+                          &remove_key, sizeof(remove_key)))
+                       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 < 0 || 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 >= IW_ESSID_MAX_SIZE || wrqu->data.length <= 0)
+               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;
+       int i, nrates;
+       unsigned char buf[MAX_WPA_IE_LEN * 2 + 30];
+       struct ndis_wlan_bssid *bssid;
+       struct ndis_wlan_bssid_ex *bssid_ex;
+
+       ENTER2("%p, %p", event, item);
+       bssid = item;
+       bssid_ex = item;
+       /* 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);
+       event = iwe_stream_add_event(info, event, end_buf, &iwe,
+                                    IW_EV_ADDR_LEN);
+
+       /* 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;
+       event = iwe_stream_add_point(info, event, end_buf, &iwe,
+                                    bssid->ssid.essid);
+
+       /* add protocol name */
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = SIOCGIWNAME;
+       strncpy(iwe.u.name, network_type_to_name(bssid->net_type), IFNAMSIZ);
+       event = iwe_stream_add_event(info, event, end_buf, &iwe,
+                                    IW_EV_CHAR_LEN);
+
+       /* 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_INFRA;
+       else // if (bssid->mode == Ndis802_11AutoUnknown)
+               iwe.u.mode = IW_MODE_AUTO;
+       event = iwe_stream_add_event(info, event, end_buf, &iwe,
+                                    IW_EV_UINT_LEN);
+
+       /* 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;
+       event = iwe_stream_add_event(info, event, end_buf, &iwe,
+                                    IW_EV_FREQ_LEN);
+
+       /* 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;
+       event = iwe_stream_add_event(info, event, end_buf, &iwe,
+                                    IW_EV_QUAL_LEN);
+
+       /* 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;
+       event = iwe_stream_add_point(info, event, end_buf, &iwe,
+                                    bssid->ssid.essid);
+
+       /* add rate */
+       memset(&iwe, 0, sizeof(iwe));
+       current_val = event + iwe_stream_lcp_len(info);
+       iwe.cmd = SIOCGIWRATE;
+       if (bssid->length > sizeof(*bssid))
+               nrates = NDIS_MAX_RATES_EX;
+       else
+               nrates = NDIS_MAX_RATES;
+       for (i = 0 ; i < nrates ; i++) {
+               if (bssid->rates[i] & 0x7f) {
+                       iwe.u.bitrate.value = ((bssid->rates[i] & 0x7f) *
+                                              500000);
+                       current_val = iwe_stream_add_value(info, event,
+                                                          current_val,
+                                                          end_buf, &iwe,
+                                                          IW_EV_PARAM_LEN);
+               }
+       }
+
+       if ((current_val - event) > iwe_stream_lcp_len(info))
+               event = current_val;
+
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = IWEVCUSTOM;
+       sprintf(buf, "bcn_int=%d", bssid->config.beacon_period);
+       iwe.u.data.length = strlen(buf);
+       event = iwe_stream_add_point(info, event, end_buf, &iwe, buf);
+
+       memset(&iwe, 0, sizeof(iwe));
+       iwe.cmd = IWEVCUSTOM;
+       sprintf(buf, "atim=%u", bssid->config.atim_window);
+       iwe.u.data.length = strlen(buf);
+       event = iwe_stream_add_point(info, event, end_buf, &iwe, buf);
+
+       TRACE2("%d, %u", bssid->length, (unsigned int)sizeof(*bssid));
+       if (bssid->length > sizeof(*bssid)) {
+               unsigned char *iep = (unsigned char *)bssid_ex->ies +
+                       sizeof(struct ndis_fixed_ies);
+               no_warn_unused unsigned char *end = iep + bssid_ex->ie_length;
+
+               while (iep + 1 < end && iep + 2 + iep[1] <= end) {
+                       unsigned char ielen = 2 + iep[1];
+
+                       if (ielen > SSID_MAX_WPA_IE_LEN) {
+                               iep += ielen;
+                               continue;
+                       }
+                       if ((iep[0] == WLAN_EID_GENERIC && iep[1] >= 4 &&
+                            memcmp(iep + 2, "\x00\x50\xf2\x01", 4) == 0) ||
+                           iep[0] == RSN_INFO_ELEM) {
+                               memset(&iwe, 0, sizeof(iwe));
+                               iwe.cmd = IWEVGENIE;
+                               iwe.u.data.length = ielen;
+                               event = iwe_stream_add_point(info, event,
+                                                            end_buf, &iwe,
+                                                            iep);
+                       }
+                       iep += ielen;
+               }
+       }
+       TRACE2("event = %p, current_val = %p", event, current_val);
+       EXIT2(return event);
+}
+
+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, list_len, needed;
+       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 */
+       list_len = sizeof(ULONG) + sizeof(struct ndis_wlan_bssid_ex) * 8;
+       bssid_list = kmalloc(list_len, GFP_KERNEL);
+       if (!bssid_list) {
+               ERROR("couldn't allocate memory");
+               return -ENOMEM;
+       }
+       /* some drivers don't set bssid_list->num_items to 0 if
+          OID_802_11_BSSID_LIST returns no items (prism54 driver, e.g.,) */
+       memset(bssid_list, 0, list_len);
+
+       needed = 0;
+       res = mp_query_info(wnd, OID_802_11_BSSID_LIST,
+                           bssid_list, list_len, NULL, &needed);
+       if (res == NDIS_STATUS_INVALID_LENGTH ||
+           res == NDIS_STATUS_BUFFER_TOO_SHORT) {
+               /* now try with required space */
+               kfree(bssid_list);
+               list_len = needed;
+               bssid_list = kmalloc(list_len, GFP_KERNEL);
+               if (!bssid_list) {
+                       ERROR("couldn't allocate memory");
+                       return -ENOMEM;
+               }
+               memset(bssid_list, 0, list_len);
+
+               res = mp_query(wnd, OID_802_11_BSSID_LIST,
+                              bssid_list, list_len);
+       }
+       if (res) {
+               WARNING("getting BSSID list failed (%08X)", res);
+               kfree(bssid_list);
+               EXIT2(return -EOPNOTSUPP);
+       }
+       TRACE2("%d", bssid_list->num_items);
+       cur_item = &bssid_list->bssid[0];
+       for (i = 0; i < bssid_list->num_items; i++) {
+               event = ndis_translate_scan(dev, info, event,
+                                           extra + IW_SCAN_MAX_DATA, cur_item);
+               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;
+       ULONG 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;
+       ULONG 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;
+       ndis_rates_ex rates;
+       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 = 18;
+
+       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 = (sizeof(freq_chan)/sizeof(freq_chan[0]));
+
+       for (i = 0; i < (sizeof(freq_chan)/sizeof(freq_chan[0])) &&
+                   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 ULONG 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;
+}
+
+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 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;
+       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%Lx", 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   = sizeof(ndis_handler) / sizeof(ndis_handler[0]),
+       .num_private    = sizeof(priv_handler) / sizeof(priv_handler[0]),
+       .num_private_args = sizeof(priv_args) / sizeof(priv_args[0]),
+
+       .standard       = (iw_handler *)ndis_handler,
+       .private        = (iw_handler *)priv_handler,
+       .private_args   = (struct iw_priv_args *)priv_args,
+       .get_wireless_stats = get_iw_stats,
+};
diff --git a/ubuntu/ndiswrapper/iw_ndis.h b/ubuntu/ndiswrapper/iw_ndis.h
new file mode 100644 (file)
index 0000000..9d057c3
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ *  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 {
+       ULONG elem_id;
+       UCHAR length;
+       UCHAR data[1];
+};
+
+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;
+       ndis_rates 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;
+       ndis_rates_ex rates_ex;
+       ULONG ie_length;
+       UCHAR ies[1];
+};
+
+/* 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;
+       ndis_pmkid_vavlue pmkid;
+};
+
+struct ndis_pmkid {
+       ULONG length;
+       ULONG bssid_info_count;
+       struct ndis_bssid_info bssid_info[1];
+};
+
+int add_wep_key(struct ndis_device *wnd, char *key, int key_len,
+               int index);
+int set_essid(struct ndis_device *wnd, const char *ssid, int ssid_len);
+int set_infra_mode(struct ndis_device *wnd,
+                  enum ndis_infrastructure_mode mode);
+int get_ap_address(struct ndis_device *wnd, mac_address mac);
+int set_ndis_auth_mode(struct ndis_device *wnd, ULONG auth_mode);
+int set_iw_auth_mode(struct ndis_device *wnd, int wpa_version,
+                    int auth_80211_alg);
+int set_auth_mode(struct ndis_device *wnd);
+int set_ndis_encr_mode(struct ndis_device *wnd, int cipher_pairwise,
+                      int cipher_groupwise);
+int get_ndis_encr_mode(struct ndis_device *wnd);
+int set_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);
+int set_priv_filter(struct ndis_device *wnd);
+int set_scan(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
+
+#define RSN_INFO_ELEM          0x30
+
+/* these have to match what is in wpa_supplicant */
+
+typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP } wpa_alg;
+typedef enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP,
+              CIPHER_WEP104 } wpa_cipher;
+typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE,
+              KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE } wpa_key_mgmt;
+
+#endif // IW_NDIS_H
diff --git a/ubuntu/ndiswrapper/lin2win.h b/ubuntu/ndiswrapper/lin2win.h
new file mode 100644 (file)
index 0000000..824f69c
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ *  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
+
+/* Windows functions must have 32 bytes of shadow space for arguments
+ * above return address, irrespective of number of args. So argc >= 4
+ */
+
+#define alloc_win_stack_frame(argc)            \
+       "sub $(" #argc "+1)*8, %%rsp\n\t"
+#define free_win_stack_frame(argc)             \
+       "add $(" #argc "+1)*8, %%rsp\n\t"
+
+/* m is index of Windows arg required; Windows arg 1 should be at
+ * 0(%rsp), arg 2 at 8(%rsp) and so on after the frame is allocated.
+*/
+
+#define lin2win_win_arg(m) "(" #m "-1)*8(%%rsp)"
+
+/* args for Windows function must be in clobber / output list */
+
+#define outputs()                                                      \
+       "=a" (_ret), "=c" (_dummy), "=d" (_dummy),                      \
+               "=r" (r8), "=r" (r9), "=r" (r10), "=r" (r11)
+
+#define clobbers() "cc"
+
+#define LIN2WIN0(func)                                                 \
+({                                                                     \
+       u64 _ret, _dummy;                                               \
+       register u64 r8 __asm__("r8");                                  \
+       register u64 r9 __asm__("r9");                                  \
+       register u64 r10 __asm__("r10");                                \
+       register u64 r11 __asm__("r11");                                \
+       __asm__ __volatile__(                                           \
+               alloc_win_stack_frame(4)                                \
+               "callq *%[fptr]\n\t"                                    \
+               free_win_stack_frame(4)                                 \
+               : outputs()                                             \
+               : [fptr] "r" (func)                                     \
+               : clobbers());                                          \
+       _ret;                                                           \
+})
+
+#define LIN2WIN1(func, arg1)                                           \
+({                                                                     \
+       u64 _ret, _dummy;                                               \
+       register u64 r8 __asm__("r8");                                  \
+       register u64 r9 __asm__("r9");                                  \
+       register u64 r10 __asm__("r10");                                \
+       register u64 r11 __asm__("r11");                                \
+       __asm__ __volatile__(                                           \
+               alloc_win_stack_frame(4)                                \
+               "callq *%[fptr]\n\t"                                    \
+               free_win_stack_frame(4)                                 \
+               : outputs()                                             \
+               : "c" (arg1), [fptr] "r" (func)                         \
+               : clobbers());                                          \
+       _ret;                                                           \
+})
+
+#define LIN2WIN2(func, arg1, arg2)                                     \
+({                                                                     \
+       u64 _ret, _dummy;                                               \
+       register u64 r8 __asm__("r8");                                  \
+       register u64 r9 __asm__("r9");                                  \
+       register u64 r10 __asm__("r10");                                \
+       register u64 r11 __asm__("r11");                                \
+       __asm__ __volatile__(                                           \
+               alloc_win_stack_frame(4)                                \
+               "callq *%[fptr]\n\t"                                    \
+               free_win_stack_frame(4)                                 \
+               : outputs()                                             \
+               : "c" (arg1), "d" (arg2), [fptr] "r" (func)             \
+               : clobbers());                                          \
+       _ret;                                                           \
+})
+
+#define LIN2WIN3(func, arg1, arg2, arg3)                               \
+({                                                                     \
+       u64 _ret, _dummy;                                               \
+       register u64 r8 __asm__("r8") = (u64)arg3;                      \
+       register u64 r9 __asm__("r9");                                  \
+       register u64 r10 __asm__("r10");                                \
+       register u64 r11 __asm__("r11");                                \
+       __asm__ __volatile__(                                           \
+               alloc_win_stack_frame(4)                                \
+               "callq *%[fptr]\n\t"                                    \
+               free_win_stack_frame(4)                                 \
+               : outputs()                                             \
+               : "c" (arg1), "d" (arg2), "r" (r8),                     \
+                 [fptr] "r" (func)                                     \
+               : clobbers());                                          \
+       _ret;                                                           \
+})
+
+#define LIN2WIN4(func, arg1, arg2, arg3, arg4)                         \
+({                                                                     \
+       u64 _ret, _dummy;                                               \
+       register u64 r8 __asm__("r8") = (u64)arg3;                      \
+       register u64 r9 __asm__("r9") = (u64)arg4;                      \
+       register u64 r10 __asm__("r10");                                \
+       register u64 r11 __asm__("r11");                                \
+       __asm__ __volatile__(                                           \
+               alloc_win_stack_frame(4)                                \
+               "callq *%[fptr]\n\t"                                    \
+               free_win_stack_frame(4)                                 \
+               : outputs()                                             \
+               : "c" (arg1), "d" (arg2), "r" (r8), "r" (r9),           \
+                 [fptr] "r" (func)                                     \
+               : clobbers());                                          \
+       _ret;                                                           \
+})
+
+#define LIN2WIN5(func, arg1, arg2, arg3, arg4, arg5)                   \
+({                                                                     \
+       u64 _ret, _dummy;                                               \
+       register u64 r8 __asm__("r8") = (u64)arg3;                      \
+       register u64 r9 __asm__("r9") = (u64)arg4;                      \
+       register u64 r10 __asm__("r10");                                \
+       register u64 r11 __asm__("r11");                                \
+       __asm__ __volatile__(                                           \
+               alloc_win_stack_frame(5)                                \
+               "movq %[rarg5], " lin2win_win_arg(5) "\n\t"             \
+               "callq *%[fptr]\n\t"                                    \
+               free_win_stack_frame(5)                                 \
+               : outputs()                                             \
+               : "c" (arg1), "d" (arg2), "r" (r8), "r" (r9),           \
+                 [rarg5] "ri" ((u64)arg5),                             \
+                 [fptr] "r" (func)                                     \
+               : clobbers());                                          \
+       _ret;                                                           \
+})
+
+#define LIN2WIN6(func, arg1, arg2, arg3, arg4, arg5, arg6)             \
+({                                                                     \
+       u64 _ret, _dummy;                                               \
+       register u64 r8 __asm__("r8") = (u64)arg3;                      \
+       register u64 r9 __asm__("r9") = (u64)arg4;                      \
+       register u64 r10 __asm__("r10");                                \
+       register u64 r11 __asm__("r11");                                \
+       __asm__ __volatile__(                                           \
+               alloc_win_stack_frame(6)                                \
+               "movq %[rarg5], " lin2win_win_arg(5) "\n\t"             \
+               "movq %[rarg6], " lin2win_win_arg(6) "\n\t"             \
+               "callq *%[fptr]\n\t"                                    \
+               free_win_stack_frame(6)                                 \
+               : outputs()                                             \
+               : "c" (arg1), "d" (arg2), "r" (r8), "r" (r9),           \
+                 [rarg5] "ri" ((u64)arg5), [rarg6] "ri" ((u64)arg6),   \
+                 [fptr] "r" (func)                                     \
+               : clobbers());                                          \
+       _ret;                                                           \
+})
+
+#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 --git a/ubuntu/ndiswrapper/loader.c b/ubuntu/ndiswrapper/loader.c
new file mode 100644 (file)
index 0000000..e1be090
--- /dev/null
@@ -0,0 +1,877 @@
+/*
+ *  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 */
+       {0x4d36e972, 0xe325, 0x11ce, },
+       /* USB WDM */
+       {0x36fc9e60, 0xc465, 0x11cf, },
+       /* Bluetooth */
+       {0xe0cbf06c, 0xcd8b, 0x4647, },
+       /* ivtcorporatino.com's bluetooth device claims this is
+        * bluetooth guid */
+       {0xf12d3cf8, 0xb11d, 0x457e, },
+};
+
+struct semaphore 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 < sizeof(class_guids) / sizeof(class_guids[0]); 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);
+       if (down_interruptible(&loader_mutex)) {
+               WARNING("couldn't obtain loader_mutex");
+               EXIT1(return NULL);
+       }
+       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;
+       }
+       up(&loader_mutex);
+
+       if (!wrap_driver) {
+               char *argv[] = {"loadndisdriver", WRAP_CMD_LOAD_DRIVER,
+#if defined(DEBUG) && 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);
+               if (down_interruptible(&loader_mutex)) {
+                       WARNING("couldn't obtain loader_mutex");
+                       EXIT1(return NULL);
+               }
+               INIT_COMPLETION(loader_complete);
+               ret = call_usermodehelper("/sbin/loadndisdriver", argv, env, 1);
+               if (ret) {
+                       up(&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;
+               }
+               up(&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: %lu bytes",
+                      (unsigned long)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,
+                                 PAGE_KERNEL_EXECUTABLE);
+#elif defined PAGE_KERNEL_EXEC
+               pe_image->image =
+                       __vmalloc(load_driver->sys_files[i].size,
+                                 GFP_KERNEL | __GFP_HIGHMEM,
+                                 PAGE_KERNEL_EXEC);
+#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,
+                                         __pgprot(__PAGE_KERNEL & ~_PAGE_NX));
+               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);
+       if (down_interruptible(&loader_mutex)) {
+               WARNING("couldn't obtain loader_mutex");
+               EXIT1(return NULL);
+       }
+       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;
+       }
+       up(&loader_mutex);
+       if (!driver) {
+               TRACE1("coudln'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 defined(DEBUG) && 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);
+               if (down_interruptible(&loader_mutex)) {
+                       WARNING("couldn't obtain loader_mutex");
+                       EXIT1(return NULL);
+               }
+               INIT_COMPLETION(loader_complete);
+               ret = call_usermodehelper("/sbin/loadndisdriver", argv, env, 1);
+               if (ret) {
+                       up(&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);
+               up(&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 settnigs 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);
+       if (down_interruptible(&loader_mutex))
+               WARNING("couldn't obtain 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);
+       up(&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);
+       }
+       if (driver->bin_files)
+               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);
+       InitializeListHead(&wrap_driver->wrap_devices);
+       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);
+               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);
+               EXIT1(return 0);
+       }
+}
+
+static struct pci_device_id wrap_pci_id_table[] = {
+       {PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID},
+};
+
+static struct pci_driver wrap_pci_driver = {
+       .name           = DRIVER_NAME,
+       .id_table       = wrap_pci_id_table,
+       .probe          = wrap_pnp_start_pci_device,
+       .remove         = __devexit_p(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 = __devexit_p(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;
+
+       if (down_interruptible(&loader_mutex))
+               WARNING("couldn't obtain 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_PRESENT, &wd->hw_status);
+       }
+       up(&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 defined(DEBUG) && 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);
+               if (down_interruptible(&loader_mutex)) {
+                       WARNING("couldn't obtain loader_mutex");
+                       EXIT1(return NULL);
+               }
+               INIT_COMPLETION(loader_complete);
+               ret = call_usermodehelper("/sbin/loadndisdriver", argv, env, 1);
+               if (ret) {
+                       up(&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;
+               }
+               up(&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;
+
+       if (down_interruptible(&loader_mutex)) {
+               WARNING("couldn't obtain loader_mutex");
+               return NULL;
+       }
+       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;
+       }
+       up(&loader_mutex);
+       return wd;
+}
+
+/* called with loader_mutex is down */
+static int wrapper_ioctl(struct inode *inode, 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: %u", 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 %u", cmd);
+               ret = -EINVAL;
+               break;
+       }
+       complete(&loader_complete);
+       EXIT1(return ret);
+}
+
+static int wrapper_ioctl_release(struct inode *inode, struct file *file)
+{
+       ENTER1("");
+       return 0;
+}
+
+static struct file_operations wrapper_fops = {
+       .owner          = THIS_MODULE,
+       .ioctl          = wrapper_ioctl,
+       .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);
+       init_MUTEX(&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();
+       if (down_interruptible(&loader_mutex))
+               WARNING("couldn't obtain 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);
+       }
+       up(&loader_mutex);
+       EXIT1(return);
+}
diff --git a/ubuntu/ndiswrapper/loader.h b/ubuntu/ndiswrapper/loader.h
new file mode 100644 (file)
index 0000000..5073f49
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ *  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_devices {
+       int count;
+       struct load_device *devices;
+};
+
+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 *)
+
+#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 semaphore loader_mutex;
+#endif
+
+#endif /* LOADER_H */
+
diff --git a/ubuntu/ndiswrapper/longlong.h b/ubuntu/ndiswrapper/longlong.h
new file mode 100644 (file)
index 0000000..cd1e30e
--- /dev/null
@@ -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" ((USItype) (sh)),                                     \
+            "=&r" ((USItype) (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" ((USItype) (sh)),                                     \
+            "=&r" ((USItype) (sl))                                     \
+          : "0" ((USItype) (ah)),                                      \
+            "g" ((USItype) (bh)),                                      \
+            "1" ((USItype) (al)),                                      \
+            "g" ((USItype) (bl)))
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("mull %3"                                                   \
+          : "=a" ((USItype) (w0)),                                     \
+            "=d" ((USItype) (w1))                                      \
+          : "%0" ((USItype) (u)),                                      \
+            "rm" ((USItype) (v)))
+#define udiv_qrnnd(q, r, n1, n0, dv) \
+  __asm__ ("divl %4"                                                   \
+          : "=a" ((USItype) (q)),                                      \
+            "=d" ((USItype) (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 --git a/ubuntu/ndiswrapper/mkexport.sh b/ubuntu/ndiswrapper/mkexport.sh
new file mode 100644 (file)
index 0000000..174f38d
--- /dev/null
@@ -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\|__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 --git a/ubuntu/ndiswrapper/mkstubs.sh b/ubuntu/ndiswrapper/mkstubs.sh
new file mode 100644 (file)
index 0000000..7aab344
--- /dev/null
@@ -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 --git a/ubuntu/ndiswrapper/ndis.c b/ubuntu/ndiswrapper/ndis.c
new file mode 100644 (file)
index 0000000..d2174b4
--- /dev/null
@@ -0,0 +1,2974 @@
+/*
+ *  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 void ndis_worker(worker_param_t dummy);
+static work_struct_t ndis_work;
+static struct nt_list ndis_work_list;
+static spinlock_t ndis_work_list_lock;
+
+workqueue_struct_t *ndis_wq;
+static struct nt_thread *ndis_worker_thread;
+
+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 < (sizeof(mp_funcs) / sizeof(mp_funcs[0])); i++)
+                       TRACE2("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", (ULONG)param->data.integer);
+               break;
+       case NdisParameterHexInteger:
+               param->data.integer = simple_strtol(setting->value, NULL, 16);
+               TRACE2("0x%x", (ULONG)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", (ULONG)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, sizeof(u32), "%u",
+                        param->data.integer);
+               setting->value[sizeof(ULONG)] = 0;
+               break;
+       case NdisParameterHexInteger:
+               snprintf(setting->value, sizeof(u32), "%x",
+                        param->data.integer);
+               setting->value[sizeof(ULONG)] = 0;
+               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;
+       case NdisParameterBinary:
+               snprintf(setting->value, sizeof(u32), "%u",
+                        param->data.integer);
+               setting->value[sizeof(ULONG)] = 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;
+       if (down_interruptible(&loader_mutex))
+               WARNING("couldn't obtain loader_mutex");
+       nt_list_for_each_entry(setting, setting_list, list) {
+               if (strnicmp(keyname, setting->name, length) == 0) {
+                       TRACE2("setting %s='%s'", keyname, setting->value);
+                       up(&loader_mutex);
+                       *param = ndis_encode_setting(setting, type);
+                       if (*param)
+                               EXIT2(return 0);
+                       else
+                               EXIT2(return -1);
+               }
+       }
+       up(&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);
+
+       if (down_interruptible(&loader_mutex))
+               WARNING("couldn't obtain loader_mutex");
+       nt_list_for_each_entry(setting, &nmb->wnd->wd->settings, list) {
+               if (strnicmp(keyname, setting->name, ansi.length) == 0) {
+                       up(&loader_mutex);
+                       if (ndis_decode_setting(setting, param))
+                               *status = NDIS_STATUS_FAILURE;
+                       else
+                               *status = NDIS_STATUS_SUCCESS;
+                       RtlFreeAnsiString(&ansi);
+                       EXIT2(return);
+               }
+       }
+       up(&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;
+                       if (down_interruptible(&loader_mutex))
+                               WARNING("couldn't obtain loader_mutex");
+                       InsertTailList(&nmb->wnd->wd->settings, &setting->list);
+                       up(&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;
+       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;
+       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("%Lx, %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 simplisitic 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 (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
+       }
+       /* 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 = kmalloc(basemap * sizeof(*(wnd->dma_map_addr)),
+                                   GFP_KERNEL);
+       if (!wnd->dma_map_addr)
+               EXIT2(return NDIS_STATUS_RESOURCES);
+       memset(wnd->dma_map_addr, 0, basemap * sizeof(*(wnd->dma_map_addr)));
+       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 %p not freed by "
+                                       "Windows driver", wnd->net_dev->name,
+                                       (void *)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 (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: %lx", buf, index,
+                      (unsigned 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("%Lx, %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 (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("%lx", (unsigned 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);
+       *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, %Lx, %u", virt, addr, size);
+       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)
+               schedule_wrapndis_work(&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);
+                       schedule_wrapndis_work(&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;
+       schedule_wrapndis_work(&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;
+               }
+               wnd->net_dev->last_rx = jiffies;
+               /* 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, %Lu", 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);
+       }
+       wnd->net_dev->last_rx = jiffies;
+
+       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);
+       }
+       wnd->net_dev->last_rx = jiffies;
+       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("%Lu, %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 dma_size, ULONG max_phy_map)
+{
+       struct ndis_device *wnd = nmb->wnd;
+       ENTER2("dma_size=%d, maxtransfer=%u", dma_size, max_phy_map);
+#ifdef CONFIG_X86_64
+       if (dma_size != NDIS_DMA_64BITS) {
+               TRACE1("DMA size is not 64-bits");
+               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 (CHAR)NR_CPUS;
+}
+
+wstdcall void WIN_FUNC(NdisGetCurrentProcessorCounts,3)
+       (ULONG *idle, ULONG *kernel_user, ULONG *index)
+{
+       int cpu = smp_processor_id();
+       *idle = kstat_cpu(cpu).cpustat.idle;
+       *kernel_user = kstat_cpu(cpu).cpustat.system +
+               kstat_cpu(cpu).cpustat.user;
+       *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(worker_param_t 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);
+       schedule_ndis_work(&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,2)
+       (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();
+}
+
+static void *ndis_get_routine_address(char *name)
+{
+       int i;
+       ENTER2("%p", name);
+       for (i = 0; i < sizeof(ndis_exports) / sizeof(ndis_exports[0]); 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);
+       if (down_interruptible(&loader_mutex))
+               WARNING("couldn't obtain 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;
+               }
+       }
+       up(&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);
+       initialize_work(&ndis_work, ndis_worker, NULL);
+
+       ndis_wq = create_singlethread_workqueue("ndis_wq");
+       if (!ndis_wq) {
+               WARNING("couldn't create worker thread");
+               EXIT1(return -ENOMEM);
+       }
+
+       ndis_worker_thread = wrap_worker_init(ndis_wq);
+       TRACE1("%p", ndis_worker_thread);
+       return 0;
+}
+
+/* ndis_exit is called once when module is removed */
+void ndis_exit(void)
+{
+       ENTER1("");
+       if (ndis_wq)
+               destroy_workqueue(ndis_wq);
+       TRACE1("%p", ndis_worker_thread);
+       if (ndis_worker_thread)
+               ObDereferenceObject(ndis_worker_thread);
+       EXIT1(return);
+}
diff --git a/ubuntu/ndiswrapper/ndis.h b/ubuntu/ndiswrapper/ndis.h
new file mode 100644 (file)
index 0000000..6a01eb9
--- /dev/null
@@ -0,0 +1,1314 @@
+/*
+ *  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 ndis_rates[NDIS_MAX_RATES];
+typedef UCHAR ndis_rates_ex[NDIS_MAX_RATES_EX];
+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 UCHAR ndis_pmkid_vavlue[16];
+
+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 *)];
+               } deserailized_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 hanghcheck_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;
+
+       work_struct_t 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 semaphore tx_ring_mutex;
+       unsigned int max_tx_packets;
+       struct semaphore 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;
+
+       work_struct_t 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;
+       char netdev_name[IFNAMSIZ];
+};
+
+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
+
+/* Atrribute 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 --git a/ubuntu/ndiswrapper/ndiswrapper.h b/ubuntu/ndiswrapper/ndiswrapper.h
new file mode 100644 (file)
index 0000000..ae7b963
--- /dev/null
@@ -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.55"
+#define UTILS_VERSION "1.9"
+
+#define DRIVER_NAME "ndiswrapper"
+#define DRIVER_CONFIG_DIR "/etc/ndiswrapper"
+
+#define SSID_MAX_WPA_IE_LEN 40
+#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 WLAN_EID_GENERIC 221
+#define MAX_WPA_IE_LEN 64
+#define MAX_STR_LEN 512
+
+#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 compatability 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 "ndiswrapper (%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(fmt, ...) do { } while (0)
+#define TRACE1(fmt, ...) do { } while (0)
+#define TRACE2(fmt, ...) do { } while (0)
+#define TRACE3(fmt, ...) do { }  while (0)
+#define TRACE4(fmt, ...) do { } while (0)
+#define TRACE5(fmt, ...) do { } while (0)
+#define TRACE6(fmt, ...) do { } while (0)
+
+/* for a block of code */
+#define DBG_BLOCK(level) while (0)
+
+extern int debug;
+
+#if defined DEBUG
+#undef TRACE
+#define TRACE(level, fmt, ...)                                \
+do {                                                                  \
+       if (debug >= level)                                            \
+               printk(KERN_INFO "%s (%s:%d): " fmt "\n", DRIVER_NAME, \
+                      __func__, __LINE__ , ## __VA_ARGS__);       \
+} while (0)
+#undef DBG_BLOCK
+#define DBG_BLOCK(level) if (debug >= level)
+#endif
+
+#if defined(DEBUG) && DEBUG >= 1
+#undef TRACE1
+#define TRACE1(fmt, ...) TRACE(1, fmt , ## __VA_ARGS__)
+#endif
+
+#if defined(DEBUG) && DEBUG >= 2
+#undef TRACE2
+#define TRACE2(fmt, ...) TRACE(2, fmt , ## __VA_ARGS__)
+#endif
+
+#if defined(DEBUG) && DEBUG >= 3
+#undef TRACE3
+#define TRACE3(fmt, ...) TRACE(3, fmt , ## __VA_ARGS__)
+#endif
+
+#if defined(DEBUG) && DEBUG >= 4
+#undef TRACE4
+#define TRACE4(fmt, ...) TRACE(4, fmt , ## __VA_ARGS__)
+#endif
+
+#if defined(DEBUG) && DEBUG >= 5
+#undef TRACE5
+#define TRACE5(fmt, ...) TRACE(5, fmt , ## __VA_ARGS__)
+#endif
+
+#if defined(DEBUG) && DEBUG >= 6
+#undef TRACE6
+#define TRACE6(fmt, ...) TRACE(6, fmt , ## __VA_ARGS__)
+#endif
+
+#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 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 TRACE1
+#define USBENTER ENTER1
+#define USBEXIT EXIT1
+#else
+#define USBTRACE(fmt, ...)
+#define USBENTER(fmt, ...)
+#define USBEXIT(stmt) stmt
+#endif
+
+#if defined(EVENT_DEBUG)
+#define EVENTTRACE TRACE1
+#define EVENTENTER ENTER1
+#define EVENTEXIT EXIT1
+#else
+#define EVENTTRACE(fmt, ...)
+#define EVENTENTER(fmt, ...)
+#define EVENTEXIT(stmt) stmt
+#endif
+
+#if defined(TIMER_DEBUG)
+#define TIMERTRACE TRACE1
+#define TIMERENTER ENTER1
+#define TIMEREXIT EXIT1
+#else
+#define TIMERTRACE(fmt, ...)
+#define TIMERENTER(fmt, ...)
+#define TIMEREXIT(stmt) stmt
+#endif
+
+#if defined(IO_DEBUG)
+#define IOTRACE TRACE1
+#define IOENTER ENTER1
+#define IOEXIT EXIT1
+#else
+#define IOTRACE(fmt, ...)
+#define IOENTER(fmt, ...)
+#define IOEXIT(stmt) stmt
+#endif
+
+#if defined(WORK_DEBUG)
+#define WORKTRACE TRACE1
+#define WORKENTER ENTER1
+#define WORKEXIT EXIT1
+#else
+#define WORKTRACE(fmt, ...)
+#define WORKENTER(fmt, ...)
+#define WORKEXIT(stmt) stmt
+#endif
+
+#ifdef DEBUG
+#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 --git a/ubuntu/ndiswrapper/ntoskernel.c b/ubuntu/ndiswrapper/ntoskernel.c
new file mode 100644 (file)
index 0000000..d8e0111
--- /dev/null
@@ -0,0 +1,2683 @@
+/*
+ *  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 work_struct_t kdpc_work;
+static void kdpc_worker(worker_param_t 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 work_struct_t ntos_work;
+static struct nt_list ntos_work_list;
+static spinlock_t ntos_work_lock;
+static void ntos_work_worker(worker_param_t dummy);
+static struct nt_thread *ntos_worker_thread;
+spinlock_t irp_cancel_lock;
+static NT_SPIN_LOCK nt_list_lock;
+static struct nt_slist wrap_timer_slist;
+
+/* 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;
+static void update_user_shared_data_proc(unsigned long data);
+#endif
+
+WIN_SYMBOL_MAP("KeTickCount", &jiffies)
+
+WIN_SYMBOL_MAP("NlsMbCodePageTag", FALSE)
+
+workqueue_struct_t *ntos_wq;
+
+#ifdef WRAP_PREEMPT
+DEFINE_PER_CPU(irql_info_t, irql_info);
+#endif
+
+#if defined(CONFIG_X86_64)
+static void update_user_shared_data_proc(unsigned long data)
+{
+       /* timer is supposed to be scheduled every 10ms, but bigger
+        * intervals seem to work (tried upto 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;
+}
+
+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);
+}
+
+static void timer_proc(unsigned long data)
+{
+       struct wrap_timer *wrap_timer = (struct wrap_timer *)data;
+       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 = kmalloc(sizeof(*wrap_timer), irql_gfp());
+       else
+               wrap_timer = slack_kmalloc(sizeof(*wrap_timer));
+       if (!wrap_timer) {
+               ERROR("couldn't allocate memory for timer");
+               return;
+       }
+
+       memset(wrap_timer, 0, sizeof(*wrap_timer));
+       init_timer(&wrap_timer->timer);
+       wrap_timer->timer.data = (unsigned long)wrap_timer;
+       wrap_timer->timer.function = timer_proc;
+       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, 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, %Ld, %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, %Ld, %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(worker_param_t 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)
+               schedule_ntos_work(&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(worker_param_t 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);
+       schedule_ntos_work(&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);
+}
+
+#undef ExAllocatePoolWithTag
+
+wstdcall void *WIN_FUNC(ExAllocatePoolWithTag,3)
+       (enum pool_type pool_type, SIZE_T size, ULONG tag)
+{
+       void *addr;
+
+       ENTER4("pool_type: %d, size: %lu, 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,
+                                        PAGE_KERNEL);
+                       TRACE1("%p, %lu", addr, size);
+               } else {
+                       addr = vmalloc(size);
+                       TRACE1("%p, %lu", addr, size);
+               }
+       }
+       DBG_BLOCK(1) {
+               if (addr)
+                       TRACE4("addr: %p, %lu", addr, size);
+               else
+                       TRACE1("failed: %lu", 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: %lu, 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);
+}
+
+#if defined(ALLOC_DEBUG) && ALLOC_DEBUG > 1
+#define ExAllocatePoolWithTag(pool_type, size, tag)                    \
+       wrap_ExAllocatePoolWithTag(pool_type, size, tag, __FILE__, __LINE__)
+#endif
+
+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_signalled(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;
+
+       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);
+
+       DBG_BLOCK(2) {
+               KIRQL irql = current_irql();
+               if (irql >= DISPATCH_LEVEL) {
+                       TRACE2("wait in atomic context: %lu, %d, %ld",
+                              wait_hz, in_atomic(), in_interrupt());
+               }
+       }
+       assert_irql(_irql_ < DISPATCH_LEVEL);
+       EVENTTRACE("%p: sleep for %ld on %p", current, wait_hz, &wait_done);
+       /* we don't honor 'alertable' - according to decription 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) signalled */
+               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, 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_signalled(&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_signalled(&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 everytime a wait
+        * is satisified */
+       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_signalled(&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, %Ld, %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("%Lu, %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;
+#ifdef num_online_cpus
+       n = num_online_cpus();
+#else
+       n = NR_CPUS;
+#endif
+       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 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("%lu, 0x%lx, 0x%lx, 0x%lx, %d", size, (long)lowest,
+              (long)highest, (long)boundary, cache_type);
+       flags = irql_gfp();
+       addr = wrap_get_free_pages(flags, size);
+       TRACE2("%p, %lu, 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
+       addr = wrap_get_free_pages(flags, size);
+       TRACE2("%p, %lu, 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, %lu", 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)
+               virt = ioremap(phys_addr, size);
+       else
+               virt = ioremap_nocache(phys_addr, size);
+       TRACE1("%Lx, %lu, %p", phys_addr, size, virt);
+       return virt;
+}
+
+wstdcall void WIN_FUNC(MmUnmapIoSpace,2)
+       (void __iomem *addr, SIZE_T size)
+{
+       ENTER1("%p, %lu", 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
+ * devels, 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, %u", length + offset, (unsigned int)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, 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,4)
+       (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 */
+noregparm ULONG WIN_FUNC(DbgPrint,12)
+       (char *format, ...)
+{
+#ifdef DEBUG
+       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;
+}
+
+wstdcall void WIN_FUNC(KeBugCheck,1)
+       (ULONG code)
+{
+       TODO();
+       return;
+}
+
+wstdcall void WIN_FUNC(KeBugCheckEx,5)
+       (ULONG code, ULONG_PTR param1, ULONG_PTR param2,
+        ULONG_PTR param3, ULONG_PTR param4)
+{
+       TODO();
+       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();
+}
+
+wstdcall void WIN_FUNC(__chkstk,0)
+       (void)
+{
+       TODO();
+}
+
+struct worker_init_struct {
+       work_struct_t work;
+       struct completion completion;
+       struct nt_thread *nt_thread;
+};
+
+static void wrap_worker_init_func(worker_param_t param)
+{
+       struct worker_init_struct *worker_init_struct;
+
+       worker_init_struct =
+               worker_param_data(param, struct worker_init_struct, work);
+       TRACE1("%p", worker_init_struct);
+       worker_init_struct->nt_thread = create_nt_thread(current);
+       if (!worker_init_struct->nt_thread)
+               WARNING("couldn't create worker thread");
+       complete(&worker_init_struct->completion);
+}
+
+struct nt_thread *wrap_worker_init(workqueue_struct_t *wq)
+{
+       struct worker_init_struct worker_init_struct;
+
+       TRACE1("%p", &worker_init_struct);
+       init_completion(&worker_init_struct.completion);
+       initialize_work(&worker_init_struct.work, wrap_worker_init_func,
+                       &worker_init_struct);
+       worker_init_struct.nt_thread = NULL;
+       if (wq)
+               queue_work(wq, &worker_init_struct.work);
+       else
+               schedule_work(&worker_init_struct.work);
+       wait_for_completion(&worker_init_struct.completion);
+       TRACE1("%p", worker_init_struct.nt_thread);
+       return worker_init_struct.nt_thread;
+}
+
+int ntoskernel_init(void)
+{
+       struct timeval now;
+
+       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);
+
+       initialize_work(&kdpc_work, kdpc_worker, NULL);
+       initialize_work(&ntos_work, ntos_work_worker, NULL);
+       wrap_timer_slist.next = NULL;
+
+       do_gettimeofday(&now);
+       wrap_ticks_to_boot = TICKS_1601_TO_1970;
+       wrap_ticks_to_boot += (u64)now.tv_sec * TICKSPERSEC;
+       wrap_ticks_to_boot += now.tv_usec * 10;
+       wrap_ticks_to_boot -= jiffies * TICKSPERJIFFY;
+       TRACE2("%Lu", wrap_ticks_to_boot);
+
+#ifdef WRAP_PREEMPT
+       do {
+               int cpu;
+               for_each_possible_cpu(cpu) {
+                       irql_info_t *info;
+                       info = &per_cpu(irql_info, cpu);
+                       mutex_init(&(info->lock));
+                       info->task = NULL;
+                       info->count = 0;
+               }
+       } while (0);
+#endif
+
+       ntos_wq = create_singlethread_workqueue("ntos_wq");
+       if (!ntos_wq) {
+               WARNING("couldn't create ntos_wq thread");
+               return -ENOMEM;
+       }
+       ntos_worker_thread = wrap_worker_init(ntos_wq);
+       TRACE1("%p", ntos_worker_thread);
+
+       if (add_bus_driver("PCI")
+#ifdef ENABLE_USB
+           || add_bus_driver("USB")
+#endif
+               ) {
+               ntoskernel_exit();
+               return -ENOMEM;
+       }
+       mdl_cache =
+               wrap_kmem_cache_create("wrap_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();
+       init_timer(&shared_data_timer);
+       shared_data_timer.function = update_user_shared_data_proc;
+       shared_data_timer.data = (unsigned long)0;
+#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);
+       TRACE1("%p", ntos_worker_thread);
+       if (ntos_worker_thread)
+               ObDereferenceObject(ntos_worker_thread);
+       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 --git a/ubuntu/ndiswrapper/ntoskernel.h b/ubuntu/ndiswrapper/ntoskernel.h
new file mode 100644 (file)
index 0000000..46aeb53
--- /dev/null
@@ -0,0 +1,1157 @@
+/*
+ *  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>
+
+#if !defined(CONFIG_X86) && !defined(CONFIG_X86_64)
+#error "this module is for x86 or x86_64 architectures only"
+#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 */
+
+/* pci functions in 2.6 kernels have problems allocating dma buffers,
+ * but seem to work fine with dma functions
+ */
+#include <asm/dma-mapping.h>
+
+#define PCI_DMA_ALLOC_COHERENT(pci_dev,size,dma_handle)                        \
+       dma_alloc_coherent(&pci_dev->dev,size,dma_handle,               \
+                          GFP_KERNEL | __GFP_REPEAT)
+#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 workqueue_struct;
+
+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;
+};
+
+typedef struct workqueue_struct {
+       u8 singlethread;
+       u8 qon;
+       int num_cpus;
+       struct workqueue_thread threads[0];
+} workqueue_struct_t;
+
+typedef struct {
+       struct list_head list;
+       void (*func)(void *data);
+       void *data;
+       /* whether/on which thread scheduled */
+       struct workqueue_thread *thread;
+} work_struct_t;
+
+#define initialize_work(work, pfunc, pdata)                    \
+       do {                                                    \
+               (work)->func = (pfunc);                         \
+               (work)->data = (pdata);                         \
+               (work)->thread = NULL;                          \
+       } while (0)
+
+#undef create_singlethread_workqueue
+#define create_singlethread_workqueue(name) wrap_create_wq(name, 1, 0)
+#undef create_workqueue
+#define create_workqueue(name) wrap_create_wq(name, 0, 0)
+#undef destroy_workqueue
+#define destroy_workqueue wrap_destroy_wq
+#undef queue_work
+#define queue_work wrap_queue_work
+#undef flush_workqueue
+#define flush_workqueue wrap_flush_wq
+
+workqueue_struct_t *wrap_create_wq(const char *name, u8 singlethread, u8 freeze);
+void wrap_destroy_wq_on(workqueue_struct_t *workq, int cpu);
+void wrap_destroy_wq(workqueue_struct_t *workq);
+int wrap_queue_work_on(workqueue_struct_t *workq, work_struct_t *work,
+                      int cpu);
+int wrap_queue_work(workqueue_struct_t *workq, work_struct_t *work);
+void wrap_cancel_work(work_struct_t *work);
+void wrap_flush_wq_on(workqueue_struct_t *workq, int cpu);
+void wrap_flush_wq(workqueue_struct_t *workq);
+typedef void *worker_param_t;
+#define worker_param_data(param, type, member) param
+
+#else // WRAP_WQ
+
+typedef struct workqueue_struct workqueue_struct_t;
+typedef struct work_struct work_struct_t;
+
+#if defined(INIT_WORK_NAR) || defined(INIT_DELAYED_WORK_DEFERRABLE)
+#define initialize_work(work, func, data) INIT_WORK(work, func)
+typedef struct work_struct *worker_param_t;
+#define worker_param_data(param, type, member) \
+       container_of(param, type, member)
+#else
+#define initialize_work(work, func, data) INIT_WORK(work, func, data)
+typedef void *worker_param_t;
+#define worker_param_data(param, type, member) param
+#endif // INIT_WORK_NAR
+
+#endif // WRAP_WQ
+
+struct nt_thread *wrap_worker_init(workqueue_struct_t *wq);
+
+#ifdef module_param
+#define WRAP_MODULE_PARM_INT(name, perm) module_param(name, int, perm)
+#define WRAP_MODULE_PARM_STRING(name, perm) module_param(name, charp, perm)
+#else
+#define WRAP_MODULE_PARM_INT(name, perm) MODULE_PARM(name, "i")
+#define WRAP_MODULE_PARM_STRING(name, perm) MODULE_PARM(name, "s")
+#endif
+
+#ifndef LOCK_PREFIX
+#ifdef LOCK
+#define LOCK_PREFIX LOCK
+#else
+#ifdef CONFIG_SMP
+#define LOCK_PREFIX "lock ; "
+#else
+#define LOCK_PREFIX ""
+#endif
+#endif
+#endif
+
+#ifndef NETDEV_TX_OK
+#define NETDEV_TX_OK 0
+#endif
+
+#ifndef NETDEV_TX_BUSY
+#define NETDEV_TX_BUSY 1
+#endif
+
+#ifndef CHECKSUM_HW
+#define CHECKSUM_HW CHECKSUM_PARTIAL
+#endif
+
+#ifndef offset_in_page
+#define offset_in_page(p) ((unsigned long)(p) & ~PAGE_MASK)
+#endif
+
+#ifndef PMSG_SUSPEND
+#ifdef PM_SUSPEND
+/* this is not correct - the value of PM_SUSPEND is different from
+ * PMSG_SUSPEND, but ndiswrapper doesn't care about the value when
+ * suspending */
+#define PMSG_SUSPEND PM_SUSPEND
+#define PSMG_ON PM_ON
+#else
+typedef u32 pm_message_t;
+#define PMSG_SUSPEND 2
+#define PMSG_ON 0
+#endif
+#endif
+
+#ifndef PCI_D0
+#define PCI_D0 0
+#endif
+
+#ifndef PCI_D3hot
+#define PCI_D3hot 3
+#endif
+
+#ifndef PCI_D3cold
+#define PCI_D3cold 3
+#endif
+
+#ifndef PM_EVENT_SUSPEND
+#define PM_EVENT_SUSPEND 2
+#endif
+
+#if !defined(HAVE_NETDEV_PRIV)
+#define netdev_priv(dev)  ((dev)->priv)
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
+#define ISR_PT_REGS_PARAM_DECL
+#define ISR_PT_REGS_ARG
+#else
+#define ISR_PT_REGS_PARAM_DECL , struct pt_regs *regs
+#define ISR_PT_REGS_ARG , NULL
+#endif
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,16)
+#define for_each_possible_cpu(_cpu) for_each_cpu(_cpu)
+#endif
+
+#ifndef flush_icache_range
+#define flush_icache_range(start, end) do { } while (0)
+#endif
+
+#ifndef CHECKSUM_PARTIAL
+#define CHECKSUM_PARTIAL CHECKSUM_HW
+#endif
+
+#ifndef IRQF_SHARED
+#define IRQF_SHARED SA_SHIRQ
+#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
+
+#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
+
+/* 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 wrap_devices;
+       struct nt_list settings;
+       int dev_type;
+       struct ndis_driver *ndis_driver;
+};
+
+enum hw_status {
+       HW_INITIALIZED = 1, HW_SUSPENDED, HW_HALTED, HW_PRESENT,
+};
+
+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;
+       };
+       union {
+               struct ndis_device *wnd;
+       };
+};
+
+#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 workqueue_struct_t *ntos_wq;
+#define schedule_ntos_work(work_struct) queue_work(ntos_wq, work_struct)
+#define schedule_work(work_struct) queue_work(ntos_wq, work_struct)
+
+extern workqueue_struct_t *ndis_wq;
+#define schedule_ndis_work(work_struct) queue_work(ndis_wq, work_struct)
+
+extern workqueue_struct_t *wrapndis_wq;
+#define schedule_wrapndis_work(work_struct) queue_work(wrapndis_wq, work_struct)
+
+#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)
+
+#ifndef in_atomic
+#define in_atomic() in_interrupt()
+#endif
+
+#ifndef preempt_enable_no_resched
+#define preempt_enable_no_resched() preempt_enable()
+#endif
+
+//#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
+
+typedef struct {
+       int count;
+       struct mutex lock;
+#ifdef CONFIG_SMP
+       typeof(current->cpus_allowed) cpus_allowed;
+#endif
+       struct task_struct *task;
+} irql_info_t;
+
+DECLARE_PER_CPU(irql_info_t, irql_info);
+
+static inline KIRQL raise_irql(KIRQL newirql)
+{
+       irql_info_t *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) && defined(DEBUG)
+               do {
+                       cpumask_t cpumask;
+                       cpumask = cpumask_of_cpu(smp_processor_id());
+                       cpus_xor(cpumask, cpumask, current->cpus_allowed);
+                       assert(cpus_empty(cpumask));
+               } while (0);
+#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());
+       info->cpus_allowed = current->cpus_allowed;
+       current->cpus_allowed = cpumask_of_cpu(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)
+{
+       irql_info_t *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
+               current->cpus_allowed = info->cpus_allowed;
+#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)
+{
+       __asm__ __volatile__(
+               "1:\t"
+               "  xchgl %1, %0\n\t"
+               "  testl %1, %1\n\t"
+               "  jz 3f\n"
+               "2:\t"
+               "  rep; nop\n\t"
+               "  cmpl %2, %0\n\t"
+               "  je 1b\n\t"
+               "  jmp 2b\n"
+               "3:\n\t"
+               : "+m" (*lock)
+               : "r" (NT_SPIN_LOCK_LOCKED), "i" (NT_SPIN_LOCK_UNLOCKED));
+}
+
+static inline void nt_spin_unlock(NT_SPIN_LOCK *lock)
+{
+       *lock = NT_SPIN_LOCK_UNLOCKED;
+}
+
+#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_no_resched();                                    \
+       local_irq_restore(flags);                                       \
+       preempt_check_resched();                                        \
+} 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 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 (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 (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);
+void free_object(void *object);
+
+int usb_init(void);
+void usb_exit(void);
+int usb_init_device(struct wrap_device *wd);
+void usb_exit_device(struct wrap_device *wd);
+void usb_cancel_pending_urbs(void);
+
+int crt_init(void);
+void crt_exit(void);
+int rtl_init(void);
+void rtl_exit(void);
+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)
+
+void WRITE_PORT_UCHAR(ULONG_PTR port, UCHAR value) wstdcall;
+UCHAR READ_PORT_UCHAR(ULONG_PTR port) wstdcall;
+
+#undef ExAllocatePoolWithTag
+void *ExAllocatePoolWithTag(enum pool_type pool_type, SIZE_T size,
+                           ULONG tag) wstdcall;
+#if defined(ALLOC_DEBUG) && ALLOC_DEBUG > 1
+#define ExAllocatePoolWithTag(pool_type, size, tag)                    \
+       wrap_ExAllocatePoolWithTag(pool_type, size, tag, __FILE__, __LINE__)
+#endif
+
+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;
+void KeClearEvent(struct nt_event *nt_event) wstdcall;
+void KeInitializeDpc(struct kdpc *kdpc, void *func, void *ctx) wstdcall;
+BOOLEAN queue_kdpc(struct kdpc *kdpc);
+BOOLEAN dequeue_kdpc(struct kdpc *kdpc);
+
+void KeFlushQueuedDpcs(void) wstdcall;
+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;
+struct mdl *IoAllocateMdl(void *virt, ULONG length, BOOLEAN second_buf,
+                         BOOLEAN charge_quota, struct irp *irp) wstdcall;
+void MmBuildMdlForNonPagedPool(struct mdl *mdl) wstdcall;
+void IoFreeMdl(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;
+void KeInitializeEvent(struct nt_event *nt_event, enum event_type type,
+                      BOOLEAN state) wstdcall;
+struct irp *IoAllocateIrp(char stack_count, BOOLEAN charge_quota) wstdcall;
+void IoFreeIrp(struct irp *irp) 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;
+struct irp *IoBuildAsynchronousFsdRequest
+       (ULONG major_func, struct device_object *dev_obj, void *buf,
+        ULONG length, LARGE_INTEGER *offset,
+        struct io_status_block *status) wstdcall;
+NTSTATUS PoCallDriver(struct device_object *dev_obj, struct irp *irp) 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;
+
+KIRQL KeGetCurrentIrql(void) wstdcall;
+void KeInitializeSpinLock(NT_SPIN_LOCK *lock) wstdcall;
+void KeAcquireSpinLock(NT_SPIN_LOCK *lock, KIRQL *irql) wstdcall;
+void KeReleaseSpinLock(NT_SPIN_LOCK *lock, KIRQL oldirql) wstdcall;
+KIRQL KeAcquireSpinLockRaiseToDpc(NT_SPIN_LOCK *lock) wstdcall;
+
+void IoAcquireCancelSpinLock(KIRQL *irql) wstdcall;
+void IoReleaseCancelSpinLock(KIRQL irql) wstdcall;
+
+void RtlCopyMemory(void *dst, const void *src, SIZE_T length) 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 RtlInitString(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;
+void RtlCopyUnicodeString(struct unicode_string *dst,
+                         struct unicode_string *src) wstdcall;
+NTSTATUS RtlUpcaseUnicodeString(struct unicode_string *dst,
+                               struct unicode_string *src,
+                               BOOLEAN alloc) wstdcall;
+void KeInitializeTimer(struct nt_timer *nt_timer) wstdcall;
+void KeInitializeTimerEx(struct nt_timer *nt_timer,
+                        enum timer_type type) wstdcall;
+BOOLEAN KeSetTimerEx(struct nt_timer *nt_timer, LARGE_INTEGER duetime_ticks,
+                    LONG period_ms, struct kdpc *kdpc) wstdcall;
+BOOLEAN KeSetTimer(struct nt_timer *nt_timer, LARGE_INTEGER duetime_ticks,
+                  struct kdpc *kdpc) wstdcall;
+BOOLEAN KeCancelTimer(struct nt_timer *nt_timer) wstdcall;
+void KeInitializeDpc(struct kdpc *kdpc, void *func, void *ctx) wstdcall;
+struct nt_thread *KeGetCurrentThread(void) wstdcall;
+NTSTATUS ObReferenceObjectByHandle(void *handle, ACCESS_MASK desired_access,
+                                  void *obj_type, KPROCESSOR_MODE access_mode,
+                                  void **object, void *handle_info) wstdcall;
+
+void adjust_user_shared_data_addr(char *driver, unsigned long length);
+
+extern spinlock_t ntoskernel_lock;
+extern spinlock_t irp_cancel_lock;
+extern struct nt_list object_list;
+#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 --git a/ubuntu/ndiswrapper/ntoskernel_io.c b/ubuntu/ndiswrapper/ntoskernel_io.c
new file mode 100644 (file)
index 0000000..7af0eb1
--- /dev/null
@@ -0,0 +1,1106 @@
+/*
+ *  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 io_stack_location *irp_sl;
+               irp_sl = IoGetCurrentIrpStackLocation(irp);
+               IOTRACE("%p, %p", irp_sl, irp_sl->dev_obj);
+               /* cancel_routine will release the spin lock */
+               __release(irp->cancel_irql);
+               LIN2WIN2(cancel_routine, irp_sl->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->flags & IRP_SYNCHRONOUS_API)
+               IoDequeueThreadIrp(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,
+                       "ndiswrapper", 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,
+        void *file_obj, struct device_object *dev_obj)
+{
+       struct common_object_header *coh;
+
+       dev_obj = NULL;
+       /* TODO: access is not checked and file_obj is set to NULL */
+       file_obj = NULL;
+       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)
+               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();
+}
diff --git a/ubuntu/ndiswrapper/pe_linker.c b/ubuntu/ndiswrapper/pe_linker.c
new file mode 100644 (file)
index 0000000..46badd1
--- /dev/null
@@ -0,0 +1,609 @@
+/*
+ *  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 char *get_export(char *name)
+{
+       int i;
+
+       for (i = 0 ; ntoskernel_exports[i].name != NULL; i++)
+               if (strcmp(ntoskernel_exports[i].name, name) == 0)
+                       return (char *)ntoskernel_exports[i].func;
+
+       for (i = 0 ; ntoskernel_io_exports[i].name != NULL; i++)
+               if (strcmp(ntoskernel_io_exports[i].name, name) == 0)
+                       return (char *)ntoskernel_io_exports[i].func;
+
+       for (i = 0 ; ndis_exports[i].name != NULL; i++)
+               if (strcmp(ndis_exports[i].name, name) == 0)
+                       return (char *)ndis_exports[i].func;
+
+       for (i = 0 ; crt_exports[i].name != NULL; i++)
+               if (strcmp(crt_exports[i].name, name) == 0)
+                       return (char *)crt_exports[i].func;
+
+       for (i = 0 ; hal_exports[i].name != NULL; i++)
+               if (strcmp(hal_exports[i].name, name) == 0)
+                       return (char *)hal_exports[i].func;
+
+       for (i = 0 ; rtl_exports[i].name != NULL; i++)
+               if (strcmp(rtl_exports[i].name, name) == 0)
+                       return (char *)rtl_exports[i].func;
+
+#ifdef ENABLE_USB
+       for (i = 0 ; usb_exports[i].name != NULL; i++)
+               if (strcmp(usb_exports[i].name, name) == 0)
+                       return (char *)usb_exports[i].func;
+#endif
+
+       for (i = 0; i < num_pe_exports; i++)
+               if (strcmp(pe_exports[i].name, name) == 0)
+                       return (char *)pe_exports[i].addr;
+
+       return NULL;
+}
+#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: secion: 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;
+       void *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: %Lu",
+                             (uint64_t)lookup_tbl[i]);
+                       return -1;
+               }
+               else {
+                       symname = RVA2VA(image,
+                                        ((lookup_tbl[i] &
+                                          ~IMAGE_ORDINAL_FLAG) + 2), char *);
+               }
+
+               adr = get_export(symname);
+               if (adr == NULL) {
+                       ERROR("unknown symbol: %s:'%s'", dll, symname);
+                       ret = -1;
+               } else {
+                       DBGLINKER("found symbol: %s:%s: addr: %p, rva = %Lu",
+                                 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 %Lu 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 memroy 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 fullfill the sections alignements. 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,
+                         PAGE_KERNEL_EXECUTABLE);
+#elif defined PAGE_KERNEL_EXEC
+       image = __vmalloc(image_size, GFP_KERNEL | __GFP_HIGHMEM,
+                         PAGE_KERNEL_EXEC);
+#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,
+                                 __pgprot(__PAGE_KERNEL & ~_PAGE_NX));
+       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;
+       }
+
+       /* 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;
+
+       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;
+
+#ifdef DEBUG
+       /* Sanity checkings */
+       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)
+               INFO("fixing KI_USER_SHARED_DATA address in the driver");
+               fix_user_shared_data_addr(pe_image[i].image, pe_image[i].size);
+#endif
+               flush_icache_range(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 --git a/ubuntu/ndiswrapper/pe_linker.h b/ubuntu/ndiswrapper/pe_linker.h
new file mode 100644 (file)
index 0000000..d582cdb
--- /dev/null
@@ -0,0 +1,993 @@
+/*
+ * 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 --git a/ubuntu/ndiswrapper/pnp.c b/ubuntu/ndiswrapper/pnp.c
new file mode 100644 (file)
index 0000000..252e9c7
--- /dev/null
@@ -0,0 +1,742 @@
+/*
+ *  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)) {
+#ifdef ENABLE_USB
+               if (usb_init_device(wd)) {
+                       ntoskernel_exit_device(wd);
+                       EXIT1(return STATUS_FAILURE);
+               }
+#endif
+               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)) {
+#ifdef ENABLE_USB
+               usb_exit_device(wd);
+#endif
+       }
+       if (wd->resource_list)
+               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)
+{
+       struct io_stack_location *irp_sl;
+       NTSTATUS status;
+
+       DUMP_IRP(irp);
+       irp_sl = IoGetCurrentIrpStackLocation(irp);
+#ifdef ENABLE_USB
+       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);
+#endif
+       IOEXIT(return status);
+}
+WIN_FUNC_DECL(pdoDispatchDeviceControl,2)
+
+wstdcall NTSTATUS pdoDispatchPnp(struct device_object *pdo, struct irp *irp)
+{
+       struct io_stack_location *irp_sl;
+       struct wrap_device *wd;
+       NTSTATUS status;
+#ifdef ENABLE_USB
+       struct usbd_bus_interface_usbdi *usb_intf;
+#endif
+
+       irp_sl = IoGetCurrentIrpStackLocation(irp);
+       TRACE2("%p %d:%d", pdo, irp_sl->major_fn, irp_sl->minor_fn);
+       wd = pdo->reserved;
+       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:
+#ifdef ENABLE_USB
+               if (!wrap_is_usb_bus(wd->dev_bus)) {
+                       status = STATUS_NOT_IMPLEMENTED;
+                       break;
+               }
+               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);
+               usb_intf = (struct usbd_bus_interface_usbdi *)
+                       irp_sl->params.query_intf.intf;
+               usb_intf->Context = wd;
+               usb_intf->InterfaceReference = USBD_InterfaceReference;
+               usb_intf->InterfaceDereference = USBD_InterfaceDereference;
+               usb_intf->GetUSBDIVersion = USBD_InterfaceGetUSBDIVersion;
+               usb_intf->QueryBusTime = USBD_InterfaceQueryBusTime;
+               usb_intf->SubmitIsoOutUrb = USBD_InterfaceSubmitIsoOutUrb;
+               usb_intf->QueryBusInformation =
+                       USBD_InterfaceQueryBusInformation;
+               if (irp_sl->params.query_intf.version >=
+                   USB_BUSIF_USBDI_VERSION_1)
+                       usb_intf->IsDeviceHighSpeed =
+                               USBD_InterfaceIsDeviceHighSpeed;
+               if (irp_sl->params.query_intf.version >=
+                   USB_BUSIF_USBDI_VERSION_2)
+                       usb_intf->LogEntry = USBD_InterfaceLogEntry;
+               status = STATUS_SUCCESS;
+#else
+               status = STATUS_NOT_IMPLEMENTED;
+#endif
+               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 { // usb device
+#ifdef ENABLE_USB
+                               wrap_resume_urbs(wd);
+#endif
+                       }
+               } 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 { // usb device
+#ifdef ENABLE_USB
+                               wrap_suspend_urbs(wd);
+#endif
+                       }
+               }
+               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);
+}
+
+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);
+       if (status == STATUS_SUCCESS)
+               fdo->drv_obj->drv_ext->count++;
+       else
+               WARNING("Windows driver couldn't initialize the device (%08X)",
+                       status);
+       EXIT1(return status);
+}
+
+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);
+}
+
+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 (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) {
+                       if (down_interruptible(&loader_mutex))
+                               WARNING("couldn't obtain loader_mutex");
+                       unload_wrap_driver(wrap_driver);
+                       up(&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;
+}
+
+/*
+ * This function should not be marked __devinit because PCI IDs are
+ * added dynamically.
+ */
+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 __devexit 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;
+       struct device_object *pdo;
+
+       wd = usb_get_intfdata(intf);
+       ENTER1("%p, %p", intf, wd);
+       if (!wd)
+               EXIT1(return 0);
+       pdo = wd->pdo;
+       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 --git a/ubuntu/ndiswrapper/pnp.h b/ubuntu/ndiswrapper/pnp.h
new file mode 100644 (file)
index 0000000..5803770
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *  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"
+
+NTSTATUS pnp_start_device(struct wrap_device *wd);
+NTSTATUS pnp_stop_device(struct wrap_device *wd);
+NTSTATUS pnp_remove_device(struct wrap_device *wd);
+
+int wrap_pnp_start_pci_device(struct pci_dev *pdev,
+                             const struct pci_device_id *ent);
+void __devexit 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);
+
+#ifdef ENABLE_USB
+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
+
+#endif
diff --git a/ubuntu/ndiswrapper/proc.c b/ubuntu/ndiswrapper/proc.c
new file mode 100644 (file)
index 0000000..bbcefca
--- /dev/null
@@ -0,0 +1,565 @@
+/*
+ *  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/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
+
+static struct proc_dir_entry *wrap_procfs_entry;
+
+static int procfs_read_ndis_stats(char *page, char **start, off_t off,
+                                 int count, int *eof, void *data)
+{
+       char *p = page;
+       struct ndis_device *wnd = (struct ndis_device *)data;
+       struct ndis_wireless_stats stats;
+       NDIS_STATUS res;
+       ndis_rssi rssi;
+
+       if (off != 0) {
+               *eof = 1;
+               return 0;
+       }
+
+       res = mp_query(wnd, OID_802_11_RSSI, &rssi, sizeof(rssi));
+       if (!res)
+               p += sprintf(p, "signal_level=%d dBm\n", (s32)rssi);
+
+       res = mp_query(wnd, OID_802_11_STATISTICS, &stats, sizeof(stats));
+       if (!res) {
+
+               p += sprintf(p, "tx_frames=%Lu\n", stats.tx_frag);
+               p += sprintf(p, "tx_multicast_frames=%Lu\n",
+                            stats.tx_multi_frag);
+               p += sprintf(p, "tx_failed=%Lu\n", stats.failed);
+               p += sprintf(p, "tx_retry=%Lu\n", stats.retry);
+               p += sprintf(p, "tx_multi_rerty=%Lu\n", stats.multi_retry);
+               p += sprintf(p, "tx_rtss_success=%Lu\n", stats.rtss_succ);
+               p += sprintf(p, "tx_rtss_fail=%Lu\n", stats.rtss_fail);
+               p += sprintf(p, "ack_fail=%Lu\n", stats.ack_fail);
+               p += sprintf(p, "frame_duplicates=%Lu\n", stats.frame_dup);
+               p += sprintf(p, "rx_frames=%Lu\n", stats.rx_frag);
+               p += sprintf(p, "rx_multicast_frames=%Lu\n",
+                            stats.rx_multi_frag);
+               p += sprintf(p, "fcs_errors=%Lu\n", stats.fcs_err);
+       }
+
+       if (p - page > count) {
+               ERROR("wrote %lu bytes (limit is %u)\n",
+                     (unsigned long)(p - page), count);
+               *eof = 1;
+       }
+
+       return p - page;
+}
+
+static int procfs_read_ndis_encr(char *page, char **start, off_t off,
+                                int count, int *eof, void *data)
+{
+       char *p = page;
+       struct ndis_device *wnd = (struct ndis_device *)data;
+       int i, encr_status, auth_mode, infra_mode;
+       NDIS_STATUS res;
+       struct ndis_essid essid;
+       mac_address ap_address;
+
+       if (off != 0) {
+               *eof = 1;
+               return 0;
+       }
+
+       res = mp_query(wnd, OID_802_11_BSSID,
+                      &ap_address, sizeof(ap_address));
+       if (res)
+               memset(ap_address, 0, ETH_ALEN);
+       p += sprintf(p, "ap_address=%2.2X", ap_address[0]);
+       for (i = 1 ; i < ETH_ALEN ; i++)
+               p += sprintf(p, ":%2.2X", ap_address[i]);
+       p += sprintf(p, "\n");
+
+       res = mp_query(wnd, OID_802_11_SSID, &essid, sizeof(essid));
+       if (!res)
+               p += sprintf(p, "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;
+               p += sprintf(p, "tx_key=%u\n", wnd->encr_info.tx_key_index);
+               p += sprintf(p, "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++)
+                               p += sprintf(p, "%2.2X", tx_key->key[i]);
+               else
+                       p += sprintf(p, "off");
+               p += sprintf(p, "\n");
+               p += sprintf(p, "encr_mode=%d\n", encr_status);
+       }
+       res = mp_query_int(wnd, OID_802_11_AUTHENTICATION_MODE, &auth_mode);
+       if (!res)
+               p += sprintf(p, "auth_mode=%d\n", auth_mode);
+       res = mp_query_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, &infra_mode);
+       p += sprintf(p, "mode=%s\n", (infra_mode == Ndis802_11IBSS) ?
+                    "adhoc" : (infra_mode == Ndis802_11Infrastructure) ?
+                    "managed" : "auto");
+       if (p - page > count) {
+               WARNING("wrote %lu bytes (limit is %u)",
+                       (unsigned long)(p - page), count);
+               *eof = 1;
+       }
+
+       return p - page;
+}
+
+static int procfs_read_ndis_hw(char *page, char **start, off_t off,
+                              int count, int *eof, void *data)
+{
+       char *p = page;
+       struct ndis_device *wnd = (struct ndis_device *)data;
+       struct ndis_configuration config;
+       unsigned int 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"};
+
+       if (off != 0) {
+               *eof = 1;
+               return 0;
+       }
+
+       res = mp_query_int(wnd, OID_GEN_HARDWARE_STATUS, &n);
+       if (res == NDIS_STATUS_SUCCESS &&
+           n >= 0 && n < sizeof(hw_status) / sizeof(hw_status[0]))
+               p += sprintf(p, "status=%s\n", hw_status[n]);
+
+       res = mp_query(wnd, OID_802_3_CURRENT_ADDRESS, mac, sizeof(mac));
+       if (!res)
+               p += sprintf(p, "mac: " MACSTRSEP "\n", MAC2STR(mac));
+       res = mp_query(wnd, OID_802_11_CONFIGURATION, &config, sizeof(config));
+       if (!res) {
+               p += sprintf(p, "beacon_period=%u msec\n",
+                            config.beacon_period);
+               p += sprintf(p, "atim_window=%u msec\n", config.atim_window);
+               p += sprintf(p, "frequency=%u kHZ\n", config.ds_config);
+               p += sprintf(p, "hop_pattern=%u\n",
+                            config.fh_config.hop_pattern);
+               p += sprintf(p, "hop_set=%u\n",
+                            config.fh_config.hop_set);
+               p += sprintf(p, "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)
+               p += sprintf(p, "tx_power=%u mW\n", tx_power);
+
+       res = mp_query(wnd, OID_GEN_LINK_SPEED, &bit_rate, sizeof(bit_rate));
+       if (!res)
+               p += sprintf(p, "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)
+               p += sprintf(p, "rts_threshold=%u bytes\n", rts_threshold);
+
+       res = mp_query(wnd, OID_802_11_FRAGMENTATION_THRESHOLD,
+                      &frag_threshold, sizeof(frag_threshold));
+       if (!res)
+               p += sprintf(p, "frag_threshold=%u bytes\n", frag_threshold);
+
+       res = mp_query_int(wnd, OID_802_11_POWER_MODE, &power_mode);
+       if (!res)
+               p += sprintf(p, "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)
+               p += sprintf(p, "num_antennas=%u\n", antenna);
+
+       res = mp_query(wnd, OID_802_11_TX_ANTENNA_SELECTED,
+                      &antenna, sizeof(antenna));
+       if (!res)
+               p += sprintf(p, "tx_antenna=%u\n", antenna);
+
+       res = mp_query(wnd, OID_802_11_RX_ANTENNA_SELECTED,
+                      &antenna, sizeof(antenna));
+       if (!res)
+               p += sprintf(p, "rx_antenna=%u\n", antenna);
+
+       p += sprintf(p, "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);
+               p += sprintf(p, "packet_filter: 0x%08x\n", packet_filter);
+       }
+       if (p - page > count) {
+               WARNING("wrote %lu bytes (limit is %u)",
+                       (unsigned long)(p - page), count);
+               *eof = 1;
+       }
+
+       return p - page;
+}
+
+static int procfs_read_ndis_settings(char *page, char **start, off_t off,
+                                    int count, int *eof, void *data)
+{
+       char *p = page;
+       struct ndis_device *wnd = (struct ndis_device *)data;
+       struct wrap_device_setting *setting;
+
+       if (off != 0) {
+               *eof = 1;
+               return 0;
+       }
+
+       p += sprintf(p, "hangcheck_interval=%d\n",
+                    hangcheck_interval == 0 ?
+                    (int)(wnd->hangcheck_interval / HZ) : -1);
+
+       list_for_each_entry(setting, &wnd->wd->settings, list) {
+               p += sprintf(p, "%s=%s\n", setting->name, setting->value);
+       }
+
+       list_for_each_entry(setting, &wnd->wd->driver->settings, list) {
+               p += sprintf(p, "%s=%s\n", setting->name, setting->value);
+       }
+
+       return p - page;
+}
+
+static int procfs_write_ndis_settings(struct file *file, const char __user *buf,
+                                     unsigned long count, void *data)
+{
+       struct ndis_device *wnd = (struct ndis_device *)data;
+       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;
+               if (wrap_is_pci_bus(wnd->wd->dev_bus))
+                       i = wrap_pnp_suspend_pci_device(wnd->wd->pci.pdev,
+                                                       PMSG_SUSPEND);
+               else
+#ifdef ENABLE_USB
+                       i = wrap_pnp_suspend_usb_device(wnd->wd->usb.intf,
+                                                       PMSG_SUSPEND);
+#else
+               i = -1;
+#endif
+               if (i)
+                       return -EINVAL;
+       } else if (!strcmp(setting, "resume")) {
+               if (wrap_is_pci_bus(wnd->wd->dev_bus))
+                       i = wrap_pnp_resume_pci_device(wnd->wd->pci.pdev);
+               else
+#ifdef ENABLE_USB
+                       i = wrap_pnp_resume_usb_device(wnd->wd->usb.intf);
+#else
+               i = -1;
+#endif
+               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;
+}
+
+int wrap_procfs_add_ndis_device(struct ndis_device *wnd)
+{
+       struct proc_dir_entry *procfs_entry;
+
+       if (wrap_procfs_entry == NULL)
+               return -ENOMEM;
+
+       if (wnd->procfs_iface) {
+               ERROR("%s already registered?", wnd->netdev_name);
+               return -EINVAL;
+       }
+       wnd->procfs_iface = proc_mkdir(wnd->netdev_name, wrap_procfs_entry);
+       if (wnd->procfs_iface == NULL) {
+               ERROR("couldn't create proc directory");
+               return -ENOMEM;
+       }
+       wnd->procfs_iface->uid = proc_uid;
+       wnd->procfs_iface->gid = proc_gid;
+
+       procfs_entry = create_proc_entry("hw", S_IFREG | S_IRUSR | S_IRGRP,
+                                        wnd->procfs_iface);
+       if (procfs_entry == NULL) {
+               ERROR("couldn't create proc entry for 'hw'");
+               goto err_hw;
+       } else {
+               procfs_entry->uid = proc_uid;
+               procfs_entry->gid = proc_gid;
+               procfs_entry->data = wnd;
+               procfs_entry->read_proc = procfs_read_ndis_hw;
+       }
+
+       procfs_entry = create_proc_entry("stats", S_IFREG | S_IRUSR | S_IRGRP,
+                                        wnd->procfs_iface);
+       if (procfs_entry == NULL) {
+               ERROR("couldn't create proc entry for 'stats'");
+               goto err_stats;
+       } else {
+               procfs_entry->uid = proc_uid;
+               procfs_entry->gid = proc_gid;
+               procfs_entry->data = wnd;
+               procfs_entry->read_proc = procfs_read_ndis_stats;
+       }
+
+       procfs_entry = create_proc_entry("encr", S_IFREG | S_IRUSR | S_IRGRP,
+                                        wnd->procfs_iface);
+       if (procfs_entry == NULL) {
+               ERROR("couldn't create proc entry for 'encr'");
+               goto err_encr;
+       } else {
+               procfs_entry->uid = proc_uid;
+               procfs_entry->gid = proc_gid;
+               procfs_entry->data = wnd;
+               procfs_entry->read_proc = procfs_read_ndis_encr;
+       }
+
+       procfs_entry = create_proc_entry("settings", S_IFREG |
+                                        S_IRUSR | S_IRGRP |
+                                        S_IWUSR | S_IWGRP, wnd->procfs_iface);
+       if (procfs_entry == NULL) {
+               ERROR("couldn't create proc entry for 'settings'");
+               goto err_settings;
+       } else {
+               procfs_entry->uid = proc_uid;
+               procfs_entry->gid = proc_gid;
+               procfs_entry->data = wnd;
+               procfs_entry->read_proc = procfs_read_ndis_settings;
+               procfs_entry->write_proc = procfs_write_ndis_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:
+       remove_proc_entry(wnd->netdev_name, wrap_procfs_entry);
+       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)
+               remove_proc_entry(wnd->netdev_name, wrap_procfs_entry);
+}
+
+static int procfs_read_debug(char *page, char **start, off_t off,
+                            int count, int *eof, void *data)
+{
+       char *p = page;
+       enum alloc_type type;
+
+       if (off != 0) {
+               *eof = 1;
+               return 0;
+       }
+       p += sprintf(p, "%d\n", debug);
+       type = 0;
+#ifdef ALLOC_DEBUG
+       for (type = 0; type < ALLOC_TYPE_MAX; type++)
+               p += sprintf(p, "total size of allocations in %d: %d\n",
+                            type, alloc_size(type));
+#endif
+       return p - page;
+}
+
+static int procfs_write_debug(struct file *file, const char __user *buf,
+                             unsigned long count, void *data)
+{
+       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;
+}
+
+int wrap_procfs_init(void)
+{
+       struct proc_dir_entry *procfs_entry;
+
+       wrap_procfs_entry = proc_mkdir(DRIVER_NAME, proc_net_root);
+       if (wrap_procfs_entry == NULL) {
+               ERROR("couldn't create procfs directory");
+               return -ENOMEM;
+       }
+       wrap_procfs_entry->uid = proc_uid;
+       wrap_procfs_entry->gid = proc_gid;
+
+       procfs_entry = create_proc_entry("debug", S_IFREG | S_IRUSR | S_IRGRP,
+                                        wrap_procfs_entry);
+       if (procfs_entry == NULL) {
+               ERROR("couldn't create proc entry for 'debug'");
+               return -ENOMEM;
+       } else {
+               procfs_entry->uid = proc_uid;
+               procfs_entry->gid = proc_gid;
+               procfs_entry->read_proc  = procfs_read_debug;
+               procfs_entry->write_proc = procfs_write_debug;
+       }
+       return 0;
+}
+
+void wrap_procfs_remove(void)
+{
+       if (wrap_procfs_entry == NULL)
+               return;
+       remove_proc_entry("debug", wrap_procfs_entry);
+       remove_proc_entry(DRIVER_NAME, proc_net_root);
+}
diff --git a/ubuntu/ndiswrapper/rtl.c b/ubuntu/ndiswrapper/rtl.c
new file mode 100644 (file)
index 0000000..918e73e
--- /dev/null
@@ -0,0 +1,716 @@
+/*
+ *  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 "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;
+
+       x = (char *)a;
+       y = (char *)b;
+       /* MSDN says this should return number of bytes that compare as
+        * equal. This can be interpretted 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
+        * upto when they compare as not equal.
+        * 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();
+}
+
+int rtl_init(void)
+{
+       return 0;
+}
+
+/* called when module is being removed */
+void rtl_exit(void)
+{
+       EXIT4(return);
+}
diff --git a/ubuntu/ndiswrapper/usb.c b/ubuntu/ndiswrapper/usb.c
new file mode 100644 (file)
index 0000000..81a17b3
--- /dev/null
@@ -0,0 +1,1457 @@
+/*
+ *  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 int inline 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 work_struct_t wrap_urb_complete_work;
+static void wrap_urb_complete_worker(worker_param_t 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 irp *irp;
+       struct wrap_urb *wrap_urb;
+
+       USBTRACE("freeing urb: %p", urb);
+       wrap_urb = urb->context;
+       irp = wrap_urb->irp;
+       if (wrap_urb->flags & WRAP_URB_COPY_BUFFER) {
+               USBTRACE("freeing DMA buffer for URB: %p %p",
+                        urb, urb->transfer_buffer);
+               usb_buffer_free(IRP_WRAP_DEVICE(irp)->usb.udev,
+                               urb->transfer_buffer_length,
+                               urb->transfer_buffer, urb->transfer_dma);
+       }
+       if (urb->setup_packet)
+               kfree(urb->setup_packet);
+       if (IRP_WRAP_DEVICE(irp)->usb.num_alloc_urbs > MAX_ALLOCATED_URBS) {
+               IoAcquireCancelSpinLock(&irp->cancel_irql);
+               RemoveEntryList(&wrap_urb->list);
+               IRP_WRAP_DEVICE(irp)->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;
+
+       /* NB: this function is called holding Cancel spinlock */
+       USBENTER("irp: %p", irp);
+       urb = IRP_WRAP_URB(irp)->urb;
+       USBTRACE("canceling urb %p", urb);
+       if (wrap_cancel_urb(IRP_WRAP_URB(irp))) {
+               irp->cancel = FALSE;
+               ERROR("urb %p can't be canceld: %d", urb,
+                     IRP_WRAP_URB(irp)->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);
+       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;
+                       usb_init_urb(urb);
+                       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_buffer_alloc(wd->usb.udev, buf_len, alloc_flags,
+                                        &urb->transfer_dma);
+               if (!urb->transfer_buffer) {
+                       WARNING("couldn't allocate dma buf");
+                       IoAcquireCancelSpinLock(&irp->cancel_irql);
+                       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 urb *urb;
+       union nt_urb *nt_urb;
+
+       urb = IRP_WRAP_URB(irp)->urb;
+       nt_urb = IRP_URB(irp);
+#ifdef USB_DEBUG
+       if (IRP_WRAP_URB(irp)->state != URB_ALLOCATED) {
+               ERROR("urb %p is in wrong state: %d",
+                     urb, IRP_WRAP_URB(irp)->state);
+               NT_URB_STATUS(nt_urb) = USBD_STATUS_REQUEST_FAILED;
+               return NT_URB_STATUS(nt_urb);
+       }
+       IRP_WRAP_URB(irp)->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);
+       IRP_WRAP_URB(irp)->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);
+       schedule_ntos_work(&wrap_urb_complete_work);
+}
+
+/* one worker for all devices */
+static void wrap_urb_complete_worker(worker_param_t 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:
+                       /* succesfully 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)
+{
+       usbd_pipe_handle pipe_handle;
+       struct urb *urb;
+       unsigned int pipe;
+       struct usbd_bulk_or_intr_transfer *bulk_int_tx;
+       USBD_STATUS status;
+       struct usb_device *udev;
+       union nt_urb *nt_urb;
+
+       nt_urb = IRP_URB(irp);
+       udev = IRP_WRAP_DEVICE(irp)->usb.udev;
+       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;
+       struct usb_device *udev;
+       union nt_urb *nt_urb;
+       USBD_STATUS status;
+       struct urb *urb;
+       struct usb_ctrlrequest *dr;
+
+       nt_urb = IRP_URB(irp);
+       udev = IRP_WRAP_DEVICE(irp)->usb.udev;
+       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), GFP_ATOMIC);
+       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;
+       usbd_pipe_handle 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;
+       usbd_pipe_handle 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));
+       ret = usb_get_status(udev, type, status_req->index,
+                            status_req->transfer_buffer);
+       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 interpretted */
+                       /* 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 langauges 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)
+{
+       union nt_urb *nt_urb;
+       struct usb_device *udev;
+       USBD_STATUS status;
+       struct wrap_device *wd;
+
+       wd = IRP_WRAP_DEVICE(irp);
+       udev = wd->usb.udev;
+       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);
+
+       DUMP_IRP(irp);
+       switch (nt_urb->header.function) {
+               /* bulk/int and vendor/class urbs are submitted to
+                * Linux USB core; if the call is sucessful, 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)
+{
+       no_warn_unused 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;
+               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 usb_common_descriptor_t *WIN_FUNC(USBD_ParseDescriptors,4)
+       (void *buf, ULONG length, void *start, LONG type)
+{
+       usb_common_descriptor_t *descr = start;
+
+       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)
+{
+       struct wrap_device *wd = context;
+       struct usb_bus_information_level *bus_info;
+       struct usb_bus *bus;
+
+       bus = wd->usb.udev->bus;
+       bus_info = buf;
+       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);
+}
+
+int usb_init(void)
+{
+       InitializeListHead(&wrap_urb_complete_list);
+       spin_lock_init(&wrap_urb_complete_list_lock);
+       initialize_work(&wrap_urb_complete_work, wrap_urb_complete_worker, NULL);
+#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 --git a/ubuntu/ndiswrapper/usb.h b/ubuntu/ndiswrapper/usb.h
new file mode 100644 (file)
index 0000000..fedfdb8
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ *  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];
+};
+
+typedef struct usb_endpoint_descriptor *usbd_pipe_handle;
+typedef struct usb_descriptor_header usb_common_descriptor_t;
+
+struct usbd_pipe_information {
+       USHORT wMaxPacketSize;
+       UCHAR bEndpointAddress;
+       UCHAR bInterval;
+       enum pipe_type type;
+       usbd_pipe_handle 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;
+       usbd_pipe_handle 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;
+       usbd_pipe_handle 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;
+       usbd_pipe_handle 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);
+
+void USBD_InterfaceGetUSBDIVersion(void *context,
+                                  struct usbd_version_info *version_info,
+                                  ULONG *hcd_capa) wstdcall;
+BOOLEAN USBD_InterfaceIsDeviceHighSpeed(void *context) wstdcall;
+void USBD_InterfaceReference(void *context) wstdcall;
+void USBD_InterfaceDereference(void *context) wstdcall;
+NTSTATUS USBD_InterfaceQueryBusTime(void *context, ULONG *frame) wstdcall;
+NTSTATUS USBD_InterfaceSubmitIsoOutUrb(void *context,
+                                      union nt_urb *nt_urb) wstdcall;
+NTSTATUS USBD_InterfaceQueryBusInformation(void *context, ULONG level, void *buf,
+                                          ULONG *buf_length,
+                                          ULONG *buf_actual_length) wstdcall;
+NTSTATUS USBD_InterfaceLogEntry(void *context, ULONG driver_tag, ULONG enum_tag,
+                               ULONG p1, ULONG p2) wstdcall;
+
+#endif /* USB_H */
diff --git a/ubuntu/ndiswrapper/win2lin_stubs.S b/ubuntu/ndiswrapper/win2lin_stubs.S
new file mode 100644 (file)
index 0000000..1ccfd2d
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ *  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>
+
+#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 variabes (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
+
+*/
+
+#
+# setup for Windows to Linux function call
+#
+
+       .text
+
+.macro win2lin_prolog
+       push    %rsi
+       push    %rdi
+.endm
+
+.macro win2lin_epilog
+       pop     %rdi
+       pop     %rsi
+.endm
+
+# when Windows function calls Linux function, the function address is in %r10
+
+.macro call_lin_func
+       xor     %rax, %rax      # rax indicates number of SSE regs
+       call    *%r10
+.endm
+
+# before prolog, 0(%rsp) is return address, 8(%rsp) would be arg1
+# (but it is in register) and so on, so n'th arg would be at n*8(%rsp)
+# for n > 4. But in prolog, we push 2 registers that are non-volaile in
+# Windows, but volatile in Linux. So after prolog, args are at (n+2)*8(%rsp)
+
+#define win2lin_win_arg(n) (n+2)*8(%rsp)
+
+#define win2lin_arg1 mov %rcx, %rdi
+#define win2lin_arg2 mov %rdx, %rsi
+#define win2lin_arg3 mov %r8, %rdx
+#define win2lin_arg4 mov %r9, %rcx
+#define win2lin_arg5 mov win2lin_win_arg(5), %r8
+#define win2lin_arg6 mov win2lin_win_arg(6), %r9
+
+       .type   win2lin0, @function
+win2lin0:
+       win2lin_prolog
+       call_lin_func
+       win2lin_epilog
+       ret
+       .size   win2lin0, .-win2lin0
+
+       .type   win2lin1, @function
+win2lin1:
+       win2lin_prolog
+       win2lin_arg1
+       call_lin_func
+       win2lin_epilog
+       ret
+       .size   win2lin1, .-win2lin1
+
+       .type   win2lin2, @function
+win2lin2:
+       win2lin_prolog
+       win2lin_arg1
+       win2lin_arg2
+       call_lin_func
+       win2lin_epilog
+       ret
+       .size   win2lin2, .-win2lin2
+
+       .type   win2lin3, @function
+win2lin3:
+       win2lin_prolog
+       win2lin_arg1
+       win2lin_arg2
+       win2lin_arg3
+       call_lin_func
+       win2lin_epilog
+       ret
+       .size   win2lin3, .-win2lin3
+
+       .type   win2lin4, @function
+win2lin4:
+       win2lin_prolog
+       win2lin_arg1
+       win2lin_arg2
+       win2lin_arg3
+       win2lin_arg4
+       call_lin_func
+       win2lin_epilog
+       ret
+       .size   win2lin4, .-win2lin4
+
+       .type   win2lin5, @function
+win2lin5:
+       win2lin_prolog
+       win2lin_arg1
+       win2lin_arg2
+       win2lin_arg3
+       win2lin_arg4
+       win2lin_arg5
+       call_lin_func
+       win2lin_epilog
+       ret
+       .size   win2lin5, .-win2lin5
+
+       .type   win2lin6, @function
+win2lin6:
+       win2lin_prolog
+       win2lin_arg1
+       win2lin_arg2
+       win2lin_arg3
+       win2lin_arg4
+       win2lin_arg5
+       win2lin_arg6
+       call_lin_func
+       win2lin_epilog
+       ret
+       .size   win2lin6, .-win2lin6
+
+# Allocate stack frame for Linux arguments before calling function.
+# First 6 args are passed through registers, so we need space for 7 and above.
+# The arguments should have been copied onto stack already.
+
+.macro call_lin_func_args n
+       sub $(\n-6)*8, %rsp
+       call_lin_func
+       add $(\n-6)*8, %rsp
+       .endm
+
+# m is index of Linux arg required, n is total number of args to function
+# After stack frame is allocated, Linux arg 7 should be at 0(%rsp),
+# arg 8 should be at 1*8(%rsp) and so on. So Linux arg m should be at (m-7)*8
+# Stack frame starts at -(n-6)*8(%rsp), so before stack frame is allocated
+# Linux arg m should be at (6-n+m-7)*8(%rsp)
+
+#define win2lin_lin_arg(m,n) (m-1-n)*8(%rsp)
+
+       .type   win2lin7, @function
+win2lin7:
+       win2lin_prolog
+
+       win2lin_arg1
+       win2lin_arg2
+       win2lin_arg3
+       win2lin_arg4
+       win2lin_arg5
+       win2lin_arg6
+
+       # copy windows argument 7 onto stack for Linux function
+       mov     win2lin_win_arg(7), %r11
+       mov     %r11, win2lin_lin_arg(7,7)
+
+       call_lin_func_args(7)
+       win2lin_epilog
+       ret
+       .size   win2lin7, .-win2lin7
+
+       .type   win2lin8, @function
+win2lin8:
+       win2lin_prolog
+
+       win2lin_arg1
+       win2lin_arg2
+       win2lin_arg3
+       win2lin_arg4
+       win2lin_arg5
+       win2lin_arg6
+
+       # copy windows arguments 7 and 8 onto stack for Linux function
+       mov     win2lin_win_arg(7), %r11
+       mov     %r11, win2lin_lin_arg(7,8)
+       mov     win2lin_win_arg(8), %r11
+       mov     %r11, win2lin_lin_arg(8,8)
+
+       call_lin_func_args(8)
+       win2lin_epilog
+       ret
+       .size   win2lin8, .-win2lin8
+
+       .type   win2lin9, @function
+win2lin9:
+win2lin10:
+win2lin11:
+win2lin12:
+       win2lin_prolog
+
+       # since we destroy rsi and rdi here, first copy windows
+       # arguments 7 through 12 onto stack for Linux function
+       mov     %rcx, %r11              # save rcx
+       lea     win2lin_win_arg(7), %rsi        # source (windows arg 7 and up)
+       lea     win2lin_lin_arg(7,12), %rdi     # = destination
+       mov     $6, %rcx                        # 6 arguments
+       rep
+       movsq
+       mov     %r11, %rcx              # restore rcx
+
+       win2lin_arg1
+       win2lin_arg2
+       win2lin_arg3
+       win2lin_arg4
+       win2lin_arg5
+       win2lin_arg6
+
+       call_lin_func_args(12)
+       win2lin_epilog
+       ret
+       .size   win2lin9, .-win2lin9
+
+#define win2lin(name, argc)                    \
+ENTRY(win2lin_ ## name ## _ ## argc)           \
+       lea     name(%rip), %r10 ;              \
+       jmp     win2lin ## argc
+
+#include "win2lin_stubs.h"
+
+#endif // CONFIG_X86_64
diff --git a/ubuntu/ndiswrapper/winnt_types.h b/ubuntu/ndiswrapper/winnt_types.h
new file mode 100644 (file)
index 0000000..62a8e53
--- /dev/null
@@ -0,0 +1,1702 @@
+/*
+ *  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 KI_USER_SHARED_DATA 0xfffff78000000000UL
+
+#else
+
+#define noregparm __attribute__((regparm(0)))
+#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
+
+#define packed __attribute__((packed))
+#define no_warn_unused __attribute__((unused))
+
+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 ULONG_PTR 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) wstdcall;
+       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 --git a/ubuntu/ndiswrapper/wrapmem.c b/ubuntu/ndiswrapper/wrapmem.c
new file mode 100644 (file)
index 0000000..bd63df6
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ *  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"
+
+struct slack_alloc_info {
+       struct nt_list list;
+       size_t size;
+};
+
+static struct nt_list allocs;
+static struct nt_list slack_allocs;
+static spinlock_t alloc_lock;
+
+struct vmem_block {
+       struct nt_list list;
+       int size;
+};
+
+static struct nt_list vmem_list;
+
+#if defined(ALLOC_DEBUG)
+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
+
+void wrapmem_info(void)
+{
+#ifdef ALLOC_DEBUG
+       enum alloc_type type;
+       for (type = 0; type < ALLOC_TYPE_MAX; type++)
+               INFO("total size of allocations in %d: %d",
+                      type, atomic_read(&alloc_sizes[type]));
+#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;
+       gfp_t flags;
+
+       ENTER4("size = %lu", (unsigned long)size);
+
+       if (irql_gfp() & GFP_ATOMIC)
+               flags = GFP_ATOMIC;
+       else
+               flags = GFP_KERNEL;
+       info = kmalloc(size + sizeof(*info), flags);
+       if (!info)
+               return NULL;
+       info->size = size;
+       spin_lock_bh(&alloc_lock);
+       InsertTailList(&slack_allocs, &info->list);
+       spin_unlock_bh(&alloc_lock);
+#ifdef 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);
+#ifdef ALLOC_DEBUG
+       atomic_sub(info->size, &alloc_sizes[ALLOC_TYPE_SLACK]);
+#endif
+       kfree(info);
+       EXIT4(return);
+}
+
+#if defined(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("invliad type: %d", info->type);
+#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, pgprot_t prot,
+                   const char *file, int line)
+{
+       struct alloc_info *info;
+
+       info = __vmalloc(size + sizeof(*info), gfp_mask, prot);
+       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("invliad type: %d", info->type);
+#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("invliad type: %d", info->type);
+#endif
+       free_pages((unsigned long)info, get_order(info->size));
+}
+
+#if ALLOC_DEBUG > 1
+#undef ExAllocatePoolWithTag
+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: %lu, 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 (type >= 0 && type < ALLOC_TYPE_MAX)
+               return atomic_read(&alloc_sizes[type]);
+       else
+               return -EINVAL;
+}
+
+#endif // ALLOC_DEBUG
+
+int wrapmem_init(void)
+{
+       InitializeListHead(&allocs);
+       InitializeListHead(&slack_allocs);
+       InitializeListHead(&vmem_list);
+       spin_lock_init(&alloc_lock);
+       return 0;
+}
+
+void wrapmem_exit(void)
+{
+       enum alloc_type type;
+       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);
+#ifdef ALLOC_DEBUG
+               atomic_sub(info->size, &alloc_sizes[ALLOC_TYPE_SLACK]);
+#endif
+               kfree(info);
+       }
+       type = 0;
+#ifdef 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 %d leaking", n, 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]);
+               WARNING("%p in %d of size %zu allocated at %s(%d) "
+                       "with tag 0x%08X leaking; freeing it now",
+                       info + 1, info->type, info->size, info->file,
+                       info->line, 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 --git a/ubuntu/ndiswrapper/wrapmem.h b/ubuntu/ndiswrapper/wrapmem.h
new file mode 100644 (file)
index 0000000..7374ae2
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ *  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_
+
+/* set ALLOC_DEBUG to 1 to get information about memory used by both
+ * ndiswrapper and Windows driver by reading
+ * /proc/net/ndiswrapper/debug; this will also show memory leaks
+ * (memory allocated but not freed) when ndiswrapper module is
+ * unloaded.
+
+ * ALLOC_DEBUG=2: details about individual allocations leaking is printed
+ * ALLOC_DEBUG=3: tags in ExAllocatePoolWithTag leaking printed
+*/
+
+//#ifndef ALLOC_DEBUG
+//#define ALLOC_DEBUG 1
+//#endif
+
+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 };
+
+int wrapmem_init(void);
+void wrapmem_exit(void);
+void *slack_kmalloc(size_t size);
+void slack_kfree(void *ptr);
+void wrapmem_info(void);
+
+#ifdef ALLOC_DEBUG
+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, pgprot_t prot,
+                   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);
+
+#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, prot)                           \
+       wrap__vmalloc(size, flags, prot, __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)
+
+#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
+
+#endif // _WRAPMEM_C_
+
+#else
+
+#define wrap_get_free_pages(flags, size)                       \
+       (void *)__get_free_pages(flags, get_order(size))
+
+#endif // ALLOC_DEBUG
+
+#endif
diff --git a/ubuntu/ndiswrapper/wrapndis.c b/ubuntu/ndiswrapper/wrapndis.c
new file mode 100644 (file)
index 0000000..3de1fbd
--- /dev/null
@@ -0,0 +1,2133 @@
+/*
+ *  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 "pnp.h"
+#include "loader.h"
+#include "wrapndis.h"
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/in.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);
+
+workqueue_struct_t *wrapndis_wq;
+static struct nt_thread *wrapndis_worker_thread;
+
+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);
+       if (down_interruptible(&wnd->tx_ring_mutex))
+               EXIT3(return NDIS_STATUS_FAILURE);
+       if (down_interruptible(&wnd->ndis_req_mutex)) {
+               up(&wnd->tx_ring_mutex);
+               EXIT3(return NDIS_STATUS_FAILURE);
+       }
+       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);
+       }
+       up(&wnd->ndis_req_mutex);
+       if (res == NDIS_STATUS_SUCCESS && reset_address) {
+               set_packet_filter(wnd, wnd->packet_filter);
+               set_multicast_list(wnd);
+       }
+       up(&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;
+
+       if (down_interruptible(&wnd->ndis_req_mutex))
+               EXIT3(return NDIS_STATUS_FAILURE);
+       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);
+       }
+       up(&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_PRESENT, &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, (ULONG)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,
+                         sizeof(medium_array) / sizeof(medium_array[0]),
+                         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);
+       if (wnd->physical_medium == NdisPhysicalMediumWirelessLan &&
+           wrap_is_pci_bus(wnd->wd->dev_bus)) {
+               up(&wnd->ndis_req_mutex);
+               disassociate(wnd, 0);
+               if (down_interruptible(&wnd->ndis_req_mutex))
+                       WARNING("couldn't obtain ndis_req_mutex");
+       }
+       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 bugyy 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;
+               up(&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) {
+                       up(&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 {
+               if (down_interruptible(&wnd->tx_ring_mutex))
+                       EXIT1(return NDIS_STATUS_FAILURE);
+               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) {
+                               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;
+               }
+               if (down_interruptible(&wnd->ndis_req_mutex))
+                       WARNING("couldn't lock 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("%Lx, %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++;
+               sg_element->length = frag->size;
+               sg_element->address =
+                       pci_map_page(wnd->wd->pci.pdev, frag->page,
+                                    frag->page_offset, frag->size,
+                                    PCI_DMA_TODEVICE);
+               TRACE3("%Lx, %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("%Lx, %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);
+               schedule_wrapndis_work(&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(worker_param_t param)
+{
+       struct ndis_device *wnd;
+       s8 n;
+
+       wnd = worker_param_data(param, struct ndis_device, tx_work);
+       ENTER3("tx_ok %d", wnd->tx_ok);
+       while (wnd->tx_ok) {
+               if (down_interruptible(&wnd->tx_ring_mutex))
+                       break;
+               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);
+                               up(&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) {
+                       wnd->net_dev->trans_start = jiffies;
+                       wnd->tx_ring_start =
+                               (wnd->tx_ring_start + n) % TX_RING_SIZE;
+                       wnd->is_tx_ring_full = 0;
+               }
+               up(&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);
+       schedule_wrapndis_work(&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)
+{
+       ENTER2("state: 0x%x", state);
+       if (state == NdisMediaStateConnected) {
+               netif_carrier_on(wnd->net_dev);
+               wnd->tx_ok = 1;
+               if (netif_queue_stopped(wnd->net_dev))
+                       netif_wake_queue(wnd->net_dev);
+               if (wnd->physical_medium == NdisPhysicalMediumWirelessLan) {
+                       set_bit(LINK_STATUS_ON, &wnd->ndis_pending_work);
+                       schedule_wrapndis_work(&wnd->ndis_work);
+               }
+       } else if (state == NdisMediaStateDisconnected) {
+               netif_carrier_off(wnd->net_dev);
+               netif_stop_queue(wnd->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);
+                       schedule_wrapndis_work(&wnd->ndis_work);
+               }
+       } else {
+               WARNING("invalid media state: 0x%x", state);
+       }
+}
+
+static int ndis_net_dev_open(struct net_device *net_dev)
+{
+       ENTER1("%p", netdev_priv(net_dev));
+       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);
+       schedule_wrapndis_work(&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 ||
+                  net_dev->mc_count > wnd->multicast_size) {
+               packet_filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
+               TRACE2("0x%08x", packet_filter);
+       } else if (net_dev->mc_count > 0) {
+               int i, size;
+               char *buf;
+               struct dev_mc_list *mclist;
+               size = min(wnd->multicast_size, net_dev->mc_count);
+               TRACE2("%d, %d", wnd->multicast_size, net_dev->mc_count);
+               buf = kmalloc(size * ETH_ALEN, GFP_KERNEL);
+               if (!buf) {
+                       WARNING("couldn't allocate memory");
+                       EXIT2(return);
+               }
+               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++;
+               }
+               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);
+}
+
+static void iw_stats_timer_proc(unsigned long data)
+{
+       struct ndis_device *wnd = (struct ndis_device *)data;
+
+       ENTER2("%d", wnd->iw_stats_interval);
+       if (wnd->iw_stats_interval > 0) {
+               set_bit(COLLECT_IW_STATS, &wnd->ndis_pending_work);
+               schedule_wrapndis_work(&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;
+       wnd->iw_stats_timer.data = (unsigned long)wnd;
+       wnd->iw_stats_timer.function = iw_stats_timer_proc;
+       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);
+}
+
+static void hangcheck_proc(unsigned long data)
+{
+       struct ndis_device *wnd = (struct ndis_device *)data;
+
+       ENTER3("%d", wnd->hangcheck_interval);
+       if (wnd->hangcheck_interval > 0) {
+               set_bit(HANGCHECK, &wnd->ndis_pending_work);
+               schedule_wrapndis_work(&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;
+       wnd->hangcheck_timer.data = (unsigned long)wnd;
+       wnd->hangcheck_timer.function = hangcheck_proc;
+       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 ndis_worker(worker_param_t param)
+{
+       struct ndis_device *wnd;
+
+       wnd = worker_param_data(param, 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;
+}
+
+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 (types & Ndis802_11OFDM24)
+                       mode = Ndis802_11OFDM24;
+               else if (types & Ndis802_11DS)
+                       mode = Ndis802_11DS;
+               else if (types & Ndis802_11OFDM5)
+                       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, %lu", res, (unsigned long)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);
+}
+
+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 intresting 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;
+}
+
+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;
+}
+
+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,
+       .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,
+};
+
+static int notifier_event(struct notifier_block *notifier, unsigned long event,
+                         void *ptr)
+{
+       struct net_device *net_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 */
+               wrap_procfs_remove_ndis_device(wnd);
+               printk(KERN_INFO "%s: changing interface name from '%s' to "
+                      "'%s'\n", DRIVER_NAME, wnd->netdev_name, net_dev->name);
+               memcpy(wnd->netdev_name, net_dev->name,
+                      sizeof(wnd->netdev_name));
+               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_open = ndis_net_dev_open,
+       .ndo_stop = ndis_net_dev_close,
+       .ndo_start_xmit = tx_skbuff,
+       .ndo_change_mtu = ndis_change_mtu,
+       .ndo_set_multicast_list = ndis_set_multicast_list,
+       .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->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
+       if (wnd->physical_medium == NdisPhysicalMediumWirelessLan) {
+               net_dev->wireless_handlers = &ndis_handler_def;
+       }
+       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;
+       }
+       memcpy(wnd->netdev_name, net_dev->name, sizeof(wnd->netdev_name));
+       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 ",
+              wnd->wd->driver->name, n, wnd->drv_ndis_version, buf,
+              wnd->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;
+
+       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("capbilities = %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) ?
+                      ", 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" : "");
+
+               set_default_iw_params(wnd);
+       }
+       status = mp_query_int(wnd, OID_GEN_MEDIA_CONNECT_STATUS, (int *)buf);
+       if (status == NDIS_STATUS_SUCCESS)
+               set_media_state(wnd, *((int *)buf));
+       kfree(buf);
+       wrap_procfs_add_ndis_device(wnd);
+       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:
+err_register:
+       kfree(buf);
+err_start:
+       ndis_remove_device(wnd);
+       EXIT1(return NDIS_STATUS_FAILURE);
+}
+
+static int ndis_remove_device(struct ndis_device *wnd)
+{
+       s8 tx_pending;
+
+       /* prevent setting essid during disassociation */
+       memset(&wnd->essid, 0, sizeof(wnd->essid));
+       wnd->tx_ok = 0;
+       if (wnd->max_tx_packets)
+               unregister_netdev(wnd->net_dev);
+       netif_carrier_off(wnd->net_dev);
+       /* if device is suspended, but resume failed, tx_ring_mutex
+        * may already be locked */
+       if (down_trylock(&wnd->tx_ring_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);
+       up(&wnd->tx_ring_mutex);
+       wrap_procfs_remove_ndis_device(wnd);
+       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;
+       }
+       if (wnd->pmkids)
+               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 wstdcall NTSTATUS NdisAddDevice(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 defined(DEBUG) && 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;
+       wd->wnd = wnd;
+       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);
+       init_MUTEX(&wnd->tx_ring_mutex);
+       init_MUTEX(&wnd->ndis_req_mutex);
+       wnd->ndis_req_done = 0;
+       initialize_work(&wnd->tx_work, tx_worker, wnd);
+       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;
+       init_timer(&wnd->hangcheck_timer);
+       wnd->scan_timestamp = 0;
+       init_timer(&wnd->iw_stats_timer);
+       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;
+       initialize_work(&wnd->ndis_work, ndis_worker, wnd);
+       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 = NdisAddDevice;
+       return 0;
+}
+
+int wrapndis_init(void)
+{
+       wrapndis_wq = create_singlethread_workqueue("wrapndis_wq");
+       if (!wrapndis_wq)
+               EXIT1(return -ENOMEM);
+       wrapndis_worker_thread = wrap_worker_init(wrapndis_wq);
+       TRACE1("%p", wrapndis_worker_thread);
+       register_netdevice_notifier(&netdev_notifier);
+       return 0;
+}
+
+void wrapndis_exit(void)
+{
+       unregister_netdevice_notifier(&netdev_notifier);
+       if (wrapndis_wq)
+               destroy_workqueue(wrapndis_wq);
+       TRACE1("%p", wrapndis_worker_thread);
+       if (wrapndis_worker_thread)
+               ObDereferenceObject(wrapndis_worker_thread);
+}
diff --git a/ubuntu/ndiswrapper/wrapndis.h b/ubuntu/ndiswrapper/wrapndis.h
new file mode 100644 (file)
index 0000000..6fce2dd
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ *  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);
+
+driver_dispatch_t winNdisDispatchPnp;
+driver_dispatch_t winNdisDispatchPower;
+driver_dispatch_t winNdisDispatchDeviceControl;
+
+struct iw_statistics *get_iw_stats(struct net_device *dev);
+
+#endif
diff --git a/ubuntu/ndiswrapper/wrapper.c b/ubuntu/ndiswrapper/wrapper.c
new file mode 100644 (file)
index 0000000..3e4bf08
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *  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;
+
+#if defined(DEBUG) && (DEBUG > 0)
+int debug = DEBUG;
+#else
+int debug = 0;
+#endif
+
+WRAP_MODULE_PARM_STRING(if_name, 0400);
+MODULE_PARM_DESC(if_name, "Network interface name or template "
+                "(default: wlan%d)");
+WRAP_MODULE_PARM_INT(proc_uid, 0600);
+MODULE_PARM_DESC(proc_uid, "The uid of the files created in /proc "
+                "(default: 0).");
+WRAP_MODULE_PARM_INT(proc_gid, 0600);
+MODULE_PARM_DESC(proc_gid, "The gid of the files created in /proc "
+                "(default: 0).");
+WRAP_MODULE_PARM_INT(debug, 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
+ */
+WRAP_MODULE_PARM_INT(hangcheck_interval, 0600);
+MODULE_PARM_DESC(hangcheck_interval, "The interval, in seconds, for checking"
+                " if driver is hung. (default: 0)");
+
+WRAP_MODULE_PARM_STRING(utils_version, 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();
+#ifdef ENABLE_USB
+       usb_exit();
+#endif
+
+       wrap_procfs_remove();
+       wrapndis_exit();
+       ndis_exit();
+       rtl_exit();
+       crt_exit();
+       ntoskernel_exit();
+       wrapmem_exit();
+}
+
+static int __init wrapper_init(void)
+{
+       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() || crt_init() ||
+           rtl_init() || ndis_init() || wrapndis_init() ||
+#ifdef ENABLE_USB
+           usb_init() ||
+#endif
+           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 --git a/ubuntu/ndiswrapper/wrapper.h b/ubuntu/ndiswrapper/wrapper.h
new file mode 100644 (file)
index 0000000..ba9a09f
--- /dev/null
@@ -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 */