[转载]NDIS Protocol Driver 入门

信息来源:小四 (http://www.opencjk.org/~scz/

标题: MSDN系列(14)--"NDIS Protocol Driver"入门

日期: 2004-07-19 16:55
更新: 2004-08-17 16:23
链接: http://www.opencjk.org/~scz/200408171624.txt

--------------------------------------------------------------------------
   ☆ NDIS Protocol Driver
   ☆ ntoskrnl.exe引出的一些运行时函数
   ☆ 一个完整的NDIS Protocol Driver框架
      a) NDIS协议驱动
        1) ndisprotocol.c
        2) sources
        3) makefile
        4) ndisprotocol.inf
      b) NDIS协议驱动的用户态测试程序
        1) ndisprotocoltest.c
        2) sources
        3) makefile
      c) NDIS组件配置程序
      d) 源代码目录结构
      e) 编译
      f) 安装
      g) 测试
      h) 卸载
      i) 一些遗留问题
   ☆ 参考资源

--------------------------------------------------------------------------

☆ NDIS Protocol Driver

[1]给了一张Windows网络架构图,有助于理解NDIS,推荐入门者先看看这张图。

[2]有一些关于Windows网络架构的讨论,这只是一种个人学术观点,仅供参考,不可
当成官方结论。

NDIS(Network Device Interface Specification)提供一个系统的、完整的Wrapper,
NDIS Miniport Driver、NDIS ProtocolDriver等等均属于"插入"这个Wrapper中的"
模块",这些驱动调用Wrapper提供的函数,同时也向Wrapper注册回调函数,整个运
作过程由Wrapper统一调度。Wrapper对应ndis.sys。

TDI Client Driver利用协议驱动上沿引出的TDI接口(Transport Data Interface)实
现命名管道、邮槽、Winsock等等。

下面这些注册表内容对应着Network Control Panel Applet (NCPA):

--------------------------------------------------------------------------
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002bE10318}
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E973-E325-11CE-BFC1-08002BE10318}
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E974-E325-11CE-BFC1-08002BE10318}
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E975-E325-11CE-BFC1-08002BE10318}

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}

   对应GUID_DEVCLASS_NET、Miniport Driver、Net。收到包后NDIS首先调用
   Miniport Driver进行处理。Miniport Driver负责控制网卡硬件特性,在协议驱
   动与网卡之间传递报文。

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E973-E325-11CE-BFC1-08002BE10318}

   对应GUID_DEVCLASS_NETCLIENT、Client Driver、NetClient。"Client for
   Microsoft Networks"即是此类型驱动。负责向用户态提供NetBIOS Client API。

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E974-E325-11CE-BFC1-08002BE10318}

   对应GUID_DEVCLASS_NETSERVICE、Service Driver、NetService。"File and
   Printer Sharing for Microsoft Networks"即是此类型驱动。

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E975-E325-11CE-BFC1-08002BE10318}

   对应GUID_DEVCLASS_NETTRANS、Protocol Driver、NetTrans。负责实现各种网
   络协议,比如tcpip.sys实现了TCP/IP协议。协议驱动接收Miniport Driver上传
   的报文,也接收Client Driver、Service Driver、TDI Client Driver下传的报
   文。
--------------------------------------------------------------------------

中间层驱动(Intermediate Driver)是一种混合型驱动,位于Miniport Driver与协议
驱动之间,对下看起来像协议驱动,对上看起来像Miniport Driver。

在这些不同类型的驱动之间有一种操作叫作"绑定",其本质在于向NDIS Wrapper指明
报文(Packet)的传递路线。

☆ ntoskrnl.exe引出的一些运行时函数

> dumpbin /exports %systemroot%\system32\ntoskrnl.exe | find /I "printf"
     1397  574 00052BCB _snprintf
     1398  575 00052C22 _snwprintf
     1406  57D 00052D73 _vsnprintf
     1407  57E 00052DC9 _vsnwprintf
     1430  595 00053D4D sprintf
     1443  5A2 000543E0 swprintf
     1450  5A9 000544F4 vsprintf
> dumpbin /exports %systemroot%\system32\ntoskrnl.exe | find "_str"
     1399  576 00052C92 _stricmp
     1400  577 00052C97 _strlwr
     1401  578 00052CBA _strnicmp
     1402  579 00052CD0 _strnset
     1403  57A 00052D00 _strrev
     1404  57B 00052D30 _strset
     1405  57C 00052D50 _strupr
> dumpbin /exports %systemroot%\system32\ntoskrnl.exe | find " str"
     1432  597 00053DC0 strcat
     1433  598 00053EB0 strchr
     1434  599 00053F70 strcmp
     1435  59A 00053DB0 strcpy
     1436  59B 00054000 strlen
     1437  59C 00054080 strncat
     1438  59D 000541B0 strncmp
     1439  59E 000541F0 strncpy
     1440  59F 000542F0 strrchr
     1441  5A0 00054320 strspn
     1442  5A1 00054360 strstr
> dumpbin /exports %systemroot%\system32\ntoskrnl.exe | find "wcs"
     1408  57F 00052E38 _wcsicmp
     1409  580 00052E83 _wcslwr
     1410  581 00052EAF _wcsnicmp
     1411  582 00052F08 _wcsnset
     1412  583 00052F31 _wcsrev
     1413  584 00052F63 _wcsupr
     1422  58D 000531DB mbstowcs
     1451  5AA 0005454B wcscat
     1452  5AB 00054591 wcschr
     1453  5AC 000545B3 wcscmp
     1454  5AD 00054575 wcscpy
     1455  5AE 000545E5 wcscspn
     1456  5AF 00054628 wcslen
     1457  5B0 0005463E wcsncat
     1458  5B1 0005467B wcsncmp
     1459  5B2 000546B0 wcsncpy
     1460  5B3 000546ED wcsrchr
     1461  5B4 0005471D wcsspn
     1462  5B5 00054763 wcsstr
     1463  5B6 000547C1 wcstombs
> dumpbin /exports %systemroot%\system32\ntoskrnl.exe | find "mem"
     1424  58F 00053290 memchr
     1425  590 00053340 memcpy
     1426  591 00053680 memmove
     1427  592 000539C0 memset
> dumpbin /exports %systemroot%\system32\ntoskrnl.exe | find "_ito"
     1393  570 00052B76 _itoa
     1394  571 00052BA0 _itow

☆ 一个完整的NDIS Protocol Driver框架

a) NDIS协议驱动

XP SP1 DDK自带了一个名为ndisuio的例子(NDIS User mode I/O Protocol),但我没
有找到NT4 DDK中名为packet的例子。参[3]、[4]、[5],这些都是完整的协议驱动源
代码。

ndisuio演示了"connection-less NDIS 5.0/5.1 protocol driver",用户态程序可
简单地通过ReadFile/WriteFile直接操作链路层数据(物理帧)。对于sniffer一类的
需求,这已经足够了。ndisuio没有在其上沿提供TDI接口。ndisuio做了很多限制,
使得我们无法随心所欲地操作链路层数据。假设有如下结构:

struct etherheader
{
   unsigned char        eth_dst[6]; /* destination eth addr */
   unsigned char        eth_src[6]; /* source ether addr   */
   unsigned short int    eth_type;  /* packet type ID field */
};

ndisuio在DispatchWrite()例程中对eth_src、eth_type进行检查,发送报文时不能
伪造源MAC,必须匹配事先指定的eth_type。

ndisuio允许用户态程序通过DeviceIoControl()指定eth_type,但其在
DispatchDeviceControl()例程中做了限制,不能任意指定eth_type。

ndisuio在ProtocolReceive()、ProtocolReceivePacket()例程中对eth_type进行检
查,只接收与事先指定的eth_type相匹配的报文。

ndisuio允许用户态程序通过DeviceIoControl()向Miniport Driver设置OID,但其在
DispatchDeviceControl()例程中做了限制,只允许设置部分OID。

从ndisuio例子所附用户态测试程序的代码来看,曾经有一个版本的ndisuio未做前述
限制,至少发送报文时可以伪造源MAC。现在我们要做的就是简单地注释掉相应代码,
使得可以随心所欲地操作链路层数据。

XP事实上缺省安装了ndisuio,可用"net start ndisuio"加载这个隐藏的协议驱动。
据tk讲,2000也缺省安装ndisuio,我不确认是某个Service Pack带进来的,还是最
初就有。由于存在前述限制,ndisuio对我们来讲没有多少意义。

下面是XP SP1中与ndisuio相关的注册表内容,NDI是"Network Device Installer"的
缩写。

--------------------------------------------------------------------------
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E975-E325-11CE-BFC1-08002BE10318}\{03130807-B5F2-47A1-81B0-B870D16F272B}]
"Characteristics"=dword:00000028
"InfPath"="ndisuio.inf"
"InfSection"="Install"
"Description"="NDIS Usermode I/O Protocol"
"ComponentId"="ms_ndisuio"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E975-E325-11CE-BFC1-08002BE10318}\{03130807-B5F2-47A1-81B0-B870D16F272B}\Ndi]
"Service"="Ndisuio"
"HelpText"="A driver to support user-mode I/O on NDIS devices"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E975-E325-11CE-BFC1-08002BE10318}\{03130807-B5F2-47A1-81B0-B870D16F272B}\Ndi\Interfaces]
"UpperRange"="noupper"
"LowerRange"="ndis5,ndis4,ndis5_uio"
--------------------------------------------------------------------------
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\Root\LEGACY_NDISUIO]
"NextInstance"=dword:00000001

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\Root\LEGACY_NDISUIO\0000]
"Service"="Ndisuio"
"Legacy"=dword:00000001
"ConfigFlags"=dword:00000000
"Class"="LegacyDriver"
"ClassGUID"="{8ECC055D-047F-11D1-A537-0000F8753ED1}"
"DeviceDesc"="NDIS Usermode I/O Protocol"
"Capabilities"=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\Root\LEGACY_NDISUIO\0000\LogConf]

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\Root\LEGACY_NDISUIO\0000\Control]
"ActiveService"="Ndisuio"
--------------------------------------------------------------------------
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Ndisuio]
"Type"=dword:00000001
"Start"=dword:00000003
"ErrorControl"=dword:00000001
"Tag"=dword:0000000c
"ImagePath"=hex(2):53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,44,00,\
  52,00,49,00,56,00,45,00,52,00,53,00,5c,00,6e,00,64,00,69,00,73,00,75,00,69,\
  00,6f,00,2e,00,73,00,79,00,73,00,00,00
"DisplayName"="NDIS Usermode I/O Protocol"
"Group"="NDIS"
"Description"="NDIS Usermode I/O Protocol"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Ndisuio\Linkage]
"Bind"=hex(7):5c,00,44,00,65,00,76,00,69,00,63,00,65,00,5c,00,7b,00,33,00,46,\
  00,32,00,46,00,37,00,46,00,35,00,33,00,2d,00,39,00,34,00,43,00,43,00,2d,00,\
  34,00,43,00,38,00,33,00,2d,00,42,00,36,00,38,00,33,00,2d,00,34,00,33,00,30,\
  00,38,00,43,00,35,00,37,00,33,00,37,00,31,00,32,00,44,00,7d,00,00,00,5c,00,\
  44,00,65,00,76,00,69,00,63,00,65,00,5c,00,7b,00,34,00,46,00,44,00,45,00,38,\
  00,42,00,37,00,42,00,2d,00,31,00,42,00,41,00,31,00,2d,00,34,00,42,00,30,00,\
  41,00,2d,00,39,00,30,00,32,00,39,00,2d,00,31,00,34,00,31,00,45,00,31,00,33,\
  00,35,00,35,00,39,00,45,00,36,00,39,00,7d,00,00,00,00,00
"Route"=hex(7):22,00,7b,00,33,00,46,00,32,00,46,00,37,00,46,00,35,00,33,00,2d,\
  00,39,00,34,00,43,00,43,00,2d,00,34,00,43,00,38,00,33,00,2d,00,42,00,36,00,\
  38,00,33,00,2d,00,34,00,33,00,30,00,38,00,43,00,35,00,37,00,33,00,37,00,31,\
  00,32,00,44,00,7d,00,22,00,00,00,22,00,7b,00,34,00,46,00,44,00,45,00,38,00,\
  42,00,37,00,42,00,2d,00,31,00,42,00,41,00,31,00,2d,00,34,00,42,00,30,00,41,\
  00,2d,00,39,00,30,00,32,00,39,00,2d,00,31,00,34,00,31,00,45,00,31,00,33,00,\
  35,00,35,00,39,00,45,00,36,00,39,00,7d,00,22,00,00,00,00,00
"Export"=hex(7):5c,00,44,00,65,00,76,00,69,00,63,00,65,00,5c,00,4e,00,64,00,69,\
  00,73,00,75,00,69,00,6f,00,5f,00,7b,00,33,00,46,00,32,00,46,00,37,00,46,00,\
  35,00,33,00,2d,00,39,00,34,00,43,00,43,00,2d,00,34,00,43,00,38,00,33,00,2d,\
  00,42,00,36,00,38,00,33,00,2d,00,34,00,33,00,30,00,38,00,43,00,35,00,37,00,\
  33,00,37,00,31,00,32,00,44,00,7d,00,00,00,5c,00,44,00,65,00,76,00,69,00,63,\
  00,65,00,5c,00,4e,00,64,00,69,00,73,00,75,00,69,00,6f,00,5f,00,7b,00,34,00,\
  46,00,44,00,45,00,38,00,42,00,37,00,42,00,2d,00,31,00,42,00,41,00,31,00,2d,\
  00,34,00,42,00,30,00,41,00,2d,00,39,00,30,00,32,00,39,00,2d,00,31,00,34,00,\
  31,00,45,00,31,00,33,00,35,00,35,00,39,00,45,00,36,00,39,00,7d,00,00,00,00,\
  00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Ndisuio\Security]
"Security"=hex:01,00,14,80,90,00,00,00,9c,00,00,00,14,00,00,00,30,00,00,00,02,\
  00,1c,00,01,00,00,00,02,80,14,00,ff,01,0f,00,01,01,00,00,00,00,00,01,00,00,\
  00,00,02,00,60,00,04,00,00,00,00,00,14,00,fd,01,02,00,01,01,00,00,00,00,00,\
  05,12,00,00,00,00,00,18,00,ff,01,0f,00,01,02,00,00,00,00,00,05,20,00,00,00,\
  20,02,00,00,00,00,14,00,8d,01,02,00,01,01,00,00,00,00,00,05,0b,00,00,00,00,\
  00,18,00,fd,01,02,00,01,02,00,00,00,00,00,05,20,00,00,00,23,02,00,00,01,01,\
  00,00,00,00,00,05,12,00,00,00,01,01,00,00,00,00,00,05,12,00,00,00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Ndisuio\Enum]
"0"="Root\\LEGACY_NDISUIO\\0000"
"Count"=dword:00000001
"NextInstance"=dword:00000001
--------------------------------------------------------------------------

名为"Characteristics"的键值对应INF文件中的Characteristics项,0x28表示逻辑
或NCF_HIDDEN(0x08)、NCF_NOT_USER_REMOVABLE(0x20),即NCPA中不可见、不可通过
NCPA或设备管理器删除。调试协议驱动时,应在INF文件中指定0x00,这样才便于删
除、重新增加并测试。

1) ndisprotocol.c

本文所给完整框架(安装/卸载、驱动、用户态测试程序)完全是DDK自带例子代码,我
可写不出这么大的框架代码来。如有疑问,请问微软。

--------------------------------------------------------------------------
/*
* For x86/EWindows XP SP1 & VC 7 & Windows DDK 2600.1106
*/

/************************************************************************
*                                               *
*                     Head File                    *
*                                               *
************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ndis.h>
#include <ntddk.h>
#include <windef.h>
#include <devioctl.h>

/************************************************************************
*                                               *
*                     Macro                       *
*                                               *
************************************************************************/

#define INTERNALNAME                      L"\\Device\\NDISProtocolInternal"
#define EXTERNALNAME                      L"\\??\\NDISProtocolExternal"
#define PRIVATETAG                       &#39;OFSN&#39;
#define NDISPROTOCOL_INDEX                  0x0800
#define IOCTL_NDISPROTOCOL_GET_PRIVATEFLAGS      CTL_CODE   \
(                                          \
   FILE_DEVICE_NETWORK,                           \
   NDISPROTOCOL_INDEX + 0,                         \
   METHOD_BUFFERED,                              \
   FILE_READ_ACCESS                              \
)
#define IOCTL_NDISPROTOCOL_SET_PRIVATEFLAGS      CTL_CODE   \
(                                          \
   FILE_DEVICE_NETWORK,                           \
   NDISPROTOCOL_INDEX + 1,                         \
   METHOD_BUFFERED,                              \
   FILE_WRITE_ACCESS                             \
)
#define IOCTL_NDISPROTOCOL_OPEN_DEVICE          CTL_CODE   \
(                                          \
   FILE_DEVICE_NETWORK,                           \
   NDISPROTOCOL_INDEX + 2,                         \
   METHOD_BUFFERED,                              \
   FILE_READ_ACCESS | FILE_WRITE_ACCESS                \
)
#define IOCTL_NDISPROTOCOL_GET_OID_VALUE        CTL_CODE   \
(                                          \
   FILE_DEVICE_NETWORK,                           \
   NDISPROTOCOL_INDEX + 3,                         \
   METHOD_BUFFERED,                              \
   FILE_READ_ACCESS                              \
)
#define IOCTL_NDISPROTOCOL_SET_OID_VALUE        CTL_CODE   \
(                                          \
   FILE_DEVICE_NETWORK,                           \
   NDISPROTOCOL_INDEX + 4,                         \
   METHOD_BUFFERED,                              \
   FILE_WRITE_ACCESS                             \
)
#define IOCTL_NDISPROTOCOL_SET_ETHER_TYPE        CTL_CODE   \
(                                          \
   FILE_DEVICE_NETWORK,                           \
   NDISPROTOCOL_INDEX + 5,                         \
   METHOD_BUFFERED,                              \
   FILE_WRITE_ACCESS                             \
)
#define IOCTL_NDISPROTOCOL_QUERY_BINDING        CTL_CODE   \
(                                          \
   FILE_DEVICE_NETWORK,                           \
   NDISPROTOCOL_INDEX + 6,                         \
   METHOD_BUFFERED,                              \
   FILE_READ_ACCESS                              \
)
#define IOCTL_NDISPROTOCOL_BIND_WAIT           CTL_CODE   \
(                                          \
   FILE_DEVICE_NETWORK,                           \
   NDISPROTOCOL_INDEX + 7,                         \
   METHOD_BUFFERED,                              \
   FILE_READ_ACCESS | FILE_WRITE_ACCESS                \
)

#define PRIVATEFLAGS_ETHERTYPE               0x00000001
#define PRIVATEFLAGS_DEFAULT                0x00000001

typedef struct _DEVICE_EXTENSION
{
   PDEVICE_OBJECT       DeviceObject;
   ULONG             DeviceNumber;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

#define PRIVATEMIN(x,y)                    ((x)<(y)?(x):(y))
#define SET_FLAGS(_FlagsVar, _Mask, _BitsToSet)    (_FlagsVar) = ((_FlagsVar) & ~(_Mask)) | (_BitsToSet)
#define TEST_FLAGS(_FlagsVar, _Mask, _BitsToCheck)  (((_FlagsVar) & (_Mask)) == (_BitsToCheck))

#define PRIVATE_BIND_IDLE                  0x00000000
#define PRIVATE_BIND_OPENING                0x00000001
#define PRIVATE_BIND_FAILED                 0x00000002
#define PRIVATE_BIND_ACTIVE                 0x00000004
#define PRIVATE_BIND_CLOSING                0x00000008
#define PRIVATE_BIND_FLAGS                  0x0000000F

#define PRIVATE_OPEN_IDLE                  0x00000000
#define PRIVATE_OPEN_ACTIVE                 0x00000010
#define PRIVATE_OPEN_FLAGS                  0x000000F0

#define PRIVATE_RESET_IN_PROGRESS             0x00000100
#define PRIVATE_NOT_RESETTING                0x00000000
#define PRIVATE_RESET_FLAGS                 0x00000100

#define PRIVATE_MEDIA_CONNECTED              0x00000000
#define PRIVATE_MEDIA_DISCONNECTED            0x00000200
#define PRIVATE_MEDIA_FLAGS                 0x00000200

#define PRIVATE_READ_SERVICING               0x00100000
#define PRIVATE_READ_FLAGS                  0x00100000

#define PRIVATE_UNBIND_RECEIVED              0x10000000
#define PRIVATE_UNBIND_FLAGS                0x10000000

#define DEFAULT_PACKET_FILTER                (NDIS_PACKET_TYPE_DIRECTED|NDIS_PACKET_TYPE_MULTICAST|NDIS_PACKET_TYPE_BROADCAST)

typedef struct _ADAPTER_CONTEXT
{
   LIST_ENTRY          Link;
   NDIS_SPIN_LOCK       Lock;
   ULONG             ReferenceCount;
   ULONG             Flags;
   PFILE_OBJECT        FileObject;
   NDIS_STRING         DeviceName;
   NDIS_STRING         AdapterInstanceName;
   NDIS_STATUS         BindStatus;
   NDIS_EVENT          BindEvent;
   NDIS_HANDLE         SendPacketPool;
   NDIS_HANDLE         SendBufferPool;
   NDIS_HANDLE         RecvPacketPool;
   NDIS_HANDLE         RecvBufferPool;
   LIST_ENTRY          PendedWrites;
   ULONG             PendedWriteCount;
   LIST_ENTRY          PendedReads;
   ULONG             PendedReadCount;
   LIST_ENTRY          RecvPacketQueue;
   ULONG             RecvPacketCount;
   NDIS_HANDLE         NdisBindingHandle;
   UCHAR             CurrentAddress[6];
   ULONG             MacOptions;
   ULONG             MaximumFrameSize;
   NET_DEVICE_POWER_STATE  PowerState;
   NDIS_EVENT          PoweredUpEvent;
} ADAPTER_CONTEXT, *PADAPTER_CONTEXT;

typedef struct _BINDINGINFO
{
   ULONG             BindingIndex;          // 0-based binding number
   ULONG             DeviceNameOffset;        // from start of this struct
   ULONG             DeviceNameLength;        // in bytes
   ULONG             AdapterInstanceNameOffset;  // from start of this struct
   ULONG             AdapterInstanceNameLength;  // in bytes
} BINDINGINFO, *PBINDINGINFO;

typedef struct _OIDVALUE
{
   NDIS_OID           Oid;
   UCHAR             Value[sizeof(ULONG)];
} OIDVALUE, *POIDVALUE;

#define MIN_SEND_PACKET_NUM                 20
#define MAX_SEND_PACKET_NUM                 400

typedef struct _SEND_PACKET_PROTOCOLRESERVED
{
   PIRP              Irp;
   ULONG             ReferenceCount;

} SEND_PACKET_PROTOCOLRESERVED, *PSEND_PACKET_PROTOCOLRESERVED;

#define MIN_RECV_PACKET_NUM                 4
#define MAX_RECV_PACKET_NUM                 20
#define MAX_RECV_BUFFER_NUM                 20
#define MAX_RECV_QUEUE_NUM                  4

typedef struct _RECV_PACKET_PROTOCOLRESERVED
{
   LIST_ENTRY          Link;
   PNDIS_BUFFER        Buffer;
} RECV_PACKET_PROTOCOLRESERVED, *PRECV_PACKET_PROTOCOLRESERVED;

#define GETBUFFER(x)                      (((PRECV_PACKET_PROTOCOLRESERVED)(x)->ProtocolReserved)->Buffer)
#define GETLINK(x)                       (&((PRECV_PACKET_PROTOCOLRESERVED)(x)->ProtocolReserved)->Link)
#define GETRECVPACKET(x)                   CONTAINING_RECORD(CONTAINING_RECORD(x, RECV_PACKET_PROTOCOLRESERVED, Link), NDIS_PACKET, ProtocolReserved)
#define GETREFERENCECOUNT(x)                (((PSEND_PACKET_PROTOCOLRESERVED)(x)->ProtocolReserved)->ReferenceCount)
#define GETIRP(x)                        (((PSEND_PACKET_PROTOCOLRESERVED)(x)->ProtocolReserved)->Irp)
#define GETCANCELID()                     (HighCancelId | (NdisInterlockedIncrement(&LowCancelId) & 0x00FFFFFF))

typedef struct _REQUEST_CONTEXT
{
   NDIS_REQUEST        NdisRequest;
   NDIS_EVENT          NdisRequestEvent;
   ULONG             Status;
} REQUEST_CONTEXT, *PREQUEST_CONTEXT;

#pragma pack( push, 1 )

struct etherheader
{
   unsigned char        eth_dst[6]; /* destination eth addr */
   unsigned char        eth_src[6]; /* source ether addr   */
   unsigned short int    eth_type;  /* packet type ID field */
};

#pragma pack( pop )

#define swap_16(x)                       ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
#define swap_32(x)                       ((((x) >> 24) & 0xff) | (((x) & 0xff) << 24) | (((x) >> 8) & 0xff00) | (((x) & 0xff00) << 8))

#define ETH_P_IP                        0x0800       /* Internet Protocol packet    */
#define ETH_P_ARP                        0x0806       /* Address Resolution packet   */
#define ETH_P_RARP                       0x8035       /* Reverse Addr Res packet    */
#define ETH_P_8021P                      0x8100       /* 802.1p                */
#define ETH_P_DEFAULT                     ETH_P_ARP

/************************************************************************
*                                               *
*                   Function Prototype                *
*                                               *
************************************************************************/

static NTSTATUS         NDISProtocolCreateDevice
(
   IN  PDRIVER_OBJECT       DriverObject,
   IN  ULONG             DeviceNumber
);
static VOID            NDISProtocolDeleteDevice
(
   IN  PDEVICE_OBJECT       DeviceObject
);
static NTSTATUS         NDISProtocolDispatch
(
   IN  PDEVICE_OBJECT       DeviceObject,
   IN  PIRP              Irp
);
static NTSTATUS         NDISProtocolDispatchRead
(
   IN  PDEVICE_OBJECT       DeviceObject,
   IN  PIRP              Irp
);
static VOID            NDISProtocolReadCancel
(
   IN  PDEVICE_OBJECT       DeviceObject,
   IN  PIRP              Irp
);
static NTSTATUS         NDISProtocolDispatchWrite
(
   IN  PDEVICE_OBJECT       DeviceObject,
   IN  PIRP              Irp
);
#ifdef NDIS51
static VOID            NDISProtocolWriteCancel
(
   IN  PDEVICE_OBJECT       DeviceObject,
   IN  PIRP              Irp
);
#endif
static NTSTATUS         NDISProtocolDispatchCleanup
(
   IN  PDEVICE_OBJECT       DeviceObject,
   IN  PIRP              Irp
);
static NTSTATUS         NDISProtocolDispatchDeviceControl
(
   IN  PDEVICE_OBJECT       DeviceObject,
   IN  PIRP              Irp
);
static VOID            NDISProtocolDriverUnload
(
   IN  PDRIVER_OBJECT       DriverObject
);
static PNDIS_PACKET      PrivateAllocateReceivePacket
(
   IN  PADAPTER_CONTEXT      AdapterContext,
   IN  UINT              DataLength,
   OUT PUCHAR            *pData
);
static VOID            PrivateCancelPendingReads
(
   IN  PADAPTER_CONTEXT      AdapterContext
);
static NDIS_STATUS       PrivateCreateBinding
(
   IN  PADAPTER_CONTEXT      AdapterContext,
   IN  PWSTR             DeviceName,
   IN  ULONG             DeviceNameLength
);
static VOID            PrivateDereferenceCount
(
   IN  PADAPTER_CONTEXT      AdapterContext
);
static VOID            PrivateFlushReceiveQueue
(
   IN  PADAPTER_CONTEXT      AdapterContext
);
static VOID            PrivateFreeAdapterContextResources
(
   IN  PADAPTER_CONTEXT      AdapterContext
);
static VOID            PrivateFreeReceivePacket
(
   IN  PADAPTER_CONTEXT      AdapterContext,
   IN  PNDIS_PACKET        Packet
);
static NDIS_STATUS       PrivateGetOidValue
(
   IN  PADAPTER_CONTEXT      AdapterContext,
   OUT PVOID             OutputBuffer,
   IN  ULONG             OutputBufferLength,
   OUT PULONG            BytesWritten
);
static PADAPTER_CONTEXT    PrivateLookupDevice
(
   IN  PWSTR             DeviceName,
   IN  ULONG             DeviceNameLength
);
static NDIS_STATUS       PrivateNdisRequest
(
   IN  PADAPTER_CONTEXT      AdapterContext,
   IN  NDIS_REQUEST_TYPE     RequestType,
   IN  NDIS_OID           Oid,
   IN  PVOID             InformationBuffer,
   IN  UINT              InformationBufferLength,
   OUT PUINT             BytesProcessed
);
static VOID            PrivateNdisStatusToNtStatus
(
   IN  NDIS_STATUS         NdisStatus,
   IN  NTSTATUS          *NtStatus
);
static NTSTATUS         PrivateOpenDevice
(
   IN  PWSTR             DeviceName,
   IN  ULONG             DeviceNameLength,
   IN  PFILE_OBJECT        FileObject,
   OUT PADAPTER_CONTEXT     *pAdapterContext
);
static NDIS_STATUS       PrivateQueryBinding
(
   IN  PUCHAR            Buffer,
   IN  ULONG             InputBufferLength,
   IN  ULONG             OutputBufferLength,
   OUT PULONG            BytesReturned
);
static VOID            PrivateQueueReceivePacket
(
   IN  PADAPTER_CONTEXT      AdapterContext,
   IN  PNDIS_PACKET        RecvPacket
);
static VOID            PrivateReferenceCount
(
   IN  PADAPTER_CONTEXT      AdapterContext
);
static VOID            PrivateSendDereferenceCount
(
   IN  PNDIS_PACKET        Packet
);
static VOID            PrivateSendReferenceCount
(
   IN  PNDIS_PACKET        Packet
);
static VOID            PrivateServiceReads
(
   IN  PADAPTER_CONTEXT      AdapterContext
);
static NDIS_STATUS       PrivateSetOidValue
(
   IN  PADAPTER_CONTEXT      AdapterContext,
   OUT PVOID             InputBuffer,
   IN  ULONG             InputBufferLength
);
static VOID            PrivateShutdownBinding
(
   IN  PADAPTER_CONTEXT      AdapterContext
);
static VOID            PrivateSleep
(
   IN  UINT              seconds
);
static BOOLEAN          PrivateValidateOid
(
   IN  NDIS_OID           Oid
);
static NDIS_STATUS       PrivateValidateOpenAndDoRequest
(
   IN  PADAPTER_CONTEXT      AdapterContext,
   IN  NDIS_REQUEST_TYPE     RequestType,
   IN  NDIS_OID           Oid,
   IN  PVOID             InformationBuffer,
   IN  UINT              InformationBufferLength,
   OUT PUINT             BytesProcessed,
   IN  BOOLEAN            WaitForPowerOn
);
static VOID            PrivateWaitForPendingIO
(
   IN  PADAPTER_CONTEXT      AdapterContext,
   IN  BOOLEAN            DoCancelPendingReads
);
static VOID            ProtocolOpenAdapterComplete
(
   IN  NDIS_HANDLE         ProtocolBindingContext,
   IN  NDIS_STATUS         Status,
   IN  NDIS_STATUS         OpenErrorStatus
);
static VOID            ProtocolCloseAdapterComplete
(
   IN  NDIS_HANDLE         ProtocolBindingContext,
   IN  NDIS_STATUS         Status
);
static VOID            ProtocolSendComplete
(
   IN  NDIS_HANDLE         ProtocolBindingContext,
   IN  PNDIS_PACKET        Packet,
   IN  NDIS_STATUS         Status
);
static VOID            ProtocolTransferDataComplete
(
   IN  NDIS_HANDLE         ProtocolBindingContext,
   IN  PNDIS_PACKET        Packet,
   IN  NDIS_STATUS         Status,
   IN  UINT              BytesTransferred
);
static VOID            ProtocolResetComplete
(
   IN  NDIS_HANDLE         ProtocolBindingContext,
   IN  NDIS_STATUS         Status
);
static VOID            ProtocolRequestComplete
(
   IN  NDIS_HANDLE         ProtocolBindingContext,
   IN  PNDIS_REQUEST        NdisRequest,
   IN  NDIS_STATUS         Status
);
static NDIS_STATUS       ProtocolReceive
(
   IN  NDIS_HANDLE         ProtocolBindingContext,
   IN  NDIS_HANDLE         MacReceiveContext,
   IN  PVOID             HeaderBuffer,
   IN  UINT              HeaderBufferSize,
   IN  PVOID             LookAheadBuffer,
   IN  UINT              LookaheadBufferSize,
   IN  UINT              PacketSize
);
static VOID            ProtocolReceiveComplete
(
   IN  NDIS_HANDLE         ProtocolBindingContext
);
static VOID            ProtocolStatus
(
   IN  NDIS_HANDLE         ProtocolBindingContext,
   IN  NDIS_STATUS         GeneralStatus,
   IN  PVOID             StatusBuffer,
   IN  UINT              StatusBufferSize
);
static VOID            ProtocolStatusComplete
(
   IN  NDIS_HANDLE         ProtocolBindingContext
);
static INT            ProtocolReceivePacket
(
   IN  NDIS_HANDLE         ProtocolBindingContext,
   IN  PNDIS_PACKET        Packet
);
static VOID            ProtocolBindAdapter
(
   OUT PNDIS_STATUS        Status,
   IN  NDIS_HANDLE         BindContext,
   IN  PNDIS_STRING        DeviceName,
   IN  PVOID             SystemSpecific1,
   IN  PVOID             SystemSpecific2
);
static VOID            ProtocolUnbindAdapter
(
   OUT PNDIS_STATUS        Status,
   IN  NDIS_HANDLE         ProtocolBindingContext,
   IN  NDIS_HANDLE         UnbindContext
);
static NDIS_STATUS       ProtocolPnPEvent
(
   IN  NDIS_HANDLE         ProtocolBindingContext,
   IN  PNET_PNP_EVENT       NetPnPEvent
);
static VOID            ProtocolUnload
(
      VOID
);
     NTSTATUS         DriverEntry
(
   IN  PDRIVER_OBJECT       DriverObject,
   IN  PUNICODE_STRING      RegistryPath
);

#ifdef ALLOC_PRAGMA

#pragma NDIS_INIT_FUNCTION    ( NDISProtocolCreateDevice       )
#pragma NDIS_PAGEABLE_FUNCTION  ( NDISProtocolDeleteDevice       )
#pragma NDIS_PAGEABLE_FUNCTION  ( NDISProtocolDispatch          )
#pragma NDIS_PAGEABLE_FUNCTION  ( NDISProtocolDispatchRead       )
#pragma NDIS_PAGEABLE_FUNCTION  ( NDISProtocolDispatchWrite      )
#pragma NDIS_PAGEABLE_FUNCTION  ( NDISProtocolDispatchCleanup     )
#pragma NDIS_PAGEABLE_FUNCTION  ( NDISProtocolDispatchDeviceControl )
#pragma NDIS_PAGEABLE_FUNCTION  ( NDISProtocolDriverUnload       )
#pragma NDIS_INIT_FUNCTION    ( DriverEntry                )

#endif

/************************************************************************
*                                               *
*                   Static Global Var                 *
*                                               *
************************************************************************/

static ULONG           PrivateFlags      = PRIVATEFLAGS_DEFAULT;
static unsigned short int  EtherType        = ETH_P_DEFAULT;
static NDIS_HANDLE       NdisProtocolHandle  = ( NDIS_HANDLE )NULL;
static LIST_ENTRY        AdapterContextList;
static NDIS_SPIN_LOCK     Lock;
static NDIS_EVENT        BindsComplete;

#ifdef NDIS51
static ULONG           HighCancelId;
static ULONG           LowCancelId      = 0;
#endif

/************************************************************************/

static NTSTATUS NDISProtocolCreateDevice
(
   IN  PDRIVER_OBJECT  DriverObject,
   IN  ULONG        DeviceNumber
)
{
   NTSTATUS        status       = STATUS_SUCCESS;
   PDEVICE_OBJECT    DeviceObject   = NULL;
   PDEVICE_EXTENSION  DeviceExtension;
   UNICODE_STRING    DeviceName;
   UNICODE_STRING    SymbolicLinkName;
   SIZE_T          InternalNameLen;
   SIZE_T          ExternalNameLen;
   PWSTR          InternalName   = NULL;
   PWSTR          ExternalName   = NULL;

   KdPrint(( "Entering NDISProtocolCreateDevice()\n" ));
   InternalNameLen            = sizeof( INTERNALNAME ) + 10 * sizeof( WCHAR );
   InternalName              = ( PWSTR )ExAllocatePoolWithTag
   (
      PagedPool,
      InternalNameLen,
      PRIVATETAG
   );
   if ( NULL == InternalName )
   {
      status  = STATUS_INSUFFICIENT_RESOURCES;
      goto NDISProtocolCreateDevice_exit;
   }
   swprintf( InternalName, L"%s%u", INTERNALNAME, DeviceNumber );
   RtlInitUnicodeString( &DeviceName, InternalName );
   status                  = IoCreateDevice
   (
      DriverObject,
      sizeof( DEVICE_EXTENSION ),
      &DeviceName,
      FILE_DEVICE_NETWORK,
      FILE_DEVICE_SECURE_OPEN,
      FALSE,
      &DeviceObject
   );
   if ( !NT_SUCCESS( status ) )
   {
      goto NDISProtocolCreateDevice_exit;
   }
   DeviceObject->Flags        |= DO_DIRECT_IO;
   DeviceExtension            = ( PDEVICE_EXTENSION )DeviceObject->DeviceExtension;
   DeviceExtension->DeviceObject  = DeviceObject;
   DeviceExtension->DeviceNumber  = DeviceNumber;
   ExternalNameLen            = sizeof( EXTERNALNAME ) + 10 * sizeof( WCHAR );
   ExternalName              = ( PWSTR )ExAllocatePoolWithTag
   (
      PagedPool,
      ExternalNameLen,
      PRIVATETAG
   );
   if ( NULL == ExternalName )
   {
      status  = STATUS_INSUFFICIENT_RESOURCES;
      goto NDISProtocolCreateDevice_exit;
   }
   swprintf( ExternalName, L"%s%u", EXTERNALNAME, DeviceNumber + 1 );
   RtlInitUnicodeString( &SymbolicLinkName, ExternalName );
   status                  = IoCreateSymbolicLink
   (
      &SymbolicLinkName,
      &DeviceName
   );

NDISProtocolCreateDevice_exit:

   if ( NULL != ExternalName )
   {
      ExFreePoolWithTag
      (
        ExternalName,
        PRIVATETAG
      );
      ExternalName   = NULL;
   }
   if
   (
      ( !NT_SUCCESS( status ) ) &&
      ( NULL != DeviceObject )
   )
   {
      IoDeleteDevice( DeviceObject );
      DeviceObject   = NULL;
   }
   if ( NULL != InternalName )
   {
      ExFreePoolWithTag
      (
        InternalName,
        PRIVATETAG
      );
      InternalName   = NULL;
   }
   return( status );
}  /* end of NDISProtocolCreateDevice */

static VOID NDISProtocolDeleteDevice
(
   IN  PDEVICE_OBJECT  DeviceObject
)
{
   NTSTATUS        status;
   PDEVICE_EXTENSION  DeviceExtension;
   UNICODE_STRING    SymbolicLinkName;
   SIZE_T          ExternalNameLen;
   PWSTR          ExternalName   = NULL;

   KdPrint(( "Entering NDISProtocolDeleteDevice()\n" ));
   DeviceExtension = ( PDEVICE_EXTENSION )DeviceObject->DeviceExtension;
   ExternalNameLen = sizeof( EXTERNALNAME ) + 10 * sizeof( WCHAR );
   ExternalName   = ( PWSTR )ExAllocatePoolWithTag
   (
      PagedPool,
      ExternalNameLen,
      PRIVATETAG
   );
   if ( NULL == ExternalName )
   {
      status  = STATUS_INSUFFICIENT_RESOURCES;
      KdPrint((  "ExAllocatePoolWithTag() for ExternalName failed (0x%08X)\n", status ));
      goto NDISProtocolDeleteDevice_exit;
   }
   swprintf( ExternalName, L"%s%u", EXTERNALNAME, DeviceExtension->DeviceNumber + 1 );
   RtlInitUnicodeString( &SymbolicLinkName, ExternalName );
   status       = IoDeleteSymbolicLink
   (
      &SymbolicLinkName
   );
   if ( !NT_SUCCESS( status ) )
   {
      KdPrint((  "IoDeleteSymbolicLink() failed\n" ));
      return;
   }

NDISProtocolDeleteDevice_exit:

   IoDeleteDevice( DeviceObject );
   if ( NULL != ExternalName )
   {
      ExFreePoolWithTag
      (
        ExternalName,
        PRIVATETAG
      );
      ExternalName   = NULL;
   }
   return;
}  /* end of NDISProtocolDeleteDevice */

static NTSTATUS NDISProtocolDispatch
(
   IN  PDEVICE_OBJECT  DeviceObject,
   IN  PIRP        Irp
)
{
   NTSTATUS        status;
   PIO_STACK_LOCATION  IrpStackLocation;
   ULONG          IoControlCode;
   ULONG          InputBufferLength;
   ULONG          OutputBufferLength;
   ULONG          TransferSize;
   PVOID          TransferBuffer;
   PADAPTER_CONTEXT   AdapterContext;

   KdPrint(( "Entering NDISProtocolDispatch()\n" ));
   status               = STATUS_SUCCESS;
   TransferSize           = 0;
   TransferBuffer          = Irp->AssociatedIrp.SystemBuffer;
   IrpStackLocation        = IoGetCurrentIrpStackLocation( Irp );
   IoControlCode          = IrpStackLocation->Parameters.DeviceIoControl.IoControlCode;
   InputBufferLength        = IrpStackLocation->Parameters.DeviceIoControl.InputBufferLength;
   OutputBufferLength       = IrpStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
   AdapterContext          = IrpStackLocation->FileObject->FsContext;
   switch ( IrpStackLocation->MajorFunction )
   {
   case IRP_MJ_CLOSE:
      KdPrint((  "NDISProtocolDispatch( IRP_MJ_CLOSE )\n" ));
      if ( NULL != AdapterContext )
      {
        PrivateDereferenceCount( AdapterContext );
      }
      IrpStackLocation->FileObject->FsContext = NULL;
      break;
   case IRP_MJ_CREATE:
      KdPrint((  "NDISProtocolDispatch( IRP_MJ_CREATE )\n" ));
      IrpStackLocation->FileObject->FsContext = NULL;
      break;
   default:
      KdPrint((  "NDISProtocolDispatch( 0x%02X )\n", IrpStackLocation->MajorFunction ));
      status  = STATUS_NOT_SUPPORTED;
      break;
   }  /* end of switch */
   Irp->IoStatus.Status      = status;
   Irp->IoStatus.Information  = TransferSize;
   IoCompleteRequest( Irp, IO_NO_INCREMENT );
   return( status );
}  /* end of NDISProtocolDispatch */

static NTSTATUS NDISProtocolDispatchRead
(
   IN  PDEVICE_OBJECT  DeviceObject,
   IN  PIRP        Irp
)
{
   PIO_STACK_LOCATION  IrpStackLocation;
   NTSTATUS        status;
   PADAPTER_CONTEXT   AdapterContext;

   KdPrint(( "Entering NDISProtocolDispatchRead()\n" ));
   IrpStackLocation              = IoGetCurrentIrpStackLocation( Irp );
   AdapterContext               = IrpStackLocation->FileObject->FsContext;
   if ( NULL == AdapterContext )
   {
      status  = STATUS_INVALID_HANDLE;
      goto NDISProtocolDispatchRead_exit;
   }
   if ( NULL == Irp->MdlAddress )
   {
      status  = STATUS_INVALID_PARAMETER;
      goto NDISProtocolDispatchRead_exit;
   }
   if
   (
      NULL == MmGetSystemAddressForMdlSafe
      (
        Irp->MdlAddress,
        NormalPagePriority
      )
   )
   {
      status  = STATUS_INSUFFICIENT_RESOURCES;
      goto NDISProtocolDispatchRead_exit;
   }
   NdisAcquireSpinLock( &AdapterContext->Lock );
   if ( !TEST_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_ACTIVE ) )
   {
      NdisReleaseSpinLock( &AdapterContext->Lock );
      status  = STATUS_INVALID_HANDLE;
      goto NDISProtocolDispatchRead_exit;
   }
   IoMarkIrpPending( Irp );
   status                    = STATUS_PENDING;
   InsertTailList
   (
      &AdapterContext->PendedReads,
      &Irp->Tail.Overlay.ListEntry
   );
   AdapterContext->PendedReadCount++;
   PrivateReferenceCount( AdapterContext );
   Irp->Tail.Overlay.DriverContext[0]  = ( PVOID )AdapterContext;
   IoSetCancelRoutine
   (
      Irp,
      NDISProtocolReadCancel
   );
   NdisReleaseSpinLock( &AdapterContext->Lock );
   PrivateServiceReads( AdapterContext );

NDISProtocolDispatchRead_exit:

   if ( STATUS_PENDING != status )
   {
      Irp->IoStatus.Status      = status;
      Irp->IoStatus.Information  = 0;
      IoCompleteRequest( Irp, IO_NO_INCREMENT );
   }
   return( status );
}  /* end of NDISProtocolDispatchRead */

static VOID NDISProtocolReadCancel
(
   IN  PDEVICE_OBJECT  DeviceObject,
   IN  PIRP        Irp
)
{
   PADAPTER_CONTEXT   AdapterContext;
   PLIST_ENTRY      IrpEntry;
   BOOLEAN         Found;

   KdPrint(( "Entering NDISProtocolReadCancel()\n" ));
   IoReleaseCancelSpinLock( Irp->CancelIrql );
   Found        = FALSE;
   AdapterContext  = ( PADAPTER_CONTEXT )Irp->Tail.Overlay.DriverContext[0];
   NdisAcquireSpinLock( &AdapterContext->Lock );
   for
   (
      IrpEntry   = AdapterContext->PendedReads.Flink;
      IrpEntry  != &AdapterContext->PendedReads;
      IrpEntry   = IrpEntry->Flink
   )
   {
      if ( Irp == CONTAINING_RECORD( IrpEntry, IRP, Tail.Overlay.ListEntry ) )
      {
        RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
        AdapterContext->PendedReadCount--;
        Found  = TRUE;
        break;
      }
   }  /* end of for */
   NdisReleaseSpinLock( &AdapterContext->Lock );
   if ( Found )
   {
      Irp->IoStatus.Status      = STATUS_CANCELLED;
      Irp->IoStatus.Information  = 0;
      IoCompleteRequest( Irp, IO_NO_INCREMENT );
      PrivateDereferenceCount( AdapterContext );
   }
   return;
}  /* end of NDISProtocolReadCancel */

static NTSTATUS NDISProtocolDispatchWrite
(
   IN  PDEVICE_OBJECT  DeviceObject,
   IN  PIRP        Irp
)
{
   PIO_STACK_LOCATION  IrpStackLocation;
   NTSTATUS        status;
   NDIS_STATUS      ndis_status;
   PADAPTER_CONTEXT   AdapterContext;
   PNDIS_PACKET      Packet;
   PNDIS_BUFFER      Buffer;
   struct etherheader *etherh;
   unsigned short int  eth_type;
   ULONG          ByteCount;
#ifdef NDIS51
   ULONG          CancelId;
#endif

   KdPrint(( "Entering NDISProtocolDispatchWrite()\n" ));
   IrpStackLocation              = IoGetCurrentIrpStackLocation( Irp );
   AdapterContext               = IrpStackLocation->FileObject->FsContext;
   Packet                    = NULL;
   if ( NULL == AdapterContext )
   {
      status  = STATUS_INVALID_HANDLE;
      goto NDISProtocolDispatchWrite_exit;
   }
   if ( NULL == Irp->MdlAddress )
   {
      status  = STATUS_INVALID_PARAMETER;
      goto NDISProtocolDispatchWrite_exit;
   }
   etherh                    = MmGetSystemAddressForMdlSafe
   (
      Irp->MdlAddress,
      NormalPagePriority
   );
   if ( NULL == etherh )
   {
      status  = STATUS_INSUFFICIENT_RESOURCES;
      goto NDISProtocolDispatchWrite_exit;
   }
   ByteCount                  = MmGetMdlByteCount( Irp->MdlAddress );
   if ( ByteCount < sizeof( struct etherheader ) )
   {
      status  = STATUS_BUFFER_TOO_SMALL;
      goto NDISProtocolDispatchWrite_exit;
   }
   if ( ByteCount > ( AdapterContext->MaximumFrameSize + sizeof( struct etherheader ) ) )
   {
      status  = STATUS_INVALID_BUFFER_SIZE;
      goto NDISProtocolDispatchWrite_exit;
   }
   eth_type                   = swap_16( etherh->eth_type );
   if
   (
      ( PrivateFlags & PRIVATEFLAGS_ETHERTYPE ) &&
      ( EtherType != eth_type )
   )
   {
      status  = STATUS_INVALID_PARAMETER;
      goto NDISProtocolDispatchWrite_exit;
   }
   NdisAcquireSpinLock( &AdapterContext->Lock );
   if ( !TEST_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_ACTIVE ) )
   {
      NdisReleaseSpinLock( &AdapterContext->Lock );
      status  = STATUS_INVALID_HANDLE;
      goto NDISProtocolDispatchWrite_exit;
   }
   NdisAllocatePacket
   (
      &ndis_status,
      &Packet,
      AdapterContext->SendPacketPool
   );
   if ( NDIS_STATUS_SUCCESS != ndis_status )
   {
      NdisReleaseSpinLock( &AdapterContext->Lock );
      status  = STATUS_INSUFFICIENT_RESOURCES;
      goto NDISProtocolDispatchWrite_exit;
   }
   Buffer                    = Irp->MdlAddress;
   NdisInterlockedIncrement( &AdapterContext->PendedWriteCount );
   PrivateReferenceCount( AdapterContext );
   IoMarkIrpPending( Irp );
   GETREFERENCECOUNT( Packet )      = 1;
#ifdef NDIS51
   CancelId                   = GETCANCELID();
   NDIS_SET_PACKET_CANCEL_ID( Packet, ( PVOID )CancelId );
   Irp->Tail.Overlay.DriverContext[0]  = ( PVOID )AdapterContext;
   Irp->Tail.Overlay.DriverContext[1]  = ( PVOID )Packet;
   InsertTailList
   (
      &AdapterContext->PendedWrites,
      &Irp->Tail.Overlay.ListEntry
   );
   IoSetCancelRoutine( Irp, NDISProtocolWriteCancel );
#endif
   NdisReleaseSpinLock( &AdapterContext->Lock );
   GETIRP( Packet )              = Irp;
   status                    = STATUS_PENDING;
   Buffer->Next                = NULL;
   NdisChainBufferAtFront
   (
      Packet,
      Buffer
   );
   NdisSendPackets
   (
      AdapterContext->NdisBindingHandle,
      &Packet,
      1
   );

NDISProtocolDispatchWrite_exit:

   if ( STATUS_PENDING != status )
   {
      Irp->IoStatus.Status   = status;
      IoCompleteRequest( Irp, IO_NO_INCREMENT );
   }
   return( status );
}  /* end of NDISProtocolDispatchWrite */

#ifdef NDIS51
static VOID NDISProtocolWriteCancel
(
   IN  PDEVICE_OBJECT  DeviceObject,
   IN  PIRP        Irp
)
{
   PADAPTER_CONTEXT   AdapterContext;
   PLIST_ENTRY      IrpEntry;
   PNDIS_PACKET      Packet;

   KdPrint(( "Entering NDISProtocolWriteCancel()\n" ));
   IoReleaseCancelSpinLock( Irp->CancelIrql );
   Packet       = NULL;
   AdapterContext  = ( PADAPTER_CONTEXT )Irp->Tail.Overlay.DriverContext[0];
   NdisAcquireSpinLock( &AdapterContext->Lock );
   for
   (
      IrpEntry   = AdapterContext->PendedWrites.Flink;
      IrpEntry  != &AdapterContext->PendedWrites;
      IrpEntry   = IrpEntry->Flink
   )
   {
      if ( Irp == CONTAINING_RECORD( IrpEntry, IRP, Tail.Overlay.ListEntry ) )
      {
        Packet  = ( PNDIS_PACKET )Irp->Tail.Overlay.DriverContext[1];
        PrivateSendReferenceCount( Packet );
        break;
      }
   }  /* end of for */
   NdisReleaseSpinLock( &AdapterContext->Lock );
   if ( NULL != Packet )
   {
      NdisCancelSendPackets
      (
        AdapterContext->NdisBindingHandle,
        NDIS_GET_PACKET_CANCEL_ID( Packet )
      );
      PrivateSendDereferenceCount( Packet );
   }
   return;
}  /* end of NDISProtocolWriteCancel */
#endif

static NTSTATUS NDISProtocolDispatchCleanup
(
   IN  PDEVICE_OBJECT  DeviceObject,
   IN  PIRP        Irp
)
{
   PADAPTER_CONTEXT   AdapterContext;
   PIO_STACK_LOCATION  IrpStackLocation;
   NTSTATUS        status;
   NDIS_STATUS      ndis_status;
   ULONG          PacketFilter;
   ULONG          BytesProcessed;

   KdPrint(( "Entering NDISProtocolDispatchCleanup()\n" ));
   IrpStackLocation   = IoGetCurrentIrpStackLocation( Irp );
   AdapterContext    = IrpStackLocation->FileObject->FsContext;
   if ( NULL != AdapterContext )
   {
      NdisAcquireSpinLock( &AdapterContext->Lock );
      SET_FLAGS( AdapterContext->Flags, PRIVATE_OPEN_FLAGS, PRIVATE_OPEN_IDLE );
      AdapterContext->FileObject  = NULL;
      NdisReleaseSpinLock( &AdapterContext->Lock );
      PacketFilter           = 0;
      ndis_status            = PrivateValidateOpenAndDoRequest
      (
        AdapterContext,
        NdisRequestSetInformation,
        OID_GEN_CURRENT_PACKET_FILTER,
        &PacketFilter,
        sizeof( PacketFilter ),
        &BytesProcessed,
        FALSE
      );
      if ( NDIS_STATUS_SUCCESS != ndis_status)
      {
        ndis_status = NDIS_STATUS_SUCCESS;
      }
      PrivateCancelPendingReads( AdapterContext );
   }
   status               = STATUS_SUCCESS;
   Irp->IoStatus.Information  = 0;
   Irp->IoStatus.Status      = status;
   IoCompleteRequest( Irp, IO_NO_INCREMENT );
   return( status );
}  /* end of NDISProtocolDispatchCleanup */

static NTSTATUS NDISProtocolDispatchDeviceControl
(
   IN  PDEVICE_OBJECT  DeviceObject,
   IN  PIRP        Irp
)
{
   PADAPTER_CONTEXT   AdapterContext;
   PIO_STACK_LOCATION  IrpStackLocation;
   NDIS_STATUS      ndis_status;
   NTSTATUS        status;
   ULONG          IoControlCode;
   ULONG          InputBufferLength;
   ULONG          OutputBufferLength;
   ULONG          TransferSize;
   PVOID          TransferBuffer;

   KdPrint(( "Entering NDISProtocolDispatchDeviceControl()\n" ));
   TransferBuffer    = Irp->AssociatedIrp.SystemBuffer;
   IrpStackLocation   = IoGetCurrentIrpStackLocation(Irp);
   IoControlCode     = IrpStackLocation->Parameters.DeviceIoControl.IoControlCode;
   InputBufferLength  = IrpStackLocation->Parameters.DeviceIoControl.InputBufferLength;
   OutputBufferLength  = IrpStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
   AdapterContext    = ( PADAPTER_CONTEXT )IrpStackLocation->FileObject->FsContext;
   TransferSize      = 0;
   switch ( IoControlCode )
   {
   case IOCTL_NDISPROTOCOL_BIND_WAIT:
      if ( NdisWaitEvent( &BindsComplete, 5000 ) )
      {
        status                       = STATUS_SUCCESS;
      }
      else
      {
        status                       = STATUS_TIMEOUT;
      }
      break;
   case IOCTL_NDISPROTOCOL_QUERY_BINDING:
      ndis_status = PrivateQueryBinding
      (
        TransferBuffer,
        InputBufferLength,
        OutputBufferLength,
        &TransferSize
      );
      PrivateNdisStatusToNtStatus( ndis_status, &status );
      break;
   case IOCTL_NDISPROTOCOL_OPEN_DEVICE:
      if ( NULL != AdapterContext )
      {
        status                       = STATUS_DEVICE_BUSY;
        break;
      }
      status    = PrivateOpenDevice
      (
        TransferBuffer,
        InputBufferLength,
        IrpStackLocation->FileObject,
        &AdapterContext
      );
      if ( NT_SUCCESS( status ) )
      {
        IrpStackLocation->FileObject->FsContext = ( PVOID )AdapterContext;
      }
      break;
   case IOCTL_NDISPROTOCOL_GET_OID_VALUE:
      if ( NULL != AdapterContext )
      {
        ndis_status                    = PrivateGetOidValue
        (
           AdapterContext,
           TransferBuffer,
           OutputBufferLength,
           &TransferSize
        );
        PrivateNdisStatusToNtStatus( ndis_status, &status );
      }
      else
      {
        status                       = STATUS_DEVICE_NOT_CONNECTED;
      }
      break;
   case IOCTL_NDISPROTOCOL_SET_OID_VALUE:
      if ( NULL != AdapterContext )
      {
        TransferSize                   = InputBufferLength;
        ndis_status                    = PrivateSetOidValue
        (
           AdapterContext,
           TransferBuffer,
           TransferSize
        );
        PrivateNdisStatusToNtStatus( ndis_status, &status );
      }
      else
      {
        status                       = STATUS_DEVICE_NOT_CONNECTED;
      }
      break;
   case IOCTL_NDISPROTOCOL_SET_ETHER_TYPE:
      if ( InputBufferLength < sizeof( EtherType ) )
      {
        status                       = STATUS_BUFFER_TOO_SMALL;
      }
      else
      {
        EtherType                     = *( unsigned short int * )TransferBuffer;
        TransferSize                   = sizeof( unsigned short int );
        status                       = STATUS_SUCCESS;
      }
      break;
   case IOCTL_NDISPROTOCOL_GET_PRIVATEFLAGS:
      if ( OutputBufferLength != sizeof( PrivateFlags ) )
      {
        status                       = STATUS_INVALID_BUFFER_SIZE;
      }
      else
      {
        TransferSize                   = OutputBufferLength;
        RtlCopyMemory
        (
           TransferBuffer,
           &PrivateFlags,
           TransferSize
        );
        status                       = STATUS_SUCCESS;
      }
      break;
   case IOCTL_NDISPROTOCOL_SET_PRIVATEFLAGS:
      if ( InputBufferLength != sizeof( PrivateFlags ) )
      {
        status                       = STATUS_INVALID_BUFFER_SIZE;
      }
      else
      {
        TransferSize                   = InputBufferLength;
        RtlCopyMemory
        (
           &PrivateFlags,
           TransferBuffer,
           TransferSize
        );
        status                       = STATUS_SUCCESS;
      }
      break;
   default:
      status    = STATUS_NOT_SUPPORTED;
      break;
   }  /* end of switch */
   if ( STATUS_PENDING != status )
   {
      Irp->IoStatus.Status      = status;
      Irp->IoStatus.Information  = TransferSize;
      IoCompleteRequest( Irp, IO_NO_INCREMENT );
   }
   return( status );
}  /* end of NDISProtocolDispatchDeviceControl */

static VOID NDISProtocolDriverUnload
(
   IN  PDRIVER_OBJECT  DriverObject
)
{
   PDEVICE_OBJECT    NextDeviceObject;
   PDEVICE_EXTENSION  DeviceExtension;
   NDIS_STATUS      ndis_status;

   KdPrint(( "Entering NDISProtocolDriverUnload()\n" ));
   NextDeviceObject   = DriverObject->DeviceObject;
   while ( NextDeviceObject )
   {
      DeviceExtension    = ( PDEVICE_EXTENSION )NextDeviceObject->DeviceExtension;
      NextDeviceObject   = NextDeviceObject->NextDevice;
      NDISProtocolDeleteDevice( DeviceExtension->DeviceObject );
   }  /* end of while */
   if ( NULL != NdisProtocolHandle )
   {
      NdisDeregisterProtocol
      (
        &ndis_status,
        NdisProtocolHandle
      );
      NdisProtocolHandle  = NULL;
   }
   NdisFreeSpinLock( &Lock );
   return;
}  /* end of NDISProtocolDriverUnload */

static PNDIS_PACKET PrivateAllocateReceivePacket
(
   IN  PADAPTER_CONTEXT   AdapterContext,
   IN  UINT           DataLength,
   OUT PUCHAR         *pData
)
{
   NDIS_STATUS    ndis_status;
   PNDIS_PACKET   Packet;
   PNDIS_BUFFER   Buffer;
   PUCHAR       Data;

   Packet               = NULL;
   Buffer               = NULL;
   Data                = NULL;
   NdisAllocateMemoryWithTag
   (
      &Data,
      DataLength,
      PRIVATETAG
   );
   if ( NULL == Data )
   {
      goto PrivateAllocateReceivePacket_exit;
   }
   NdisAllocateBuffer
   (
      &ndis_status,
      &Buffer,
      AdapterContext->RecvBufferPool,
      Data,
      DataLength
   );
   if ( NDIS_STATUS_SUCCESS != ndis_status )
   {
      goto PrivateAllocateReceivePacket_exit;
   }
   NdisAllocatePacket
   (
      &ndis_status,
      &Packet,
      AdapterContext->RecvPacketPool
   );
   if ( NDIS_STATUS_SUCCESS != ndis_status )
   {
      goto PrivateAllocateReceivePacket_exit;
   }
   NDIS_SET_PACKET_STATUS
   (
      Packet,
      NDIS_STATUS_SUCCESS
   );
   GETBUFFER( Packet )      = NULL;
   NdisChainBufferAtFront
   (
      Packet,
      Buffer
   );
   *pData               = Data;

PrivateAllocateReceivePacket_exit:

   if ( NULL == Packet )
   {
      if ( NULL != Buffer )
      {
        NdisFreeBuffer( Buffer );
        Buffer  = NULL;
      }
      if ( NULL != Data )
      {
        NdisFreeMemory( Data, 0, 0 );
        Data   = NULL;
      }
   }
   return( Packet );
}  /* end of PrivateAllocateReceivePacket */

static VOID PrivateCancelPendingReads
(
   IN  PADAPTER_CONTEXT   AdapterContext
)
{
   PIRP      Irp;
   PLIST_ENTRY IrpEntry;

   PrivateReferenceCount( AdapterContext );
   NdisAcquireSpinLock( &AdapterContext->Lock );
   while ( !IsListEmpty( &AdapterContext->PendedReads ) )
   {
      IrpEntry   = AdapterContext->PendedReads.Flink;
      Irp      = CONTAINING_RECORD( IrpEntry, IRP, Tail.Overlay.ListEntry );
      if ( IoSetCancelRoutine( Irp, NULL ) )
      {
        RemoveEntryList( IrpEntry );
        NdisReleaseSpinLock( &AdapterContext->Lock );
        Irp->IoStatus.Status      = STATUS_CANCELLED;
        Irp->IoStatus.Information  = 0;
        IoCompleteRequest( Irp, IO_NO_INCREMENT );
        PrivateDereferenceCount( AdapterContext );
        NdisAcquireSpinLock( &AdapterContext->Lock );
        AdapterContext->PendedReadCount--;
      }
      else
      {
        NdisReleaseSpinLock( &AdapterContext->Lock );
        PrivateSleep( 1 );
        NdisAcquireSpinLock( &AdapterContext->Lock );
      }
   }  /* end of while */
   NdisReleaseSpinLock( &AdapterContext->Lock );
   PrivateDereferenceCount( AdapterContext );
   return;
}  /* end of PrivateCancelPendingReads */

static NDIS_STATUS PrivateCreateBinding
(
   IN  PADAPTER_CONTEXT   AdapterContext,
   IN  PWSTR          DeviceName,
   IN  ULONG          DeviceNameLength
)
{
   NDIS_STATUS      ndis_status    = NDIS_STATUS_SUCCESS;
   PADAPTER_CONTEXT   TmpAdapterContext;
   BOOLEAN         DoNotDisturb   = FALSE;
   BOOLEAN         BindComplete   = FALSE;
   NDIS_STATUS      OpenErrorStatus;
   UINT           SelectedMediumIndex;
   NDIS_MEDIUM      MediumArray[1]  = { NdisMedium802_3 };
   UINT           BytesProcessed;
   ULONG          MediaConnectStatus;

   KdPrint
   ((
      "Entering PrivateCreateBinding(): %ws\n",
      DeviceName
   ));
   TmpAdapterContext        = PrivateLookupDevice( DeviceName, DeviceNameLength );
   if ( NULL != TmpAdapterContext )
   {
      PrivateDereferenceCount( TmpAdapterContext );
      ndis_status = NDIS_STATUS_FAILURE;
      goto PrivateCreateBinding_exit;
   }
   NdisAcquireSpinLock( &AdapterContext->Lock );
   if
   (
      ( !TEST_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_IDLE ) ) ||
      ( TEST_FLAGS( AdapterContext->Flags, PRIVATE_UNBIND_FLAGS, PRIVATE_UNBIND_RECEIVED ) )
   )
   {
      NdisReleaseSpinLock( &AdapterContext->Lock );
      ndis_status    = NDIS_STATUS_NOT_ACCEPTED;
      DoNotDisturb   = TRUE;
      goto PrivateCreateBinding_exit;
   }
   SET_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_OPENING );
   NdisReleaseSpinLock( &AdapterContext->Lock );
   ndis_status            = NdisAllocateMemoryWithTag
   (
      &AdapterContext->DeviceName.Buffer,
      DeviceNameLength + sizeof( WCHAR ),
      PRIVATETAG
   );
   if ( NDIS_STATUS_SUCCESS != ndis_status )
   {
      ndis_status = NDIS_STATUS_RESOURCES;
      goto PrivateCreateBinding_exit;
   }
   NdisMoveMemory
   (
      AdapterContext->DeviceName.Buffer,
      DeviceName,
      DeviceNameLength
   );
   *( PWCHAR )( ( PUCHAR )AdapterContext->DeviceName.Buffer + DeviceNameLength )
                = L&#39;\0&#39;;
   NdisInitUnicodeString
   (
      &AdapterContext->DeviceName,
      AdapterContext->DeviceName.Buffer
   );
   NdisAllocatePacketPoolEx
   (
      &ndis_status,
      &AdapterContext->SendPacketPool,
      MIN_SEND_PACKET_NUM,
      MAX_SEND_PACKET_NUM - MIN_SEND_PACKET_NUM,
      sizeof( SEND_PACKET_PROTOCOLRESERVED )
   );
   if ( NDIS_STATUS_SUCCESS != ndis_status )
   {
      KdPrint(( "NdisAllocatePacketPoolEx() for SendPacketPool failed\n" ));
      goto PrivateCreateBinding_exit;
   }
   NdisAllocatePacketPoolEx
   (
      &ndis_status,
      &AdapterContext->RecvPacketPool,
      MIN_RECV_PACKET_NUM,
      MAX_RECV_PACKET_NUM - MIN_RECV_PACKET_NUM,
      sizeof( RECV_PACKET_PROTOCOLRESERVED )
   );
   if ( NDIS_STATUS_SUCCESS != ndis_status )
   {
      KdPrint(( "NdisAllocatePacketPoolEx() for RecvPacketPool failed\n" ));
      goto PrivateCreateBinding_exit;
   }
   NdisAllocateBufferPool
   (
      &ndis_status,
      &AdapterContext->RecvBufferPool,
      MAX_RECV_BUFFER_NUM
   );
   if ( NDIS_STATUS_SUCCESS != ndis_status )
   {
      KdPrint(( "NdisAllocateBufferPool() for RecvBufferPool failed\n" ));
      goto PrivateCreateBinding_exit;
   }
   NdisInitializeEvent( &AdapterContext->BindEvent );
   NdisOpenAdapter
   (
      &ndis_status,
      &OpenErrorStatus,
      &AdapterContext->NdisBindingHandle,
      &SelectedMediumIndex,
      MediumArray,
      sizeof( MediumArray ) / sizeof( NDIS_MEDIUM ),
      NdisProtocolHandle,
      ( NDIS_HANDLE )AdapterContext,
      &AdapterContext->DeviceName,
      0,
      NULL
   );
   if ( NDIS_STATUS_PENDING == ndis_status )
   {
      NdisWaitEvent( &AdapterContext->BindEvent, 0 );
      ndis_status = AdapterContext->BindStatus;
   }
   if ( NDIS_STATUS_SUCCESS != ndis_status )
   {
      KdPrint(( "NdisOpenAdapter() failed (0x%08X)\n", ndis_status ));
      goto PrivateCreateBinding_exit;
   }
   BindComplete           = TRUE;
   ( VOID )NdisQueryAdapterInstanceName
   (
      &AdapterContext->AdapterInstanceName,
      AdapterContext->NdisBindingHandle
   );
   ndis_status            = PrivateNdisRequest
   (
      AdapterContext,
      NdisRequestQueryInformation,
      OID_802_3_CURRENT_ADDRESS,
      AdapterContext->CurrentAddress,
      sizeof( AdapterContext->CurrentAddress ),
      &BytesProcessed
   );
   if ( NDIS_STATUS_SUCCESS != ndis_status )
   {
      KdPrint(( "PrivateNdisRequest( OID_802_3_CURRENT_ADDRESS ) failed (0x%08X)\n", ndis_status ));
      goto PrivateCreateBinding_exit;
   }
   KdPrint
   ((
      "OID_802_3_CURRENT_ADDRESS: %02X-%02X-%02X-%02X-%02X-%02X\n",
      AdapterContext->CurrentAddress[0],
      AdapterContext->CurrentAddress[1],
      AdapterContext->CurrentAddress[2],
      AdapterContext->CurrentAddress[3],
      AdapterContext->CurrentAddress[4],
      AdapterContext->CurrentAddress[5]
   ));
   ndis_status            = PrivateNdisRequest
   (
      AdapterContext,
      NdisRequestQueryInformation,
      OID_GEN_MAC_OPTIONS,
      &AdapterContext->MacOptions,
      sizeof( AdapterContext->MacOptions ),
      &BytesProcessed
   );
   if ( NDIS_STATUS_SUCCESS != ndis_status )
   {
      KdPrint(( "PrivateNdisRequest( OID_GEN_MAC_OPTIONS ) failed (0x%08X)\n", ndis_status ));
      goto PrivateCreateBinding_exit;
   }
   ndis_status            = PrivateNdisRequest
   (
      AdapterContext,
      NdisRequestQueryInformation,
      OID_GEN_MAXIMUM_FRAME_SIZE,
      &AdapterContext->MaximumFrameSize,
      sizeof( AdapterContext->MaximumFrameSize ),
      &BytesProcessed
   );
   if ( NDIS_STATUS_SUCCESS != ndis_status )
   {
      KdPrint(( "PrivateNdisRequest( OID_GEN_MAXIMUM_FRAME_SIZE ) failed (0x%08X)\n", ndis_status ));
      goto PrivateCreateBinding_exit;
   }
   ndis_status            = PrivateNdisRequest
   (
      AdapterContext,
      NdisRequestQueryInformation,
      OID_GEN_MEDIA_CONNECT_STATUS,
      &MediaConnectStatus,
      sizeof( MediaConnectStatus ),
      &BytesProcessed
   );
   if ( NDIS_STATUS_SUCCESS != ndis_status )
   {
      KdPrint(( "PrivateNdisRequest( OID_GEN_MEDIA_CONNECT_STATUS ) failed (0x%08X)\n", ndis_status ));
      goto PrivateCreateBinding_exit;
   }
   if ( NdisMediaStateConnected == MediaConnectStatus )
   {
      SET_FLAGS( AdapterContext->Flags, PRIVATE_MEDIA_FLAGS, PRIVATE_MEDIA_CONNECTED );
   }
   else
   {
      SET_FLAGS( AdapterContext->Flags, PRIVATE_MEDIA_FLAGS, PRIVATE_MEDIA_DISCONNECTED );
   }
   AdapterContext->PowerState  = NetDeviceStateD0;
   NdisAcquireSpinLock( &AdapterContext->Lock );
   SET_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_ACTIVE );
   if ( TEST_FLAGS( AdapterContext->Flags, PRIVATE_UNBIND_FLAGS, PRIVATE_UNBIND_RECEIVED ) )
   {
      ndis_status = NDIS_STATUS_FAILURE;
   }
   NdisReleaseSpinLock( &AdapterContext->Lock );

PrivateCreateBinding_exit:

   if
   (
      ( NDIS_STATUS_SUCCESS != ndis_status ) &&
      ( !DoNotDisturb )
   )
   {
      NdisAcquireSpinLock( &AdapterContext->Lock );
      if ( BindComplete )
      {
        SET_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_ACTIVE );
      }
      else if ( TEST_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_OPENING ) )
      {
        SET_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_FAILED );
      }
      NdisReleaseSpinLock( &AdapterContext->Lock );
      PrivateShutdownBinding( AdapterContext );
   }
   return ( ndis_status );
}  /* end of PrivateCreateBinding */

static VOID PrivateDereferenceCount
(
   IN  PADAPTER_CONTEXT   AdapterContext
)
{
#if 0
   KdPrint
   ((
      "PrivateDereferenceCount: 0x%08X 0x%08X 0x%08X\n",
      AdapterContext,
      &AdapterContext->ReferenceCount,
      AdapterContext->ReferenceCount
   ));
#endif
   if ( 0 == NdisInterlockedDecrement( &AdapterContext->ReferenceCount ) )
   {
      NdisFreeMemory( AdapterContext, 0, 0 );
   }
   return;
}  /* end of PrivateDereferenceCount */

static VOID PrivateFlushReceiveQueue
(
   IN  PADAPTER_CONTEXT   AdapterContext
)
{
   PLIST_ENTRY    RecvPacketEntry;
   PNDIS_PACKET   RecvPacket;

   PrivateReferenceCount( AdapterContext );
   NdisAcquireSpinLock( &AdapterContext->Lock );
   while ( !IsListEmpty( &AdapterContext->RecvPacketQueue ) )
   {
      RecvPacketEntry = AdapterContext->RecvPacketQueue.Flink;
      RemoveEntryList( RecvPacketEntry );
      AdapterContext->RecvPacketCount--;
      NdisReleaseSpinLock( &AdapterContext->Lock );
      RecvPacket    = GETRECVPACKET( RecvPacketEntry );
      PrivateFreeReceivePacket( AdapterContext, RecvPacket );
      PrivateDereferenceCount( AdapterContext );
      NdisAcquireSpinLock( &AdapterContext->Lock );
   }  /* end of while */
   NdisReleaseSpinLock( &AdapterContext->Lock );
   PrivateDereferenceCount( AdapterContext );
   return;
}  /* end of PrivateFlushReceiveQueue */

static VOI