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

时间:2021-07-02 04:00:32
日期: 2004-07-19 16:55
更新: 2004-08-17 16:23
链接: http://www.opencjk.org/~scz/windows/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                                  'OFSN'
#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'/0';
    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 VOID PrivateFreeAdapterContextResources
(
    IN  PADAPTER_CONTEXT    AdapterContext
)
{
    if ( NULL != AdapterContext->SendPacketPool )
    {
        NdisFreePacketPool( AdapterContext->SendPacketPool );
        AdapterContext->SendPacketPool                      = NULL;
    }
    if ( NULL != AdapterContext->RecvPacketPool )
    {
        NdisFreePacketPool( AdapterContext->RecvPacketPool );
        AdapterContext->RecvPacketPool                      = NULL;
    }
    if ( NULL != AdapterContext->RecvBufferPool )
    {
        NdisFreeBufferPool( AdapterContext->RecvBufferPool );
        AdapterContext->RecvBufferPool                      = NULL;
    }
    if ( NULL != AdapterContext->SendBufferPool )
    {
        NdisFreeBufferPool( AdapterContext->SendBufferPool );
        AdapterContext->SendBufferPool                      = NULL;
    }
    if ( NULL != AdapterContext->DeviceName.Buffer )
    {
        NdisFreeMemory( AdapterContext->DeviceName.Buffer, 0, 0 );
        AdapterContext->DeviceName.Buffer                   = NULL;
        AdapterContext->DeviceName.Length                   =
        AdapterContext->DeviceName.MaximumLength            = 0;
    }
    if ( NULL != AdapterContext->AdapterInstanceName.Buffer )
    {
        NdisFreeMemory( AdapterContext->AdapterInstanceName.Buffer, 0, 0 );
        AdapterContext->AdapterInstanceName.Buffer          = NULL;
        AdapterContext->AdapterInstanceName.Length          =
        AdapterContext->AdapterInstanceName.MaximumLength   = 0;
    }
    return;
}  /* end of PrivateFreeAdapterContextResources */

static VOID PrivateFreeReceivePacket
(
    IN  PADAPTER_CONTEXT    AdapterContext,
    IN  PNDIS_PACKET        Packet
)
{
    PNDIS_BUFFER    FirstBuffer;
    UINT            TotalBufferLength;
    UINT            FirstBufferLength;
    PVOID           FirstBufferVA;

    if ( NdisGetPoolFromPacket( Packet ) == AdapterContext->RecvPacketPool )
    {
#ifdef NDIS51
        NdisGetFirstBufferFromPacketSafe
        (
            Packet,
            &FirstBuffer,
            &FirstBufferVA,
            &FirstBufferLength,
            &TotalBufferLength,
            NormalPagePriority
        );
#else
        NdisGetFirstBufferFromPacket
        (
            Packet,
            &FirstBuffer,
            &FirstBufferVA,
            &FirstBufferLength,
            &TotalBufferLength
        );
#endif
        NdisFreePacket( Packet );
        NdisFreeBuffer( FirstBuffer );
        NdisFreeMemory
        (
            FirstBufferVA,
            0,
            0
        );
    }
    else
    {
        NdisReturnPackets
        (
            &Packet,
            1
        );
    }
    return;
}  /* end of PrivateFreeReceivePacket */

static NDIS_STATUS PrivateGetOidValue
(
    IN  PADAPTER_CONTEXT    AdapterContext,
    OUT PVOID               OutputBuffer,
    IN  ULONG               OutputBufferLength,
    OUT PULONG              BytesWritten
)
{
    NDIS_STATUS ndis_status;
    NDIS_OID    Oid;
    POIDVALUE   OidValue;

    KdPrint(( "Entering PrivateGetOidValue()/n" ));
    if ( OutputBufferLength < sizeof( OIDVALUE ) )
    {
        ndis_status = NDIS_STATUS_BUFFER_TOO_SHORT;
        goto PrivateGetOidValue_exit;
    }
    OidValue    = ( POIDVALUE )OutputBuffer;
    Oid         = OidValue->Oid;
    NdisAcquireSpinLock( &AdapterContext->Lock );
    if ( !TEST_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_ACTIVE ) )
    {
        NdisReleaseSpinLock( &AdapterContext->Lock );
        ndis_status = NDIS_STATUS_FAILURE;
        goto PrivateGetOidValue_exit;
    }
    NdisInterlockedIncrement( &AdapterContext->PendedWriteCount );
    NdisReleaseSpinLock( &AdapterContext->Lock );
    ndis_status = PrivateNdisRequest
    (
        AdapterContext,
        NdisRequestQueryInformation,
        Oid,
        OidValue->Value,
        OutputBufferLength - FIELD_OFFSET( OIDVALUE, Value ),
        BytesWritten
    );
    NdisAcquireSpinLock( &AdapterContext->Lock );
    NdisInterlockedDecrement( &AdapterContext->PendedWriteCount );
    NdisReleaseSpinLock( &AdapterContext->Lock );
    if ( NDIS_STATUS_SUCCESS == ndis_status )
    {
        *BytesWritten  += FIELD_OFFSET( OIDVALUE, Value );
    }

PrivateGetOidValue_exit:

    return( ndis_status );
}  /* end of PrivateGetOidValue */

static PADAPTER_CONTEXT PrivateLookupDevice
(
    IN  PWSTR   DeviceName,
    IN  ULONG   DeviceNameLength
)
{
    PADAPTER_CONTEXT    AdapterContext  = NULL;
    PLIST_ENTRY         Link;

    KdPrint(( "Entering PrivateLookupDevice()/n" ));
    NdisAcquireSpinLock( &Lock );
    for ( Link = AdapterContextList.Flink; Link != &AdapterContextList; Link = Link->Flink )
    {
        AdapterContext  = CONTAINING_RECORD( Link, ADAPTER_CONTEXT, Link );
        if
        (
            ( AdapterContext->DeviceName.Length == DeviceNameLength ) &&
            ( 1 == NdisEqualMemory( AdapterContext->DeviceName.Buffer, DeviceName, DeviceNameLength ) )
        )
        {
            PrivateReferenceCount( AdapterContext );
            break;
        }
        AdapterContext  = NULL;
    }  /* end of for */
    NdisReleaseSpinLock( &Lock );
    return( AdapterContext );
}  /* end of PrivateLookupDevice */

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
)
{
    REQUEST_CONTEXT RequestContext;
    PNDIS_REQUEST   Request;
    NDIS_STATUS     ndis_status;

    NdisInitializeEvent( &RequestContext.NdisRequestEvent );
    Request                 = &RequestContext.NdisRequest;
    Request->RequestType    = RequestType;
    switch ( RequestType )
    {
    case NdisRequestQueryInformation:
        Request->DATA.QUERY_INFORMATION.Oid                     = Oid;
        Request->DATA.QUERY_INFORMATION.InformationBuffer       = InformationBuffer;
        Request->DATA.QUERY_INFORMATION.InformationBufferLength = InformationBufferLength;
        break;
    case NdisRequestSetInformation:
        Request->DATA.SET_INFORMATION.Oid                       = Oid;
        Request->DATA.SET_INFORMATION.InformationBuffer         = InformationBuffer;
        Request->DATA.SET_INFORMATION.InformationBufferLength   = InformationBufferLength;
        break;
    case NdisRequestQueryStatistics:
    default:
        break;
    }  /* end of switch */
    NdisRequest
    (
        &ndis_status,
        AdapterContext->NdisBindingHandle,
        Request
    );
    if ( NDIS_STATUS_PENDING == ndis_status )
    {
        NdisWaitEvent( &RequestContext.NdisRequestEvent, 0 );
        ndis_status     = RequestContext.Status;
    }
    if ( NDIS_STATUS_SUCCESS == ndis_status )
    {
        *BytesProcessed =
        ( NdisRequestQueryInformation == RequestType ) ?
        Request->DATA.QUERY_INFORMATION.BytesWritten:
        Request->DATA.SET_INFORMATION.BytesRead;
        if ( *BytesProcessed > InformationBufferLength )
        {
            *BytesProcessed = InformationBufferLength;
        }
    }
    return ( ndis_status );
}  /* end of PrivateNdisRequest */

static VOID PrivateNdisStatusToNtStatus
(
    IN  NDIS_STATUS     NdisStatus,
    IN  NTSTATUS       *NtStatus
)
{
    if
    (
        ( NDIS_STATUS_SUCCESS == NdisStatus ) ||
        ( NDIS_STATUS_PENDING == NdisStatus ) ||
        ( NDIS_STATUS_BUFFER_OVERFLOW == NdisStatus ) ||
        ( NDIS_STATUS_FAILURE == NdisStatus ) ||
        ( NDIS_STATUS_RESOURCES == NdisStatus ) ||
        ( NDIS_STATUS_NOT_SUPPORTED == NdisStatus )
    )
    {
        *NtStatus   = ( NTSTATUS )NdisStatus;
    }
    else if ( NDIS_STATUS_BUFFER_TOO_SHORT == NdisStatus )
    {
        /*
         * The above NDIS status codes require a little special casing.
         */
        *NtStatus   = STATUS_BUFFER_TOO_SMALL;
    }
    else if ( NDIS_STATUS_INVALID_LENGTH == NdisStatus )
    {
        *NtStatus   = STATUS_INVALID_BUFFER_SIZE;
    }
    else if ( NDIS_STATUS_INVALID_DATA == NdisStatus )
    {
        *NtStatus   = STATUS_INVALID_PARAMETER;
    }
    else if ( NDIS_STATUS_ADAPTER_NOT_FOUND == NdisStatus )
    {
        *NtStatus   = STATUS_NO_MORE_ENTRIES;
    }
    else if ( NDIS_STATUS_ADAPTER_NOT_READY == NdisStatus )
    {
        *NtStatus   = STATUS_DEVICE_NOT_READY;
    }
    else
    {
        *NtStatus   = STATUS_UNSUCCESSFUL;
    }
    return;
}  /* end of PrivateNdisStatusToNtStatus */

static NTSTATUS PrivateOpenDevice
(
    IN  PWSTR               DeviceName,
    IN  ULONG               DeviceNameLength,
    IN  PFILE_OBJECT        FileObject,
    OUT PADAPTER_CONTEXT   *pAdapterContext
)
{
    PADAPTER_CONTEXT    AdapterContext;
    NTSTATUS            status;
    ULONG               PacketFilter;
    NDIS_STATUS         ndis_status;
    ULONG               BytesProcessed;

    KdPrint(( "Entering PrivateOpenDevice()/n" ));
    AdapterContext              = NULL;
    AdapterContext              = PrivateLookupDevice
    (
        DeviceName,
        DeviceNameLength
    );
    if ( NULL == AdapterContext )
    {
        status  = STATUS_OBJECT_NAME_NOT_FOUND;
        goto PrivateOpenDevice_exit;
    }
    NdisAcquireSpinLock( &AdapterContext->Lock );
    if ( !TEST_FLAGS( AdapterContext->Flags, PRIVATE_OPEN_FLAGS, PRIVATE_OPEN_IDLE ) )
    {
        NdisReleaseSpinLock( &AdapterContext->Lock );
        PrivateDereferenceCount( AdapterContext );
        status  = STATUS_DEVICE_BUSY;
        goto PrivateOpenDevice_exit;
    }
    AdapterContext->FileObject  = FileObject;
    SET_FLAGS( AdapterContext->Flags, PRIVATE_OPEN_FLAGS, PRIVATE_OPEN_ACTIVE );
    NdisReleaseSpinLock( &AdapterContext->Lock );
    PacketFilter                = DEFAULT_PACKET_FILTER;
    ndis_status                 = PrivateValidateOpenAndDoRequest
    (
        AdapterContext,
        NdisRequestSetInformation,
        OID_GEN_CURRENT_PACKET_FILTER,
        &PacketFilter,
        sizeof( PacketFilter ),
        &BytesProcessed,
        TRUE
    );
    if ( NDIS_STATUS_SUCCESS != ndis_status )
    {
        NdisAcquireSpinLock( &AdapterContext->Lock );
        SET_FLAGS( AdapterContext->Flags, PRIVATE_OPEN_FLAGS, PRIVATE_OPEN_IDLE );
        AdapterContext->FileObject  = NULL;
        NdisReleaseSpinLock( &AdapterContext->Lock );
        PrivateDereferenceCount( AdapterContext );
        PrivateNdisStatusToNtStatus( ndis_status, &status );
        goto PrivateOpenDevice_exit;
    }
    *pAdapterContext            = AdapterContext;
    status                      = STATUS_SUCCESS;

PrivateOpenDevice_exit:

    return( status );
}  /* end of PrivateOpenDevice */

static NDIS_STATUS PrivateQueryBinding
(
    IN  PUCHAR  Buffer,
    IN  ULONG   InputBufferLength,
    IN  ULONG   OutputBufferLength,
    OUT PULONG  BytesReturned
)
{
    PADAPTER_CONTEXT    AdapterContext;
    NDIS_STATUS         ndis_status;
    PBINDINGINFO        BindingInfo;
    PLIST_ENTRY         ListEntry;
    ULONG               Remaining;
    ULONG               BindingIndex;

    KdPrint(( "Entering PrivateQueryBinding()/n" ));
    if ( InputBufferLength < sizeof( BINDINGINFO ) )
    {
        ndis_status = NDIS_STATUS_RESOURCES;
        goto PrivateQueryBinding_exit;
    }
    if ( OutputBufferLength < sizeof( BINDINGINFO ) )
    {
        ndis_status = NDIS_STATUS_BUFFER_OVERFLOW;
        goto PrivateQueryBinding_exit;
    }
    Remaining       = OutputBufferLength - sizeof( BINDINGINFO );
    BindingInfo     = ( PBINDINGINFO )Buffer;
    BindingIndex    = BindingInfo->BindingIndex;
    ndis_status     = NDIS_STATUS_ADAPTER_NOT_FOUND;
    AdapterContext  = NULL;
    NdisAcquireSpinLock( &Lock );
    for
    (
        ListEntry   = AdapterContextList.Flink;
        ListEntry  != &AdapterContextList;
        ListEntry   = ListEntry->Flink
    )
    {
        AdapterContext  = CONTAINING_RECORD( ListEntry, ADAPTER_CONTEXT, Link );
        NdisAcquireSpinLock( &AdapterContext->Lock );
        if ( !TEST_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_ACTIVE ) )
        {
            NdisReleaseSpinLock( &AdapterContext->Lock );
            continue;
        }
        if ( 0 == BindingIndex )
        {
            BindingInfo->DeviceNameLength           = AdapterContext->DeviceName.Length + sizeof( WCHAR );
            BindingInfo->AdapterInstanceNameLength  = AdapterContext->AdapterInstanceName.Length + sizeof( WCHAR );
            if ( Remaining < BindingInfo->DeviceNameLength + BindingInfo->AdapterInstanceNameLength )
            {
                NdisReleaseSpinLock( &AdapterContext->Lock );
                ndis_status = NDIS_STATUS_BUFFER_OVERFLOW;
                break;
            }
            NdisZeroMemory
            (
                ( PUCHAR )Buffer + sizeof( BINDINGINFO ),
                BindingInfo->DeviceNameLength + BindingInfo->AdapterInstanceNameLength
            );
            BindingInfo->DeviceNameOffset           = sizeof( BINDINGINFO );
            NdisMoveMemory
            (
                ( PUCHAR )Buffer + BindingInfo->DeviceNameOffset,
                AdapterContext->DeviceName.Buffer,
                AdapterContext->DeviceName.Length
            );
            BindingInfo->AdapterInstanceNameOffset  =
            BindingInfo->DeviceNameOffset + BindingInfo->DeviceNameLength;
            NdisMoveMemory
            (
                ( PUCHAR )Buffer + BindingInfo->AdapterInstanceNameOffset,
                AdapterContext->AdapterInstanceName.Buffer,
                AdapterContext->AdapterInstanceName.Length
            );
            NdisReleaseSpinLock( &AdapterContext->Lock );
            *BytesReturned                          =
            BindingInfo->AdapterInstanceNameOffset + BindingInfo->AdapterInstanceNameLength;
            ndis_status                             = NDIS_STATUS_SUCCESS;
            break;
        }
        NdisReleaseSpinLock( &AdapterContext->Lock );
        BindingIndex--;
    }  /* end of for */
    NdisReleaseSpinLock( &Lock );

PrivateQueryBinding_exit:

    return( ndis_status );
}  /* end of PrivateQueryBinding */

static VOID PrivateQueueReceivePacket
(
    IN  PADAPTER_CONTEXT    AdapterContext,
    IN  PNDIS_PACKET        RecvPacket
)
{
    PLIST_ENTRY     ListEntry;
    PLIST_ENTRY     DiscardEntry;
    PNDIS_PACKET    DiscardPacket;

    KdPrint(( "Entering PrivateQueueReceivePacket()/n" ));
    ListEntry   = GETLINK( RecvPacket );
    PrivateReferenceCount( AdapterContext );
    NdisAcquireSpinLock( &AdapterContext->Lock );
    if
    (
        TEST_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_ACTIVE ) &&
        ( AdapterContext->PowerState == NetDeviceStateD0 )
    )
    {
        InsertTailList
        (
            &AdapterContext->RecvPacketQueue,
            ListEntry
        );
        AdapterContext->RecvPacketCount++;
    }
    else
    {
        NdisReleaseSpinLock( &AdapterContext->Lock );
        PrivateFreeReceivePacket( AdapterContext, RecvPacket );
        PrivateDereferenceCount( AdapterContext );
        goto PrivateQueueReceivePacket_exit;
    }
    if ( AdapterContext->RecvPacketCount > MAX_RECV_QUEUE_NUM )
    {
        DiscardEntry    = AdapterContext->RecvPacketQueue.Flink;
        RemoveEntryList( DiscardEntry );
        AdapterContext->RecvPacketCount--;
        NdisReleaseSpinLock( &AdapterContext->Lock );
        DiscardPacket   = GETRECVPACKET( DiscardEntry );
        PrivateFreeReceivePacket( AdapterContext, DiscardPacket );
        PrivateDereferenceCount( AdapterContext );
    }
    else
    {
        NdisReleaseSpinLock( &AdapterContext->Lock );
    }
    PrivateServiceReads( AdapterContext );

PrivateQueueReceivePacket_exit:

    return;
}  /* end of PrivateQueueReceivePacket */

static VOID PrivateReferenceCount
(
    IN  PADAPTER_CONTEXT    AdapterContext
)
{
#if 0
    KdPrint
    ((
        "PrivateReferenceCount: 0x%08X 0x%08X 0x%08X/n",
        AdapterContext,
        &AdapterContext->ReferenceCount,
        AdapterContext->ReferenceCount
    ));
#endif
    /*
     * LONG NdisInterlockedIncrement
     * (
     *     IN  PLONG   Addend
     * );
     */
    NdisInterlockedIncrement( &AdapterContext->ReferenceCount );
    return;
}  /* end of PrivateReferenceCount */

static VOID PrivateSendDereferenceCount
(
    IN  PNDIS_PACKET    Packet
)
{
#if 0
    KdPrint
    ((
        "PrivateSendDereferenceCount: 0x%08X 0x%08X 0x%08X/n",
        Packet,
        &GETREFERENCECOUNT( Packet ),
        GETREFERENCECOUNT( Packet )
    ));
#endif
    if ( 0 == NdisInterlockedDecrement( &GETREFERENCECOUNT( Packet ) ) )
    {
        /*
         * Free it.
         */
        NdisFreePacket( Packet );
    }
    return;
}  /* end of PrivateSendDereferenceCount */

static VOID PrivateSendReferenceCount
(
    IN  PNDIS_PACKET    Packet
)
{
#if 0
    KdPrint
    ((
        "PrivateSendReferenceCount: 0x%08X 0x%08X 0x%08X/n",
        Packet,
        &GETREFERENCECOUNT( Packet ),
        GETREFERENCECOUNT( Packet )
    ));
#endif
    NdisInterlockedIncrement( &GETREFERENCECOUNT( Packet ) );
    return;
}  /* end of PrivateSendReferenceCount */

static VOID PrivateServiceReads
(
    IN  PADAPTER_CONTEXT    AdapterContext
)
{
    PIRP            Irp;
    PLIST_ENTRY     IrpEntry;
    PNDIS_PACKET    RecvPacket;
    PLIST_ENTRY     RecvPacketEntry;
    PNDIS_BUFFER    Buffer;
    PUCHAR          src, dst;
    ULONG           srclen, dstlen, len;

    KdPrint(( "Entering PrivateServiceReads()/n" ));
    PrivateReferenceCount( AdapterContext );
    NdisAcquireSpinLock( &AdapterContext->Lock );
    while
    (
        !IsListEmpty( &AdapterContext->PendedReads ) &&
        !IsListEmpty( &AdapterContext->RecvPacketQueue )
    )
    {
        IrpEntry                    = AdapterContext->PendedReads.Flink;
        Irp                         = CONTAINING_RECORD( IrpEntry, IRP, Tail.Overlay.ListEntry );
        if ( IoSetCancelRoutine( Irp, NULL ) )
        {
            RemoveEntryList( IrpEntry );
        }
        else
        {
            continue;
        }
        RecvPacketEntry             = AdapterContext->RecvPacketQueue.Flink;
        RemoveEntryList( RecvPacketEntry );
        AdapterContext->RecvPacketCount--;
        NdisReleaseSpinLock( &AdapterContext->Lock );
        PrivateDereferenceCount( AdapterContext );
        RecvPacket                  = GETRECVPACKET( RecvPacketEntry );
        dst                         = MmGetSystemAddressForMdlSafe
        (
            Irp->MdlAddress,
            NormalPagePriority
        );
        dstlen                      = MmGetMdlByteCount( Irp->MdlAddress );
        Buffer                      = RecvPacket->Private.Head;
        while
        (
            ( 0 != dstlen ) &&
            ( NULL != Buffer )
        )
        {
            NdisQueryBufferSafe
            (
                Buffer,
                &src,
                &srclen,
                NormalPagePriority
            );
            if ( NULL == src )
            {
                break;
            }
            if ( srclen )
            {
                len     = PRIVATEMIN
                (
                    srclen,
                    dstlen
                );
                NdisMoveMemory
                (
                    dst,
                    src,
                    len
                );
                dstlen -= len;
                dst    += len;
            }
            NdisGetNextBuffer
            (
                Buffer,
                &Buffer
            );
        }  /* end of while */
        Irp->IoStatus.Status        = STATUS_SUCCESS;
        Irp->IoStatus.Information   = MmGetMdlByteCount( Irp->MdlAddress ) - dstlen;
        IoCompleteRequest( Irp, IO_NO_INCREMENT );
        PrivateFreeReceivePacket( AdapterContext, RecvPacket );
        PrivateDereferenceCount( AdapterContext );
        NdisAcquireSpinLock( &AdapterContext->Lock );
        AdapterContext->PendedReadCount--;
    }  /* end of while */
    NdisReleaseSpinLock( &AdapterContext->Lock );
    PrivateDereferenceCount( AdapterContext );
    return;
}  /* end of PrivateServiceReads */

static NDIS_STATUS PrivateSetOidValue
(
    IN  PADAPTER_CONTEXT    AdapterContext,
    OUT PVOID               InputBuffer,
    IN  ULONG               InputBufferLength
)
{
    NDIS_STATUS ndis_status;
    NDIS_OID    Oid;
    POIDVALUE   OidValue;
    ULONG       BytesWritten;

    if ( InputBufferLength < sizeof( OIDVALUE ) )
    {
        ndis_status = NDIS_STATUS_BUFFER_TOO_SHORT;
        goto PrivateSetOidValue_exit;
    }
    OidValue    = ( POIDVALUE )InputBuffer;
    Oid         = OidValue->Oid;
    if ( !PrivateValidateOid( Oid ) )
    {
        ndis_status = NDIS_STATUS_INVALID_DATA;
        goto PrivateSetOidValue_exit;
    }
    NdisAcquireSpinLock( &AdapterContext->Lock );
    if ( !TEST_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_ACTIVE ) )
    {
        NdisReleaseSpinLock( &AdapterContext->Lock );
        ndis_status = NDIS_STATUS_FAILURE;
        goto PrivateSetOidValue_exit;
    }
    NdisInterlockedIncrement( &AdapterContext->PendedWriteCount );
    NdisReleaseSpinLock( &AdapterContext->Lock );
    ndis_status = PrivateNdisRequest
    (
        AdapterContext,
        NdisRequestSetInformation,
        Oid,
        OidValue->Value,
        InputBufferLength - FIELD_OFFSET( OIDVALUE, Value ),
        &BytesWritten
    );
    NdisAcquireSpinLock( &AdapterContext->Lock );
    NdisInterlockedDecrement( &AdapterContext->PendedWriteCount );
    NdisReleaseSpinLock( &AdapterContext->Lock );

PrivateSetOidValue_exit:

    return( ndis_status );
}  /* end of PrivateSetOidValue */

static VOID PrivateShutdownBinding
(
    IN  PADAPTER_CONTEXT    AdapterContext
)
{
    NDIS_STATUS ndis_status;
    BOOLEAN     DoBindClosing   = FALSE;

    KdPrint(( "Entering PrivateShutdownBinding()/n" ));
    NdisAcquireSpinLock( &AdapterContext->Lock );
    if ( TEST_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_OPENING ) )
    {
        NdisReleaseSpinLock( &AdapterContext->Lock );
        goto PrivateShutdownBinding_exit;
    }
    if ( TEST_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_ACTIVE ) )
    {
        SET_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_CLOSING );
        DoBindClosing   = TRUE;
    }
    NdisReleaseSpinLock( &AdapterContext->Lock );
    if ( DoBindClosing )
    {
        PrivateWaitForPendingIO( AdapterContext, TRUE );
        PrivateFlushReceiveQueue( AdapterContext );
        NdisInitializeEvent( &AdapterContext->BindEvent );
        NdisCloseAdapter
        (
            &ndis_status,
            AdapterContext->NdisBindingHandle
        );
        if ( NDIS_STATUS_PENDING == ndis_status )
        {
            NdisWaitEvent( &AdapterContext->BindEvent, 0 );
            ndis_status = AdapterContext->BindStatus;
        }
        AdapterContext->NdisBindingHandle   = NULL;
        NdisAcquireSpinLock( &AdapterContext->Lock );
        SET_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_IDLE );
        SET_FLAGS( AdapterContext->Flags, PRIVATE_UNBIND_FLAGS, 0 );
        NdisReleaseSpinLock( &AdapterContext->Lock );
    }
    NdisAcquireSpinLock( &Lock );
    RemoveEntryList( &AdapterContext->Link );
    NdisReleaseSpinLock( &Lock );
    NdisFreeSpinLock( &AdapterContext->Lock );
    PrivateFreeAdapterContextResources( AdapterContext );
    PrivateDereferenceCount( AdapterContext );

PrivateShutdownBinding_exit:

    return;
}  /* end of PrivateShutdownBinding */

static VOID PrivateSleep
(
    IN  UINT    seconds
)
{
    NDIS_EVENT  SleepEvent;

    KdPrint(( "Entering PrivateSleep()/n" ));
    NdisInitializeEvent( &SleepEvent );
    ( VOID )NdisWaitEvent( &SleepEvent, seconds * 1000 );
    return;
}  /* end of PrivateSleep */

static BOOLEAN PrivateValidateOid
(
    IN  NDIS_OID    Oid
)
{
    KdPrint(( "Entering PrivateValidateOid()/n" ));
    return( TRUE );
}  /* end of PrivateValidateOid */

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
)
{
    NDIS_STATUS ndis_status;

    KdPrint(( "Entering PrivateValidateOpenAndDoRequest()/n" ));
    if ( NULL == AdapterContext )
    {
        ndis_status = NDIS_STATUS_INVALID_DATA;
        goto PrivateValidateOpenAndDoRequest_exit;
    }
    NdisAcquireSpinLock( &AdapterContext->Lock );
    if ( !TEST_FLAGS( AdapterContext->Flags, PRIVATE_BIND_FLAGS, PRIVATE_BIND_ACTIVE ) )
    {
        NdisReleaseSpinLock( &AdapterContext->Lock );
        ndis_status = NDIS_STATUS_INVALID_DATA;
        goto PrivateValidateOpenAndDoRequest_exit;
    }
    NdisInterlockedIncrement( &AdapterContext->PendedWriteCount );
    NdisReleaseSpinLock( &AdapterContext->Lock );
    if ( WaitForPowerOn )
    {
        NdisWaitEvent( &AdapterContext->PoweredUpEvent, 4500 );
    }
    ndis_status = PrivateNdisRequest
    (
        AdapterContext,
        RequestType,
        Oid,
        InformationBuffer,
        InformationBufferLength,
        BytesProcessed
    );
    NdisInterlockedDecrement( &AdapterContext->PendedWriteCount );

PrivateValidateOpenAndDoRequest_exit:

    return( ndis_status );
}  /* end of PrivateValidateOpenAndDoRequest */

static VOID PrivateWaitForPendingIO
(
    IN  PADAPTER_CONTEXT    AdapterContext,
    IN  BOOLEAN             DoCancelPendingReads
)
{
    NDIS_STATUS ndis_status;
    ULONG       i;
    ULONG       IoCount;

    KdPrint(( "Entering PrivateWaitForPendingIO()/n" ));
#ifdef NDIS51
    for ( i = 0; i < 60; i++ )
    {
        /*
         * NDIS_STATUS NdisQueryPendingIoCount
         * (
         *     IN  PVOID   NdisBindingHandle
         *     OUT PULONG  IoCount
         * );
         *
         * System support for NdisQueryPendingIoCount is available in
         * Windows XP and later OS versions.
         */
        ndis_status = NdisQueryPendingIOCount
        (
            AdapterContext->NdisBindingHandle,
            &IoCount
        );
        if
        (
            ( NDIS_STATUS_SUCCESS != ndis_status ) ||
            ( 0 == IoCount )
        )
        {
            break;
        }
        PrivateSleep( 2 );
    }  /* end of for */
#endif
    for ( i = 0; i < 60; i++ )
    {
        if ( 0 == AdapterContext->PendedWriteCount )
        {
            break;
        }
        PrivateSleep( 1 );
    }  /* end of for */
    if ( DoCancelPendingReads )
    {
        while ( 0 != AdapterContext->PendedReadCount )
        {
            PrivateCancelPendingReads( AdapterContext );
            PrivateSleep( 1 );
        }
    }
    KdPrint(( "Exiting PrivateWaitForPendingIO()/n" ));
    return;
}  /* end of PrivateWaitForPendingIO */

static VOID ProtocolOpenAdapterComplete
(
    IN  NDIS_HANDLE ProtocolBindingContext,
    IN  NDIS_STATUS Status,
    IN  NDIS_STATUS OpenErrorStatus
)
{
    PADAPTER_CONTEXT    AdapterContext;

    KdPrint(( "Entering ProtocolOpenAdapterComplete()/n" ));
    AdapterContext              = ( PADAPTER_CONTEXT )ProtocolBindingContext;
    AdapterContext->BindStatus  = Status;
    NdisSetEvent( &AdapterContext->BindEvent );
    return;
}  /* end of ProtocolOpenAdapterComplete */

static VOID ProtocolCloseAdapterComplete
(
    IN  NDIS_HANDLE ProtocolBindingContext,
    IN  NDIS_STATUS Status
)
{
    PADAPTER_CONTEXT    AdapterContext;

    KdPrint(( "Entering ProtocolCloseAdapterComplete()/n" ));
    AdapterContext              = ( PADAPTER_CONTEXT )ProtocolBindingContext;
    AdapterContext->BindStatus  = Status;
    NdisSetEvent( &AdapterContext->BindEvent );
    return;
}  /* end of ProtocolCloseAdapterComplete */

static VOID ProtocolSendComplete
(
    IN  NDIS_HANDLE     ProtocolBindingContext,
    IN  PNDIS_PACKET    Packet,
    IN  NDIS_STATUS     Status
)
{
    PIRP                Irp;
    PIO_STACK_LOCATION  IrpStackLocation;
    PADAPTER_CONTEXT    AdapterContext;

    KdPrint(( "Entering ProtocolSendComplete()/n" ));
    AdapterContext      = ( PADAPTER_CONTEXT )ProtocolBindingContext;
    Irp                 = GETIRP( Packet );
#ifdef NDIS51
    IoSetCancelRoutine( Irp, NULL );
    NdisAcquireSpinLock( &AdapterContext->Lock );
    RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
    NdisReleaseSpinLock( &AdapterContext->Lock );
#endif
    PrivateSendDereferenceCount( Packet );
    IrpStackLocation    = IoGetCurrentIrpStackLocation( Irp );
    if ( NDIS_STATUS_SUCCESS == Status )
    {
        Irp->IoStatus.Status        = STATUS_SUCCESS;
        Irp->IoStatus.Information   = IrpStackLocation->Parameters.Write.Length;
    }
    else
    {
        Irp->IoStatus.Status        = STATUS_UNSUCCESSFUL;
        Irp->IoStatus.Information   = 0;
    }
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    NdisInterlockedDecrement( &AdapterContext->PendedWriteCount );
    PrivateDereferenceCount( AdapterContext );
    return;
}  /* end of ProtocolSendComplete */

static VOID ProtocolTransferDataComplete
(
    IN  NDIS_HANDLE     ProtocolBindingContext,
    IN  PNDIS_PACKET    Packet,
    IN  NDIS_STATUS     Status,
    IN  UINT            BytesTransferred
)
{
    PADAPTER_CONTEXT    AdapterContext;
    PNDIS_BUFFER        OriginalBuffer, PartialBuffer;

    KdPrint(( "Entering ProtocolTransferDataComplete()/n" ));
    AdapterContext  = ( PADAPTER_CONTEXT )ProtocolBindingContext;
    OriginalBuffer  = GETBUFFER( Packet );
    if ( NULL != OriginalBuffer )
    {
        NdisUnchainBufferAtFront
        (
            Packet,
            &PartialBuffer
        );
        NdisChainBufferAtBack
        (
            Packet,
            OriginalBuffer
        );
        NdisFreeBuffer( PartialBuffer );
    }
    if ( NDIS_STATUS_SUCCESS == Status )
    {
        PrivateQueueReceivePacket( AdapterContext, Packet );
    }
    else
    {
        PrivateFreeReceivePacket( AdapterContext, Packet );
    }
    return;
}  /* end of ProtocolTransferDataComplete */

static VOID ProtocolResetComplete
(
    IN  NDIS_HANDLE ProtocolBindingContext,
    IN  NDIS_STATUS Status
)
{
    KdPrint(( "Entering ProtocolResetComplete()/n" ));
    return;
}  /* end of ProtocolResetComplete */

static VOID ProtocolRequestComplete
(
    IN  NDIS_HANDLE     ProtocolBindingContext,
    IN  PNDIS_REQUEST   NdisRequest,
    IN  NDIS_STATUS     Status
)
{
    PADAPTER_CONTEXT    AdapterContext;
    PREQUEST_CONTEXT    RequestContext;

    KdPrint(( "Entering ProtocolRequestComplete()/n" ));
    AdapterContext          = ( PADAPTER_CONTEXT )ProtocolBindingContext;
    RequestContext          = CONTAINING_RECORD( NdisRequest, REQUEST_CONTEXT, NdisRequest );
    RequestContext->Status  = Status;
    NdisSetEvent( &RequestContext->NdisRequestEvent );
    return;
}  /* end of ProtocolRequestComplete */

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
)
{
    PADAPTER_CONTEXT    AdapterContext;
    NDIS_STATUS         ndis_status;
    struct etherheader *etherh;
    unsigned short int  eth_type;
    PNDIS_PACKET        RecvPacket;
    PNDIS_BUFFER        OriginalBuffer, PartialBuffer;
    PUCHAR              RecvData;
    UINT                BytesTransferred;

    KdPrint(( "Entering ProtocolReceive
()/n" ));
    AdapterContext  = ( PADAPTER_CONTEXT )ProtocolBindingContext;
    RecvPacket      = NULL;
    RecvData        = NULL;
    ndis_status     = NDIS_STATUS_SUCCESS;
    if ( sizeof( struct etherheader ) != HeaderBufferSize )
    {
        ndis_status = NDIS_STATUS_NOT_ACCEPTED;
        goto ProtocolReceive_exit;
    }
    etherh          = ( struct etherheader * )HeaderBuffer;
    eth_type        = swap_16( etherh->eth_type );
    if ( ETH_P_8021P == eth_type )
    {
        eth_type    = *( unsigned short * )( ( unsigned char * )&etherh->eth_type + 4 );
    }
    if
    (
        ( PrivateFlags & PRIVATEFLAGS_ETHERTYPE ) &&
        ( EtherType != eth_type )
    )
    {
        ndis_status = NDIS_STATUS_NOT_ACCEPTED;
        goto ProtocolReceive_exit;
    }
    RecvPacket      = PrivateAllocateReceivePacket
    (
        AdapterContext,
        HeaderBufferSize + PacketSize,
        &RecvData
    );
    if ( NULL == RecvPacket )
    {
        ndis_status = NDIS_STATUS_NOT_ACCEPTED;
        goto ProtocolReceive_exit;
    }
    NdisMoveMappedMemory
    (
        RecvData,
        HeaderBuffer,
        HeaderBufferSize
    );
    if( AdapterContext->MacOptions & NDIS_MAC_OPTION_TRANSFERS_NOT_PEND )
    {
        LookaheadBufferSize = PacketSize;
    }
    if ( PacketSize == LookaheadBufferSize )
    {
        NdisCopyLookaheadData
        (
            RecvData + HeaderBufferSize,
            LookAheadBuffer,
            LookaheadBufferSize,
            AdapterContext->MacOptions
        );
        PrivateQueueReceivePacket( AdapterContext, RecvPacket );
    }
    else
    {
        NdisAllocateBuffer
        (
            &ndis_status,
            &PartialBuffer,
            AdapterContext->RecvBufferPool,
            RecvData + HeaderBufferSize,
            PacketSize
        );
        if ( NDIS_STATUS_SUCCESS == ndis_status )
        {
            NdisUnchainBufferAtFront
            (
                RecvPacket,
                &OriginalBuffer
            );
            GETBUFFER( RecvPacket ) = OriginalBuffer;
            NdisChainBufferAtBack
            (
                RecvPacket,
                PartialBuffer
            );
            NdisTransferData
            (
                &ndis_status,
                AdapterContext->NdisBindingHandle,
                MacReceiveContext,
                0,
                PacketSize,
                RecvPacket,
                &BytesTransferred
            );
        }
        else
        {
            BytesTransferred    = 0;
        }
        if ( NDIS_STATUS_PENDING != ndis_status )
        {
            ProtocolTransferDataComplete
            (
                ( NDIS_HANDLE )AdapterContext,
                RecvPacket,
                ndis_status,
                BytesTransferred
            );
        }
    }

ProtocolReceive_exit:

    return( ndis_status );
}  /* end of ProtocolReceive */

static VOID ProtocolReceiveComplete
(
    IN  NDIS_HANDLE ProtocolBindingContext
)
{
    KdPrint(( "Entering ProtocolReceiveComplete()/n" ));
    return;
}  /* end of ProtocolReceiveComplete */

static VOID ProtocolStatus
(
    IN  NDIS_HANDLE ProtocolBindingContext,
    IN  NDIS_STATUS GeneralStatus,
    IN  PVOID       StatusBuffer,
    IN  UINT        StatusBufferSize
)
{
    PADAPTER_CONTEXT    AdapterContext;

    KdPrint(( "Entering ProtocolStatus()/n" ));
    AdapterContext  = ( PADAPTER_CONTEXT )ProtocolBindingContext;
    NdisAcquireSpinLock( &AdapterContext->Lock );
    if ( NetDeviceStateD0 != AdapterContext->PowerState )
    {
        goto ProtocolStatus_exit;
    }
    switch( GeneralStatus )
    {
    case NDIS_STATUS_RESET_START:
        SET_FLAGS( AdapterContext->Flags, PRIVATE_RESET_FLAGS, PRIVATE_RESET_IN_PROGRESS );
        break;
    case NDIS_STATUS_RESET_END:
        SET_FLAGS( AdapterContext->Flags, PRIVATE_RESET_FLAGS, PRIVATE_NOT_RESETTING );
        break;
    case NDIS_STATUS_MEDIA_CONNECT:
        SET_FLAGS( AdapterContext->Flags, PRIVATE_MEDIA_FLAGS, PRIVATE_MEDIA_CONNECTED );
        break;
    case NDIS_STATUS_MEDIA_DISCONNECT:
        SET_FLAGS( AdapterContext->Flags, PRIVATE_MEDIA_FLAGS, PRIVATE_MEDIA_DISCONNECTED );
        break;
    default:
        break;
    }  /* end of switch */

ProtocolStatus_exit:

    NdisReleaseSpinLock( &AdapterContext->Lock );
    return;
}  /* end of ProtocolStatus */

static VOID ProtocolStatusComplete
(
    IN  NDIS_HANDLE ProtocolBindingContext
)
{
    KdPrint(( "Entering ProtocolStatusComplete()/n" ));
    return;
}  /* end of ProtocolStatusComplete */

static INT ProtocolReceivePacket
(
    IN  NDIS_HANDLE     ProtocolBindingContext,
    IN  PNDIS_PACKET    Packet
)
{
    PADAPTER_CONTEXT        AdapterContext;
    PNDIS_BUFFER            FirstBuffer;
    UINT                    FirstBufferLength;
    UINT                    TotalBufferLength;
    struct etherheader     *etherh;
    unsigned short int      eth_type;
    NDIS_STATUS             ndis_status;
    INT                     ReferenceCount  = 0;
    PNDIS_PACKET            RecvPacket;
    PUCHAR                  RecvData;
    UINT                    BytesCopied;

    KdPrint(( "Entering ProtocolReceivePacket()/n" ));
    AdapterContext  = (PADAPTER_CONTEXT)ProtocolBindingContext;
#ifdef NDIS51
    NdisGetFirstBufferFromPacketSafe
    (
        Packet,
        &FirstBuffer,
        &etherh,
        &FirstBufferLength,
        &TotalBufferLength,
        NormalPagePriority
    );
    if ( NULL == etherh )
    {
        FirstBufferLength   = 0;
    }
#else
    NdisGetFirstBufferFromPacket
    (
        Packet,
        &FirstBuffer,
        &etherh,
        &FirstBufferLength,
        &TotalBufferLength
    );
#endif
    if ( FirstBufferLength < sizeof( struct etherheader ) )
    {
        ndis_status = NDIS_STATUS_NOT_ACCEPTED;
        goto ProtocolReceivePacket_exit;
    }
    eth_type        = swap_16( etherh->eth_type );
    if ( ETH_P_8021P == eth_type )
    {
        eth_type    = *( unsigned short * )( ( unsigned char * )&etherh->eth_type + 4 );
    }
    if
    (
        ( PrivateFlags & PRIVATEFLAGS_ETHERTYPE ) &&
        ( EtherType != eth_type )
    )
    {
        ndis_status = NDIS_STATUS_NOT_ACCEPTED;
        goto ProtocolReceivePacket_exit;
    }
    if ( NDIS_STATUS_RESOURCES == NDIS_GET_PACKET_STATUS( Packet ) )
    {
        RecvPacket  = PrivateAllocateReceivePacket
        (
            AdapterContext,
            TotalBufferLength,
            &RecvData
        );
        if ( NULL == RecvPacket )
        {
            goto ProtocolReceivePacket_exit;
        }
        NdisCopyFromPacketToPacketSafe
        (
            RecvPacket,
            0,
            TotalBufferLength,
            Packet,
            0,
            &BytesCopied,
            NormalPagePriority
        );
        Packet      = RecvPacket;
    }
    else
    {
        ReferenceCount  = 1;
    }
    PrivateQueueReceivePacket( AdapterContext, Packet );

ProtocolReceivePacket_exit:

    return ( ReferenceCount );
}  /* end of ProtocolReceivePacket */

static VOID ProtocolBindAdapter
(
    OUT PNDIS_STATUS    Status,
    IN  NDIS_HANDLE     BindContext,
    IN  PNDIS_STRING    DeviceName,
    IN  PVOID           SystemSpecific1,
    IN  PVOID           SystemSpecific2
)
{
    PADAPTER_CONTEXT    AdapterContext  = NULL;
    NDIS_STATUS         ndis_status;
    NDIS_STATUS         ConfigurationStatus;
    NDIS_HANDLE         ConfigurationHandle;

    KdPrint(( "Entering ProtocolBindAdapter()/n" ));
    ndis_status = NdisAllocateMemoryWithTag
    (
        &AdapterContext,
        sizeof( AdapterContext ),
        PRIVATETAG
    );
    if ( NDIS_STATUS_SUCCESS != ndis_status )
    {
        ndis_status = NDIS_STATUS_RESOURCES;
        goto ProtocolBindAdapter_exit;
    }
    NdisZeroMemory
    (
        AdapterContext,
        sizeof( ADAPTER_CONTEXT )
    );
    NdisAllocateSpinLock( &AdapterContext->Lock );
    InitializeListHead( &AdapterContext->PendedReads );
    InitializeListHead( &AdapterContext->PendedWrites );
    InitializeListHead( &AdapterContext->RecvPacketQueue );
    NdisInitializeEvent( &AdapterContext->PoweredUpEvent );
    NdisSetEvent( &AdapterContext->PoweredUpEvent );
    KdPrint(( "SystemSpecific1: [%wZ]/n", ( PUNICODE_STRING )SystemSpecific1 ));
    NdisOpenProtocolConfiguration
    (
        &ConfigurationStatus,
        &ConfigurationHandle,
        ( PNDIS_STRING )SystemSpecific1
    );
    if ( NDIS_STATUS_SUCCESS == ConfigurationStatus )
    {
        PNDIS_CONFIGURATION_PARAMETER   ParameterValue;
        NDIS_STRING                     Keyword = NDIS_STRING_CONST( "OnlyForSczTest" );

        NdisReadConfiguration
        (
            &ConfigurationStatus,
            &ParameterValue,
            ConfigurationHandle,
            &Keyword,
            NdisParameterString
        );
        if
        (
            ( NDIS_STATUS_SUCCESS == ConfigurationStatus ) &&
            ( NdisParameterString == ParameterValue->ParameterType )
        )
        {
            KdPrint
            ((
                "NdisReadConfiguration(): %wZ",
                &ParameterValue->ParameterData.StringData
            ));
        }
        NdisCloseConfiguration( ConfigurationHandle );
    }
    PrivateReferenceCount( AdapterContext );
    NdisAcquireSpinLock( &Lock );
    InsertTailList
    (
        &AdapterContextList,
        &AdapterContext->Link
    );
    NdisReleaseSpinLock( &Lock );
    ndis_status = PrivateCreateBinding
    (
        AdapterContext,
        DeviceName->Buffer,
        DeviceName->Length
    );

ProtocolBindAdapter_exit:

    *Status     = ndis_status;
    KdPrint(( "Exiting ProtocolBindAdapter() = 0x%08X/n", ndis_status ));
    return;
}  /* end of ProtocolBindAdapter */

static VOID ProtocolUnbindAdapter
(
    OUT PNDIS_STATUS    Status,
    IN  NDIS_HANDLE     ProtocolBindingContext,
    IN  NDIS_HANDLE     UnbindContext
)
{
    PADAPTER_CONTEXT    AdapterContext;

    KdPrint(( "Entering ProtocolUnbindAdapter()/n" ));
    AdapterContext  = ( PADAPTER_CONTEXT )ProtocolBindingContext;
    NdisAcquireSpinLock( &AdapterContext->Lock );
    SET_FLAGS( AdapterContext->Flags, PRIVATE_UNBIND_FLAGS, PRIVATE_UNBIND_RECEIVED );
    NdisSetEvent( &AdapterContext->PoweredUpEvent );
    NdisReleaseSpinLock( &AdapterContext->Lock );
    PrivateShutdownBinding( AdapterContext );
    *Status         = NDIS_STATUS_SUCCESS;
    return;
}  /* end of ProtocolUnbindAdapter */

static NDIS_STATUS ProtocolPnPEvent
(
    IN  NDIS_HANDLE     ProtocolBindingContext,
    IN  PNET_PNP_EVENT  NetPnPEvent
)
{
    PADAPTER_CONTEXT    AdapterContext;
    NDIS_STATUS         ndis_status;

    KdPrint(( "Entering ProtocolPnPEvent()/n" ));
    AdapterContext  = ( PADAPTER_CONTEXT )ProtocolBindingContext;
    switch ( NetPnPEvent->NetEvent )
    {
    case NetEventSetPower:
        AdapterContext->PowerState  = *( PNET_DEVICE_POWER_STATE )NetPnPEvent->Buffer;
        if ( AdapterContext->PowerState > NetDeviceStateD0 )
        {
            NdisInitializeEvent( &AdapterContext->PoweredUpEvent );
            PrivateWaitForPendingIO( AdapterContext, FALSE );
            PrivateFlushReceiveQueue( AdapterContext );
        }
        else
        {
            NdisSetEvent( &AdapterContext->PoweredUpEvent );
        }
        ndis_status                 = NDIS_STATUS_SUCCESS;
        break;
    case NetEventQueryPower:
        ndis_status                 = NDIS_STATUS_SUCCESS;
        break;
    case NetEventBindsComplete:
        NdisSetEvent( &BindsComplete );
        ndis_status                 = NDIS_STATUS_SUCCESS;
        break;
    case NetEventQueryRemoveDevice:
    case NetEventCancelRemoveDevice:
    case NetEventReconfigure:
    case NetEventBindList:
    case NetEventPnPCapabilities:
        ndis_status                 = NDIS_STATUS_SUCCESS;
        break;
    default:
        ndis_status                 = NDIS_STATUS_NOT_SUPPORTED;
        break;
    }  /* end of switch */
    return( ndis_status );
}  /* end of ProtocolPnPEvent */

static VOID ProtocolUnload ( VOID )
{
    KdPrint(( "Entering ProtocolUnload()/n" ));
    return;
}  /* end of ProtocolUnload */

NTSTATUS DriverEntry
(
    IN  PDRIVER_OBJECT  DriverObject,
    IN  PUNICODE_STRING RegistryPath
)
{
    NTSTATUS                        status;
    DWORD                           ExceptionCode;
    DWORD                           i;
    NDIS_PROTOCOL_CHARACTERISTICS   ProtocolCharacteristics;
    NDIS_STATUS                     ndis_status;
    NDIS_STRING                     Name    = NDIS_STRING_CONST( "NDISPROTOCOL" );

    KdPrint(( "Entering DriverEntry()/n" ));
    __try
    {
        KdPrint((  "You should see this message [0]/n" ));
        NdisInitializeEvent( &BindsComplete );
        InitializeListHead( &AdapterContextList );
        NdisAllocateSpinLock( &Lock );
        NdisZeroMemory
        (
            &ProtocolCharacteristics,
            sizeof( ProtocolCharacteristics )
        );
        ProtocolCharacteristics.MajorNdisVersion                = 5;
        ProtocolCharacteristics.MinorNdisVersion                = 0;
        ProtocolCharacteristics.OpenAdapterCompleteHandler      = ProtocolOpenAdapterComplete;
        ProtocolCharacteristics.CloseAdapterCompleteHandler     = ProtocolCloseAdapterComplete;
        ProtocolCharacteristics.SendCompleteHandler             = ProtocolSendComplete;
        ProtocolCharacteristics.TransferDataCompleteHandler     = ProtocolTransferDataComplete;
        ProtocolCharacteristics.ResetCompleteHandler            = ProtocolResetComplete;
        ProtocolCharacteristics.RequestCompleteHandler          = ProtocolRequestComplete;
        ProtocolCharacteristics.ReceiveHandler                  = ProtocolReceive;
        ProtocolCharacteristics.ReceiveCompleteHandler          = ProtocolReceiveComplete;
        ProtocolCharacteristics.StatusHandler                   = ProtocolStatus;
        ProtocolCharacteristics.StatusCompleteHandler           = ProtocolStatusComplete;
        ProtocolCharacteristics.Name                            = Name;
        ProtocolCharacteristics.ReceivePacketHandler            = ProtocolReceivePacket;
        ProtocolCharacteristics.BindAdapterHandler              = ProtocolBindAdapter;
        ProtocolCharacteristics.UnbindAdapterHandler            = ProtocolUnbindAdapter;
        ProtocolCharacteristics.PnPEventHandler                 = ProtocolPnPEvent;
        ProtocolCharacteristics.UnloadHandler                   = ProtocolUnload;
        NdisRegisterProtocol
        (
            &ndis_status,
            &NdisProtocolHandle,
            &ProtocolCharacteristics,
            sizeof( ProtocolCharacteristics )
        );
        switch ( ndis_status )
        {
        case NDIS_STATUS_SUCCESS:
            break;
        case NDIS_STATUS_BAD_CHARACTERISTICS:
            KdPrint((  "NdisRegisterProtocol() failed for NDIS_STATUS_BAD_CHARACTERISTICS/n" ));
            status  = STATUS_UNSUCCESSFUL;
            __leave;
        case NDIS_STATUS_BAD_VERSION:
            KdPrint((  "NdisRegisterProtocol() failed for NDIS_STATUS_BAD_VERSION/n" ));
            status  = STATUS_UNSUCCESSFUL;
            __leave;
        case NDIS_STATUS_RESOURCES:
            KdPrint((  "NdisRegisterProtocol() failed for NDIS_STATUS_RESOURCES/n" ));
            status  = STATUS_UNSUCCESSFUL;
            __leave;
        default:
            KdPrint((  "NdisRegisterProtocol() failed for ndis_status(0x%08X)/n", ndis_status ));
            status  = STATUS_UNSUCCESSFUL;
            __leave;
        }
#ifdef NDIS51
        HighCancelId                                            =
        NdisGeneratePartialCancelId() << ( ( sizeof( ULONG ) - 1 ) * 8 );
        KdPrint((  "DriverEntry: HighCancelId(0x%08X)/n", HighCancelId ));
#endif
        DriverObject->DriverUnload                              = NDISProtocolDriverUnload;
        for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++ )
        {
            DriverObject->MajorFunction[i]  = NDISProtocolDispatch;
        }  /* end of for */
        DriverObject->MajorFunction[IRP_MJ_READ             ]   = NDISProtocolDispatchRead;
        DriverObject->MajorFunction[IRP_MJ_WRITE            ]   = NDISProtocolDispatchWrite;
        DriverObject->MajorFunction[IRP_MJ_CLEANUP          ]   = NDISProtocolDispatchCleanup;
        DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL   ]   = NDISProtocolDispatchDeviceControl;
        status                                                  = NDISProtocolCreateDevice
        (
            DriverObject,
            0
        );
        if ( !NT_SUCCESS( status ) )
        {
            if ( NULL != NdisProtocolHandle )
            {
                NdisDeregisterProtocol
                (
                    &ndis_status,
                    NdisProtocolHandle
                );
                NdisProtocolHandle  = NULL;
            }
        }
        KdPrint((  "You should see this message [1]/n" ));
    }
    __except
    (
        /*
         * filter-expression code
         */
        ExceptionCode   = GetExceptionCode(),
        EXCEPTION_EXECUTE_HANDLER
    )
    {
        KdPrint((  "ExceptionCode: 0x%08X/n", ExceptionCode ));
        status  = STATUS_UNSUCCESSFUL;
    }
    KdPrint((  "Exiting DriverEntry()/n" ));
    return( status );
}  /* end of DriverEntry */

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

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

NdisSetPacketPoolProtocolId()第二形参与eth_type有何关系,是一回事吗?

一定要留心KdPrint()出现的场合,否则可能面临BSOD:

> the Unicode format codes (%C, %S, %lc, %ls, %wc, %ws, %wZ) can only be
> used at IRQL PASSIVE_LEVEL.

参看NdisMRegisterDevice()的说明,例子代码ndisuio与文档有出入,并未调用该函
数。难道该函数只用于IMD、Miniport Driver,而不用于协议驱动?

NdisAllocateSpinLock应该与NdisFreeSpinLock()配对出现吧,ndisuio中始终没有
出现NdisFreeSpinLock(),诡异。

NetDeviceStateD0是NET_DEVICE_POWER_STATE型枚举常量,在ndis.h中定义,我未查
到其具体涵义。

[7]介绍了NDIS_PACKET、NDIS_BUFFER之间的关系,很形象,非常不错,推荐阅读。

实现ProtocolReceive()时可能需要参看[8],ndisuio没有考虑[8]中所说情形。

ProtocolReceivePacket()与ProtocolReceive()的关系参看[9]。

[10]介绍了802.1p的帧格式,ndisuio接收报文时考虑了802.1p。

ProtocolBindAdapter()例程的第四形参SystemSpecific1可能如下:

SystemSpecific1: [NDISUIO/Parameters/Adapters/{3F2F7F53-94CC-4C83-B683-4308C573712D}]

之所以说可能如下,是指{3F...2D}对应(虚拟)网卡,非固定值。这个串位于
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services下。缺省没有
Parameters/Adapters/{...},可以手工创建,并用NdisReadConfiguration()读取其
下的键值,已经测试通过。这个与协议驱动实无必然联系,盖因有人曾问起过
NdisReadConfiguration()的第四形参究竟在哪里,就实测了一次。显然这个问题的
答案是驱动相关的,记录于此仅为多一分感性认识。

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

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/NDISUIO/Parameters/Adapters/{3F2F7F53-94CC-4C83-B683-4308C573712D}]
"OnlyForSczTest"="scz is here"
--------------------------------------------------------------------------

从调试过程中看到,先退出ProtocolBindAdapter(),再进入ProtocolPnPEvent()。
如果不通过Network Control Panel Applet (NCPA)安装协议驱动,而简单地在服务
列表中增加一项,则net start NDISProtocol加载协议驱动时,NDIS不会调用
ProtocolBindAdapter(),但仍会调用ProtocolPnPEvent()。

从DDK例子代码看,可以在ProtocolUnload()例程中做DriverUnload()例程所做的事,
但DDK文档未明确表示可以这样做。

2) sources

[11]讨论了NDIS驱动的编译选项,建议阅读。

--------------------------------------------------------------------------
TARGETNAME=ndisprotocol
TARGETPATH=obj
TARGETTYPE=DRIVER
TARGETLIBS=/
$(DDK_LIB_PATH)/ntoskrnl.lib /
$(DDK_LIB_PATH)/ndis.lib
C_DEFINES=$(C_DEFINES) -DNDIS51=1
USE_PDB=1
INCLUDES=
MSC_WARNING_LEVEL=-W3 -WX
SOURCES=ndisprotocol.c
--------------------------------------------------------------------------

3) makefile

--------------------------------------------------------------------------
!INCLUDE $(NTMAKEENV)/makefile.def
--------------------------------------------------------------------------

4) ndisprotocol.inf

; ------------------------------------------------------------------------

;
; Copyright to satisfy the CHKINF utility
;

[Version]
Signature           = "$Windows NT$"
Class               = NetTrans
ClassGuid           = {4D36E975-E325-11CE-BFC1-08002BE10318}
Provider            = %INFCreator%
DriverVer           = 08/10/2004,1.00.1993.9

; ------------------------------------------------------------------------

[Manufacturer]
%INFCreator%        = NDISProtocolSection

; ------------------------------------------------------------------------

[NDISProtocolSection]
%DESCRIPTION%       = DDInstall,nsfocus_ndisprotocol

; ------------------------------------------------------------------------

[SourceDisksNames.x86]
1                   = %DiskDescription%,,,

[SourceDisksFiles.x86]
ndisprotocol.sys    = 1

[DestinationDirs]
DefaultDestDir      = 10,system32/drivers
FileList            = 10,system32/drivers

; ------------------------------------------------------------------------

[DDInstall.NTx86]
AddReg              = DDInstall.NTx86.Reg
Characteristics     = 0x00
Copyfiles           = FileList

[FileList]
ndisprotocol.sys,,,0x00000002

; ------------------------------------------------------------------------

[DDInstall.NTx86.Services]
AddService          = NDISProtocol,,ServiceInstall

[ServiceInstall]
DisplayName         = %FriendlyName%                   ; friendly name
ServiceType         = 0x00000001                       ; SERVICE_KERNEL_DRIVER
StartType           = 0x3                              ; SERVICE_DEMAND_START
ErrorControl        = 0x1                              ; SERVICE_ERROR_NORMAL
ServiceBinary       = %10%/system32/drivers/ndisprotocol.sys
LoadOrderGroup      = NDIS
Description         = %DESCRIPTION%

; ------------------------------------------------------------------------

[DDInstall.NTx86.Remove.Services]
DelService          = NDISProtocol

; ------------------------------------------------------------------------

[DDInstall.NTx86.Reg]
HKR,Ndi,Service,,NDISProtocol
HKR,Ndi,HelpText,,%DESCRIPTION%
HKR,Ndi/Interfaces,UpperRange,,noupper
HKR,Ndi/Interfaces,LowerRange,,"ndis5"

; ------------------------------------------------------------------------

[Strings]
INFCreator          = "The NDISProtocol Software"
DESCRIPTION         = "The NDISProtocol Driver"
DiskDescription     = "The NDISProtocol Software Disk"
FriendlyName        = "NDISProtocol"

; ------------------------------------------------------------------------

编写INF文件时,参看DDK中"Installation Requirements for Network Protocols"。

如果安装失败,请检查%systemroot%/setupapi.log文件。

假设用FastInst.exe或NCPA安装INF文件时碰到如下错误信息:

> Error E000020B in UpdateDriverForPlugAndPlayDevices: ERROR_NO_SUCH_DEVINST

> #E154 类别安装程式失败。 错误 0xe0000208: 操作无法在尚未注册的设备信息元素上运行。

请立即用chkinf.bat(DDK自带工具)检查一下INF文件,此时可能有低级错误出现,比
如我就碰上"AddReg项不在正确的节内"。

HKR可能对应:

HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Network/{4D36E975-E325-11CE-BFC1-08002BE10318}/{03130807-B5F2-47A1-81B0-B870D16F272B}

最后的{03130807-B5F2-47A1-81B0-B870D16F272B}不固定。

调试过程中不要给Characteristics指定NCF_HIDDEN、NCF_NOT_USER_REMOVABLE,这
样可以通过NCPA反安装该协议驱动。

DelService项不是必须的,当安装INF文件失败时系统会用到这一项。

以ndisuio.inf为例:

%NDISUIO_Desc%=Install, MS_NDISUIO

这里的MS_NDISUIO对应INetCfgClassSetup::Install()第一形参,如果二者不匹配,
安装将失败。

b) NDIS协议驱动的用户态测试程序

1) ndisprotocoltest.c

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

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <winioctl.h>
#include <ntddndis.h>

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

#pragma comment( linker, "/INCREMENTAL:NO"    )
#pragma comment( linker, "/subsystem:console" )

#define VERSION                                     "1.00"
#define EXTERNALNAME                                "////.//NDISProtocolExternal1"
#define PRIVATETAG                                  'OFSN'
#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 _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;

#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 WCHAR *  AnsiToUnicode
(
    unsigned char      *str
);
static BOOL     EnablePromiscuousMode
(
    HANDLE              DeviceHandle
);
static void     EnumerateDevices
(
    HANDLE              DeviceHandle
);
static BOOL     GetCurrentAddress
(
    HANDLE              DeviceHandle,
    unsigned char      *CurrentAddress
);
static void     outputBinary
(
    FILE               *out,
    unsigned char      *byteArray,
    size_t              byteArrayLen
);
static void     PrintWin32ErrorCLI
(
    char               *message,
    DWORD               dwMessageId
);
static BOOL     PrivateOpenDevice
(
    HANDLE              DeviceHandle,
    unsigned char      *cardname
);
static void     PrivateRead
(
    HANDLE              DeviceHandle,
    DWORD               TotalNum
);
static void     PrivateWrite
(
    HANDLE              DeviceHandle,
    unsigned char      *sendmac,
    unsigned char      *recvmac,
    unsigned short int  EtherType,
    unsigned char      *data,
    unsigned int        datalen
);
static void     usage
(
    char               *arg
);

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

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

static WCHAR * AnsiToUnicode ( unsigned char *str )
{
    int     i       = 0,
            j       = 0;
    WCHAR  *unistr  = NULL;

    if ( NULL != str )
    {
        j       = strlen( str ) + 1;
        i       = MultiByteToWideChar
        (
            CP_ACP,
            0,
            str,
            j,
            NULL,
            0
        );
        if ( 0 == i )
        {
            PrintWin32ErrorCLI( "MultiByteToWideChar() failed for unistr [0]", GetLastError() );
            goto AnsiToUnicode_exit;
        }
        unistr  = ( WCHAR * )HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, i * sizeof( WCHAR ) );
        if ( NULL == unistr )
        {
            PrintWin32ErrorCLI( "HeapAlloc() failed for unistr", ERROR_NOT_ENOUGH_MEMORY );
            goto AnsiToUnicode_exit;
        }
        if
        (
            0 == MultiByteToWideChar
            (
                CP_ACP,
                0,
                str,
                j,
                unistr,
                i
            )
        )
        {
            PrintWin32ErrorCLI( "MultiByteToWideChar() failed for unistr [1]", GetLastError() );
            goto AnsiToUnicode_exit;
        }
    }

AnsiToUnicode_exit:

    return( unistr );
}  /* end of AnsiToUnicode */

static BOOL EnablePromiscuousMode ( HANDLE DeviceHandle )
{
    BOOL            ret;
    DWORD           BytesReturned;
    unsigned char   buf[sizeof(OIDVALUE)];
    POIDVALUE       OidValue;

    OidValue                    = ( POIDVALUE )buf;
    OidValue->Oid               = OID_GEN_CURRENT_PACKET_FILTER;
    *( PDWORD )&OidValue->Value = NDIS_PACKET_TYPE_PROMISCUOUS;
    ret                         = DeviceIoControl
    (
        DeviceHandle,
        IOCTL_NDISPROTOCOL_SET_OID_VALUE,
        buf,
        sizeof( buf ),
        buf,
        sizeof( buf ),
        &BytesReturned,
        NULL
    );
    if ( FALSE == ret )
    {
        PrintWin32ErrorCLI( "DeviceIoControl( IOCTL_NDISPROTOCOL_SET_OID_VALUE ) for NDIS_PACKET_TYPE_PROMISCUOUS failed", GetLastError() );
    }
    return( ret );
}  /* end of EnablePromiscuousMode */

static void EnumerateDevices ( HANDLE DeviceHandle )
{
    unsigned char  *buf = NULL;
    unsigned int    buflen;
    DWORD           BytesReturned;
    DWORD           error;
    DWORD           i;
    PBINDINGINFO    BindingInfo;
    buflen      = 1024;
    buf         = ( unsigned char * )HeapAlloc
    (
        GetProcessHeap(),
        HEAP_ZERO_MEMORY,
        buflen
    );
    if ( NULL == buf )
    {
        PrintWin32ErrorCLI( "HeapAlloc() failed for buf", ERROR_NOT_ENOUGH_MEMORY );
        goto EnumerateDevices_exit;
    }
    BindingInfo = ( PBINDINGINFO )buf;
    for ( BindingInfo->BindingIndex = i = 0; ; BindingInfo->BindingIndex = ++i )
    {
        if
        (
            FALSE == DeviceIoControl
            (
                DeviceHandle,
                IOCTL_NDISPROTOCOL_QUERY_BINDING,
                BindingInfo,
                sizeof( BINDINGINFO ),
                buf,
                buflen,
                &BytesReturned,
                NULL
            )
        )
        {
            error   = GetLastError();
            if ( ERROR_NO_MORE_ITEMS != error )
            {
                PrintWin32ErrorCLI( "DeviceIoControl( IOCTL_NDISPROTOCOL_QUERY_BINDING ) failed", error );
            }
            else
            {
                printf
                (
                    "0x%08X [EnumerateDevices() finished]/n",
                    i
                );
            }
            goto EnumerateDevices_exit;
        }
        printf
        (
            "0x%08X [%ws]/n"
            "           [%ws]/n",
            BindingInfo->BindingIndex,
            buf + BindingInfo->DeviceNameOffset,
            buf + BindingInfo->AdapterInstanceNameOffset
        );
        ZeroMemory( buf, BytesReturned );
    }  /* end of for */

EnumerateDevices_exit:

    if ( NULL != buf )
    {
        HeapFree( GetProcessHeap(), 0, buf );
        buf = NULL;
    }
    return;
}  /* end of EnumerateDevices */

static BOOL GetCurrentAddress
(
    HANDLE          DeviceHandle,
    unsigned char  *CurrentAddress
)
{
    BOOL            ret;
    DWORD           BytesReturned;
    unsigned char   buf[sizeof(OIDVALUE)+6];
    POIDVALUE       OidValue;

    OidValue        = ( POIDVALUE )buf;
    OidValue->Oid   = OID_802_3_CURRENT_ADDRESS;
    ret             = DeviceIoControl
    (
        DeviceHandle,
        IOCTL_NDISPROTOCOL_GET_OID_VALUE,
        buf,
        sizeof( buf ),
        buf,
        sizeof( buf ),
        &BytesReturned,
        NULL
    );
    if ( FALSE == ret )
    {
        PrintWin32ErrorCLI( "DeviceIoControl( IOCTL_NDISPROTOCOL_GET_OID_VALUE ) for OID_802_3_CURRENT_ADDRESS failed", GetLastError() );
    }
    else
    {
        CopyMemory( CurrentAddress, OidValue->Value, 6 );
    }
    return( ret );
}  /* end of GetCurrentAddress */

static void outputBinary
(
    FILE           *out,
    unsigned char  *byteArray,
    size_t          byteArrayLen
)
{
    size_t  offset, k, j, i;

    fprintf( out, "byteArray [ %u bytes ] ->/n", byteArrayLen );
    if ( byteArrayLen <= 0 )
    {
        return;
    }
    i       = 0;
    offset  = 0;
    for ( k = byteArrayLen / 16; k > 0; k--, offset += 16 )
    {
        fprintf( out, "%08X ", offset );
        for ( j = 0; j < 16; j++, i++ )
        {
            if ( j == 8 )
            {
                fprintf( out, "-%02X", byteArray[i] );
            }
            else
            {
                fprintf( out, " %02X", byteArray[i] );
            }
        }
        fprintf( out, "    " );
        i  -= 16;
        for ( j = 0; j < 16; j++, i++ )
        {
            /*
             * if ( isprint( (int)byteArray[i] ) )
             */
#if 0
            if ( ( byteArray[i] >= ' ' ) && ( byteArray[i] <= 255 ) && ( byteArray[i] != 0x7F ) )
#else
            if ( ( byteArray[i] >= ' ' ) && ( byteArray[i] < 0x7F ) )
#endif
            {
                fprintf( out, "%c", byteArray[i] );
            }
            else
            {
                fprintf( out, "." );
            }
        }
        fprintf( out, "/n" );
    }  /* end of for */
    k       = byteArrayLen - i;
    if ( k <= 0 )
    {
        return;
    }
    fprintf( out, "%08X ", offset );
    for ( j = 0 ; j < k; j++, i++ )
    {
        if ( j == 8 )
        {
            fprintf( out, "-%02X", byteArray[i] );
        }
        else
        {
            fprintf( out, " %02X", byteArray[i] );
        }
    }
    i      -= k;
    for ( j = 16 - k; j > 0; j-- )
    {
        fprintf( out, "   " );
    }
    fprintf( out, "    " );
    for ( j = 0; j < k; j++, i++ )
    {
#if 0
        if ( ( byteArray[i] >= ' ' ) && ( byteArray[i] <= 255 ) && ( byteArray[i] != 0x7F ) )
#else
        if ( ( byteArray[i] >= ' ' ) && ( byteArray[i] < 0x7F ) )
#endif
        {
            fprintf( out, "%c", byteArray[i] );
        }
        else
        {
            fprintf( out, "." );
        }
    }
    fprintf( out, "/n" );
    return;
}  /* end of outputBinary */

static void PrintWin32ErrorCLI ( char *message, DWORD dwMessageId )
{
    char   *errMsg;

    FormatMessage
    (
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        dwMessageId,
        MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
        ( LPTSTR )&errMsg,
        0,
        NULL
    );
    fprintf( stderr, "%s: %s", message, errMsg );
    LocalFree( errMsg );
    return;
}  /* end of PrintWin32ErrorCLI */

static BOOL PrivateOpenDevice
(
    HANDLE          DeviceHandle,
    unsigned char  *cardname
)
{
    BOOL    ret;
    WCHAR  *unicodecardname = NULL;
    DWORD   BytesReturned;

    unicodecardname = AnsiToUnicode( cardname );
    if ( NULL == unicodecardname )
    {
        goto PrivateOpenDevice_exit;
    }
    ret             = DeviceIoControl
    (
        DeviceHandle,
        IOCTL_NDISPROTOCOL_OPEN_DEVICE,
        unicodecardname,
        wcslen( unicodecardname) * sizeof( WCHAR ),
        NULL,
        0,
        &BytesReturned,
        NULL
    );
    if ( FALSE == ret )
    {
        PrintWin32ErrorCLI( "DeviceIoControl( IOCTL_NDISPROTOCOL_OPEN_DEVICE ) failed", GetLastError() );
        goto PrivateOpenDevice_exit;
    }

PrivateOpenDevice_exit:

    if ( NULL != unicodecardname )
    {
        HeapFree( GetProcessHeap(), 0, unicodecardname );
        unicodecardname = NULL;
    }
    return( ret );
}  /* end of PrivateOpenDevice */

static void PrivateRead
(
    HANDLE  DeviceHandle,
    DWORD   TotalNum
)
{
    unsigned char   buf[1514];
    DWORD           NumberOfBytes;
    DWORD           Num = 0;

    while
    (
        ( 0 == TotalNum ) ||
        ( Num < TotalNum )
    )
    {
        Num++;
        if
        (
            FALSE == ReadFile
            (
                DeviceHandle,
                buf,
                sizeof( buf ),
                &NumberOfBytes,
                NULL
            )
        )
        {
            PrintWin32ErrorCLI( "ReadFile() failed", GetLastError() );
            break;
        }
        outputBinary
        (
            stdout,
            buf,
            NumberOfBytes
        );
    }  /* end of while */
    return;
}  /* end of PrivateRead */

static void PrivateWrite
(
    HANDLE              DeviceHandle,
    unsigned char      *sendmac,
    unsigned char      *recvmac,
    unsigned short int  EtherType,
    unsigned char      *data,
    unsigned int        datalen
)
{
    unsigned char  *buf = NULL;
    unsigned int    buflen;
    DWORD           NumberOfBytes;


    buflen  = 14 + datalen;
    buf     = ( unsigned char * )HeapAlloc
    (
        GetProcessHeap(),
        HEAP_ZERO_MEMORY,
        buflen
    );
    if ( NULL == buf )
    {
        PrintWin32ErrorCLI( "HeapAlloc() failed for buf", ERROR_NOT_ENOUGH_MEMORY );
        goto PrivateWrite_exit;
    }
    CopyMemory
    (
        buf,
        recvmac,
        6
    );
    CopyMemory
    (
        buf + 6,
        sendmac,
        6
    );
    CopyMemory
    (
        buf + 12,
        &EtherType,
        2
    );
    CopyMemory
    (
        buf + 14,
        data,
        datalen
    );
    if
    (
        FALSE == WriteFile
        (
            DeviceHandle,
            buf,
            buflen,
            &NumberOfBytes,
            NULL
        )
    )
    {
        PrintWin32ErrorCLI( "WriteFile() failed", GetLastError() );
        goto PrivateWrite_exit;
    }

PrivateWrite_exit:

    if ( NULL != buf )
    {
        HeapFree( GetProcessHeap(), 0, buf );
        buf = NULL;
    }
    return;
}  /* end of PrivateWrite */

static void usage ( char *arg )
{
    fprintf
    (
        stderr,
        "Usage: %s [-h] [-v] [-w] [-e] [-p] [-c cardname]/n"
        "       [-t EtherType] [-f PrivateFlags] [-n TotalNum]/n"
        "       [-s sendmac] [-r recvmac]/n",
        arg
    );
    exit( EXIT_FAILURE );
}  /* end of usage */

int __cdecl main ( int argc, char * argv[] )
{
    int                 c,
                        ret             = EXIT_FAILURE;
    HANDLE              Device          = INVALID_HANDLE_VALUE;
    ULONG               PrivateFlags    = PRIVATEFLAGS_DEFAULT;
    unsigned short int  EtherType       = ETH_P_DEFAULT;
    ULONG               OrigPrivateFlags;
    DWORD               BytesReturned;
    unsigned char      *cardname        = NULL;
    BOOLEAN             DoEnum          = FALSE;
    BOOLEAN             PromiscFlag     = FALSE;
    BOOLEAN             DoWrite         = FALSE;
    unsigned char       mac[6];
    unsigned char       sendmac[6]      =
    {
        'N', 'S', 'N', 'S', 'N', 'S'
    };
    unsigned char       recvmac[6]      =
    {
        'N', 'S', 'N', 'S', 'N', 'S'
    };
    unsigned int        macaddr[6];
    unsigned int        i;
    DWORD               TotalNum        = 1;
    unsigned char       data[]          =
    "Copyleft (c) 2002, 2012/n"
    "The NSFOCUS INFORMATION TECHNOLOGY CO.,LTD./n"
    "NSFocus Security Team <security@nsfocus.com>/n"
    "http://www.nsfocus.com/n"
    "scz <scz@nsfocus.com>/n";

    if ( 1 == argc )
    {
        usage( argv[0] );
    }
    for ( c = 1; c < argc; c++ )
    {
        if ( ( ( argv[c][0] != '-' ) && ( argv[c][0] != '/' ) ) || ( strlen( argv[c] ) < 2 ) )
        {
            usage( argv[0] );
        }
        else
        {
            switch ( tolower( argv[c][1] ) )
            {
            case 'c':
                if ( ( c + 1 ) >= argc )
                {
                    usage( argv[0] );
                }
                cardname        = argv[++c];
                break;
            case 'e':
                DoEnum          = TRUE;
                break;
            case 'f':
                if ( ( c + 1 ) >= argc )
                {
                    usage( argv[0] );
                }
                PrivateFlags    = strtoul( argv[++c], NULL, 0 );
                break;
            case 'n':
                if ( ( c + 1 ) >= argc )
                {
                    usage( argv[0] );
                }
                TotalNum        = strtoul( argv[++c], NULL, 0 );
                break;
            case 'p':
                PromiscFlag     = TRUE;
                break;
            case 'r':
                if ( ( c + 1 ) >= argc )
                {
                    usage( argv[0] );
                }
                if
                (
                    6 != sscanf
                    (
                        argv[++c],
                        "%02x-%02x-%02x-%02x-%02x-%02x",
                        macaddr,
                        macaddr + 1,
                        macaddr + 2,
                        macaddr + 3,
                        macaddr + 4,
                        macaddr + 5
                    )
                )
                {
                    fprintf( stderr, "Checking your [-r recvmac]/n" );
                    goto main_exit;
                }
                for ( i = 0; i < 6; i++ )
                {
                    recvmac[i]  = ( unsigned char )macaddr[i];
                }
                break;
            case 's':
                if ( ( c + 1 ) >= argc )
                {
                    usage( argv[0] );
                }
                if
                (
                    6 != sscanf
                    (
                        argv[++c],
                        "%02x-%02x-%02x-%02x-%02x-%02x",
                        macaddr,
                        macaddr + 1,
                        macaddr + 2,
                        macaddr + 3,
                        macaddr + 4,
                        macaddr + 5
                    )
                )
                {
                    fprintf( stderr, "Checking your [-s sendmac]/n" );
                    goto main_exit;
                }
                for ( i = 0; i < 6; i++ )
                {
                    sendmac[i]  = ( unsigned char )macaddr[i];
                }
                break;
            case 't':
                if ( ( c + 1 ) >= argc )
                {
                    usage( argv[0] );
                }
                EtherType       = ( unsigned short int )strtoul( argv[++c], NULL, 0 );
                break;
            case 'v':
                fprintf( stderr, "%s ver "VERSION"/n", argv[0] );
                return( EXIT_SUCCESS );
            case 'w':
                DoWrite         = TRUE;
                break;
            case 'h':
            case '?':
            default:
                usage( argv[0] );
                break;
            }  /* end of switch */
        }
    }  /* end of for */
    Device  = CreateFile
    (
        EXTERNALNAME,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,           // If lpSecurityAttributes is NULL, the handle cannot be inherited
        OPEN_EXISTING,  // The function fails if the file does not exist
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    if ( INVALID_HANDLE_VALUE == Device )
    {
        PrintWin32ErrorCLI( "CreateFile() failed", GetLastError() );
        goto main_exit;
    }
    if
    (
        FALSE == DeviceIoControl
        (
            Device,
            IOCTL_NDISPROTOCOL_BIND_WAIT,
            NULL,
            0,
            NULL,
            0,
            &BytesReturned,
            NULL
        )
    )
    {
        PrintWin32ErrorCLI( "DeviceIoControl( IOCTL_NDISPROTOCOL_BIND_WAIT ) failed", GetLastError() );
        goto main_exit;
    }
    if ( DoEnum )
    {
        EnumerateDevices( Device );
    }
    else
    {
        if
        (
            FALSE == DeviceIoControl
            (
                Device,
                IOCTL_NDISPROTOCOL_SET_PRIVATEFLAGS,
                &PrivateFlags,
                sizeof( PrivateFlags ),
                NULL,
                0,
                &BytesReturned,
                NULL
            )
        )
        {
            PrintWin32ErrorCLI( "DeviceIoControl( IOCTL_NDISPROTOCOL_SET_PRIVATEFLAGS ) failed", GetLastError() );
            goto main_exit;
        }
        if
        (
            FALSE == DeviceIoControl
            (
                Device,
                IOCTL_NDISPROTOCOL_GET_PRIVATEFLAGS,
                NULL,
                0,
                &PrivateFlags,
                sizeof( PrivateFlags ),
                &BytesReturned,
                NULL
            )
        )
        {
            PrintWin32ErrorCLI( "DeviceIoControl( IOCTL_NDISPROTOCOL_GET_PRIVATEFLAGS ) for PrivateFlags failed", GetLastError() );
            goto main_exit;
        }
        printf( "PrivateFlags        = 0x%08X/n", PrivateFlags );
        if
        (
            FALSE == DeviceIoControl
            (
                Device,
                IOCTL_NDISPROTOCOL_SET_ETHER_TYPE,
                &EtherType,
                sizeof( EtherType ),
                NULL,
                0,
                &BytesReturned,
                NULL
            )
        )
        {
            PrintWin32ErrorCLI( "DeviceIoControl( IOCTL_NDISPROTOCOL_SET_ETHER_TYPE ) failed", GetLastError() );
            goto main_exit;
        }
        if ( NULL == cardname )
        {
            fprintf( stderr, "Checking your [-c cardname]/n" );
            goto main_exit;
        }
        if ( FALSE == PrivateOpenDevice( Device, cardname ) )
        {
            goto main_exit;
        }
        if ( FALSE == GetCurrentAddress( Device, mac ) )
        {
            goto main_exit;
        }
        printf
        (
            "MAC                 = %02X-%02X-%02X-%02X-%02X-%02X/n",
            mac[0],
            mac[1],
            mac[2],
            mac[3],
            mac[4],
            mac[5]
        );
        if ( PromiscFlag )
        {
            EnablePromiscuousMode( Device );
        }
        if ( DoWrite )
        {
            PrivateWrite
            (
                Device,
                sendmac,
                recvmac,
                swap_16( EtherType ),
                data,
                sizeof( data )
            );
        }
        else
        {
            PrivateRead( Device, TotalNum );
        }
    }
    ret     = EXIT_SUCCESS;

main_exit:

    if ( INVALID_HANDLE_VALUE != Device )
    {
        CloseHandle( Device );
        Device  = INVALID_HANDLE_VALUE;
    }
    return( ret );
}  /* end of main */

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

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

如果去掉前述ndisuio的限制,可在用户态测试程序中任意读写链路层数据。

2) sources

--------------------------------------------------------------------------
TARGETNAME=ndisprotocoltest
TARGETPATH=obj
TARGETTYPE=PROGRAM
UMTYPE=console
USE_MSVCRT=1
TARGETLIBS=/
$(SDK_LIB_PATH)/kernel32.lib
C_DEFINES=$(C_DEFINES) -D_WIN32WIN_
USE_PDB=1
INCLUDES=
MSC_WARNING_LEVEL=-W3 -WX
SOURCES=ndisprotocoltest.c
--------------------------------------------------------------------------

3) makefile

--------------------------------------------------------------------------
#
# XP DDK自带了编译器,不再依赖VC
#
!INCLUDE $(NTMAKEENV)/makefile.def
--------------------------------------------------------------------------

c) NDIS组件配置程序

不能用installdriver.c安装NDIS协议驱动,网络组件的安装远比普通KMD的安装来得
复杂。一般是用NCPA安装相应的INF文件,如果想命令行安装,可参看DDK例子代码中
的netcfg(Network Configuration Sample)。netcfg演示了利用INetCfg API枚举、
安装、卸载网络组件。

HRESULT_FROM_WIN32( GetLastError() )的返回值类型是HRESULT,但我没有找到与
之相应的逆函数/宏。这个宏在crt/winerror.h中定义,另有两个用到了的宏也在该
文件中定义:

#define SUCCEEDED(Status)   ((HRESULT)(Status) >= 0)
#define FAILED(Status)      ((HRESULT)(Status)<0)

SetupCopyOEMInf()第三形参如果用SPOST_NONE,将弹出一个对话框,让你选择目录。
从命令行安装的初衷来看,应该指定SPOST_PATH。

如果INetCfgClassSetup::Install()失败,其返回值为0x8004A024,请确认之前是否
已经调用过INetCfgLock::AcquireWriteLock()。我有个疑问,如何将0x8004A024转
换成可阅读的错误信息,这次是HRESULT啊。

d) 源代码目录结构

J:/source/driver/ndisprotocol>tree /f /a
ndisprotocol
|   dirs
|
+---driver
|       makefile
|       ndisprotocol.c
|       ndisprotocol.inf
|       sources
|
+---install
|       makefile
|       ndisconfig.cpp
|       ndisconfig.idl
|       ndisconfig_idl.c
|       sources
|
/---test
        makefile
        ndisprotocoltest.c
        sources

dirs文件内容如下:

--------------------------------------------------------------------------
DIRS=/
driver /
test /
install
--------------------------------------------------------------------------

e) 编译

假设进入了"Win XP Checked Build Environment":

J:/source/driver/ndisprotocol> set DDK_LIB_PATH
DDK_LIB_PATH=J:/WINDDK/2600~1.110/lib/wxp/*
J:/source/driver/ndisprotocol> set BUILD_ALT_DIR
BUILD_ALT_DIR=chk_wxp_x86
J:/source/driver/ndisprotocol> build -cZ -x86
J:/source/driver/ndisprotocol> tree /f /a
ndisprotocol
|   buildchk_wxp_x86.log
|   dirs
|
+---driver
|   |   makefile
|   |   ndisprotocol.c
|   |   ndisprotocol.inf
|   |   sources
|   |
|   /---objchk_wxp_x86
|       |   _objects.mac
|       |
|       /---i386
|               ndisprotocol.obj
|               ndisprotocol.pdb
|               ndisprotocol.sys
|
+---install
|   |   dlldata.c
|   |   makefile
|   |   ndisconfig.cpp
|   |   ndisconfig.h
|   |   ndisconfig.idl
|   |   ndisconfig_i.c
|   |   ndisconfig_idl.c
|   |   ndisconfig_p.c
|   |   sources
|   |
|   /---objchk_wxp_x86
|       |   _objects.mac
|       |
|       /---i386
|               ndisconfig.exe
|               ndisconfig.obj
|               ndisconfig.pdb
|               ndisconfig_idl.obj
|
/---test
    |   makefile
    |   ndisprotocoltest.c
    |   sources
    |
    /---objchk_wxp_x86
        |   _objects.mac
        |
        /---i386
                ndisprotocoltest.exe
                ndisprotocoltest.obj
                ndisprotocoltest.pdb

f) 安装

将ndisprotocol.sys、ndisprotocol.inf、ndisconfig.exe、ndisprotocoltest.exe
四个文件复制到测试机的c:/onlytemp/目录下,执行如下命令进行安装:

c:/onlytemp> ndisconfig.exe -i nsfocus_ndisprotocol -t protocol -f c:/onlytemp/ndisprotocol.inf
c:/onlytemp/ndisprotocol.inf was copied to C:/WINDOWS/INF/oem3.inf
Succeeded

用户界面友好些的话,应该由程序自动分析ndisprotocol.inf,析取出诸如protocol、
nsfocus_ndisprotocol相关信息,但我的目的是了解协议驱动而非解析INF文件,所
以简单处理成靠命令行参数指定相应信息。

如果[-i installid]指定有误的话,%systemroot%/setupapi.log中可看到相应错误
信息。

现在查看%systemroot%/inf目录,多出三个文件,oem3.inf、oem3.pnf、infcache.1。
oem3.inf内容与ndisprotocol.inf完全一样,oem3.pnf是为了提高安装效率而生成的
预编译信息,infcache.1自然是一些cache信息了。

c:/onlytemp> ndisconfig.exe -e protocol
nsfocus_ndisprotocol                     The NDISProtocol Driver
ms_ndisuio                               NDIS Usermode I/O Protocol
ms_pppoe                                 Point to Point Protocol Over Ethernet
ms_pptp                                  Point to Point Tunneling Protocol
ms_l2tp                                  Layer 2 Tunneling Protocol
ms_ndiswan                               Remote Access NDIS WAN Driver
ms_netbt_smb                             Message-oriented TCP/IP Protocol (SMB session)
ms_netbt                                 WINS Client(TCP/IP) Protocol
ms_tcpip                                 Internet Protocol (TCP/IP)

在NCPA中可以看到多出一项"The NDISProtocol Driver"。与此次安装相关的注册表
内容如下:

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

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Network/{4D36E975-E325-11CE-BFC1-08002BE10318}/{DE772806-0150-4F4B-84BC-2C0FDFAF2CA2}]
"Characteristics"=dword:00000000
"InfPath"="oem3.inf"
"InfSection"="DDInstall.NTx86"
"Description"="The NDISProtocol Driver"
"ComponentId"="nsfocus_ndisprotocol"

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Network/{4D36E975-E325-11CE-BFC1-08002BE10318}/{DE772806-0150-4F4B-84BC-2C0FDFAF2CA2}/Ndi]
"Service"="NDISProtocol"
"HelpText"="The NDISProtocol Driver"

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Network/{4D36E975-E325-11CE-BFC1-08002BE10318}/{DE772806-0150-4F4B-84BC-2C0FDFAF2CA2}/Ndi/Interfaces]
"UpperRange"="noupper"
"LowerRange"="ndis5"
--------------------------------------------------------------------------
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/NDISProtocol]
"Type"=dword:00000001
"Start"=dword:00000003
"ErrorControl"=dword:00000001
"Tag"=dword:0000001d
"ImagePath"=hex(2):73,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,64,00,/
  72,00,69,00,76,00,65,00,72,00,73,00,5c,00,6e,00,64,00,69,00,73,00,70,00,72,/
  00,6f,00,74,00,6f,00,63,00,6f,00,6c,00,2e,00,73,00,79,00,73,00,00,00
"DisplayName"="NDISProtocol"
"Group"="NDIS"
"Description"="The NDISProtocol Driver"

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/NDISProtocol/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,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,00,00
"Export"=hex(7):5c,00,44,00,65,00,76,00,69,00,63,00,65,00,5c,00,4e,00,44,00,49,/
  00,53,00,50,00,72,00,6f,00,74,00,6f,00,63,00,6f,00,6c,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,00,/
  00

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/NDISProtocol/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
--------------------------------------------------------------------------

g) 测试

很有可能需要用SoftICE对ndisprotocol.sys进行源码级调试:

net start PrivateExt
nmsym.exe /translate:always,source,package /output:ndisprotocol.nms ndisprotocol.sys
nmsym.exe /symload:ndisprotocol.nms (卸载用nmsym.exe /unload:ndisprotocol.nms)
sync.exe

bpx ndisprotocol!DriverEntry
bpx ndisprotocol!ProtocolBindAdapter

net start ndisprotocol

事实上果然用SoftICE发现一处低级但致命的错误,NdisZeroMemory()第二形参不正
确。现在提供的版本是修正后的。关于BSOD,参看[12]、[13]。

至此,注册表内容有一些增加:

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

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Class/{4D36E972-E325-11CE-BFC1-08002bE10318}/0003/Linkage]
"UpperBind"=hex(7):4e,00,44,00,49,00,53,00,50,00,72,00,6f,00,74,00,6f,00,63,00,/
  6f,00,6c,00,00,00,52,00,61,00,73,00,50,00,70,00,70,00,6f,00,65,00,00,00,4e,/
  00,64,00,69,00,73,00,75,00,69,00,6f,00,00,00,54,00,63,00,70,00,69,00,70,00,/
  00,00,00,00
--------------------------------------------------------------------------
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Enum/Root/LEGACY_NDISPROTOCOL]
"NextInstance"=dword:00000001

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Enum/Root/LEGACY_NDISPROTOCOL/0000]
"Service"="NDISProtocol"
"Legacy"=dword:00000001
"ConfigFlags"=dword:00000000
"Class"="LegacyDriver"
"ClassGUID"="{8ECC055D-047F-11D1-A537-0000F8753ED1}"
"DeviceDesc"="NDISProtocol"

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Enum/Root/LEGACY_NDISPROTOCOL/0000/Control]
"*NewlyCreated*"=dword:00000000
"ActiveService"="NDISProtocol"
--------------------------------------------------------------------------
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/NDISProtocol/Enum]
"0"="Root//LEGACY_NDISPROTOCOL//0000"
"Count"=dword:00000001
"NextInstance"=dword:00000001
--------------------------------------------------------------------------

Usage: ndisprotocoltest.exe [-h] [-v] [-w] [-e] [-p] [-c cardname]
       [-t EtherType] [-f PrivateFlags] [-n TotalNum]
       [-s sendmac] [-r recvmac]

> ndisprotocoltest.exe -e
0x00000000 [/DEVICE/{3F2F7F53-94CC-4C83-B683-4308C573712D}]
           [AMD PCNET Family PCI Ethernet Adapter #2]
0x00000001 [EnumerateDevices() finished]
> ndisprotocoltest.exe -w -c /DEVICE/{3F2F7F53-94CC-4C83-B683-4308C573712D} -s 00-11-00-11-00-11 -r FF-FF-FF-FF-FF-FF -t 0x0806
PrivateFlags        = 0x00000001
MAC                 = 00-50-56-41-C0-A7

用Ethereal从别的机器上抓取上述命令发送到网络上的链路帧,确认发送成功。

> ndisprotocoltest.exe -c /DEVICE/{3F2F7F53-94CC-4C83-B683-4308C573712D} -f 0 -p -n 0
PrivateFlags        = 0x00000000
MAC                 = 00-50-56-41-C0-A7
byteArray [ 173 bytes ] ->
00000000  FF FF FF FF FF FF 00 11-00 11 00 11 08 06 43 6F    ..............Co
00000010  70 79 6C 65 66 74 20 28-63 29 20 32 30 30 32 2C    pyleft (c) 2002,
00000020  20 32 30 31 32 0A 54 68-65 20 4E 53 46 4F 43 55     2012.The NSFOCU
00000030  53 20 49 4E 46 4F 52 4D-41 54 49 4F 4E 20 54 45    S INFORMATION TE
00000040  43 48 4E 4F 4C 4F 47 59-20 43 4F 2E 2C 4C 54 44    CHNOLOGY CO.,LTD
00000050  2E 0A 4E 53 46 6F 63 75-73 20 53 65 63 75 72 69    ..NSFocus Securi
00000060  74 79 20 54 65 61 6D 20-3C 73 65 63 75 72 69 74    ty Team <securit
00000070  79 40 6E 73 66 6F 63 75-73 2E 63 6F 6D 3E 0A 68    y@nsfocus.com>.h
00000080  74 74 70 3A 2F 2F 77 77-77 2E 6E 73 66 6F 63 75    ttp://www.nsfocu
00000090  73 2E 63 6F 6D 0A 73 63-7A 20 3C 73 63 7A 40 6E    s.com.scz <scz@n
000000A0  73 66 6F 63 75 73 2E 63-6F 6D 3E 0A 00             sfocus.com>..
byteArray [ 60 bytes ] ->
00000000  00 0D 58 0A BD C8 00 0D-58 0A BD C8 90 00 00 00    ..X.....X.......
00000010  01 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
00000020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
00000030  00 00 00 00 00 00 00 00-00 00 00 00                ............
byteArray [ 342 bytes ] ->
00000000  FF FF FF FF FF FF 00 50-56 41 C0 A6 08 00 45 00    .......PVA....E.
00000010  01 48 23 4A 00 00 FF 11-97 5B 00 00 00 00 FF FF    .H#J.....[......
00000020  FF FF 00 44 00 43 01 34-FF B7 01 01 06 00 02 75    ...D.C.4.......u
00000030  C8 0E 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
00000040  00 00 00 00 00 00 00 50-56 41 C0 A6 00 00 00 00    .......PVA......
00000050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
00000060  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
00000070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
00000080  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
00000090  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
000000A0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
000000B0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
000000C0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
000000D0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
000000E0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
000000F0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
00000100  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
00000110  00 00 00 00 00 00 63 82-53 63 35 01 01 3D 07 01    ......c.Sc5..=..
00000120  00 50 56 41 C0 A6 C0 07-53 6F 66 74 49 43 45 FF    .PVA....SoftICE.
00000130  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
00000140  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
00000150  00 00 00 00 00 00                                  ......
byteArray [ 60 bytes ] ->
00000000  01 80 C2 00 00 00 00 0D-58 0A BD C8 00 26 42 42    ........X....&BB
00000010  03 00 00 00 00 00 80 00-00 07 EB 99 2C 83 00 00    ............,...
00000020  00 39 80 07 00 0D 58 0A-BD C0 80 08 03 00 14 00    .9....X.........
00000030  02 00 0F 00 00 00 00 00-00 00 00 00                ............
^C

上述命令进入混杂模式抓取所有链路帧,不对eth_type进行过滤,这正是sniffer所
需要的。

h) 卸载

> net stop ndisprotocol

与普通KMD不同,卸载网络组件不能简单地执行"sc delete ndisprotocol"。可以在
NCPA中卸载"The NDISProtocol Driver",也可以命令行卸载:

> ndisconfig.exe -u nsfocus_ndisprotocol
Succeeded

卸载完成后,前述注册表内容全部删除了。oem3.inf、oem3.pnf、infcache.1以及驱
动目录(%systemroot%/system32/drivers)下的ndisprotocol.sys未被删除。再次执
行如下命令进行安装:

> ndisconfig.exe -i nsfocus_ndisprotocol -t protocol -f c:/onlytemp/ndisprotocol.inf
c:/onlytemp/ndisprotocol.inf was copied to C:/WINDOWS/INF/oem3.inf
Succeeded

%systemroot%/inf目录下仍是oem3.inf、oem3.pnf、infcache.1,不会出现想像中的
oem4.inf。如果觉得别扭,卸载完成后可以删除残留的这四个文件。

i) 一些遗留问题

可以考虑先加载ndisuio,然后动态修改取消MS所加的那些限制。好像没什么实际意
义,懒得弄了。

再就是,对BSOD还是很畏惧,好在也不真要写什么驱动,以后慢慢增进了解吧。

☆ 参考资源

[ 1] Windows Network Data And Packet Filtering Frequently Asked Questions(这里有一张Network Architecture Diagram)
     http://www.pcausa.com/resources/winpktfilter.htm

[ 2] Network Architecture in Windows NT-based Operating Systems
     http://plasmic.com/~vizzini/ntnetarch.html

[ 3] Ntpacket.exe: Updated Windows NT 4.0 NDIS 3.0 Packet Sample Available
     http://support.microsoft.com/default.aspx?scid=kb;EN-US;238652
     ftp://ftp.microsoft.com/Softlib/MSLFILES/Ntpacket.exe

     Bugs in the NT DDK Packet Protocol Driver Sample
     http://www.panix.com/~perin/packetbugs.html

[ 4] EthernetSpy
     http://telemat.det.unifi.it/book/EthernetSpy/EthernetSpy.zip

[ 5] BriProto NDIS Protocol Driver Project Files
     http://adaptive4.ucsd.edu/projects/briproto_driver/files/BriProto.2003.08.01.1218.zip
     http://adaptive4.ucsd.edu/projects/briproto_driver/doc/

[ 6] A RARP Server(source code)
     http://www.panix.com/~perin/rarpd.zip

[ 7] NDIS "Packet" Discussion(介绍了NDIS_PACKET、NDIS_BUFFER)
     http://www.pcausa.com/resources/ndispacket.htm
     http://www.pcausa.com/resources/ndispacket_decode.htm
     http://www.pcausa.com/resources/readonpacket.htm

     NDIS_PACKET Discussion Part 2 - NDIS_PACKET Reserved Areas
     http://www.ndis.com/papers/ndispacket/ndispacket2.htm

[ 8] Workaround To Circumvent ProtocolReceive Faults Caused By Some Faulty NDIS Miniport Drivers
     http://www.pcausa.com/support/KB03080201.htm

[ 9] Conditions Needed For ReceivePacketHandler To Be Called
     http://www.pcausa.com/support/KB07130001.htm

[10] http://www.rhyshaden.com/ethernet.htm(介绍了802.1p的帧格式)

[11] NDIS Driver Compile Flags - Stephan Wolf[2004-03-15]
     http://www.wd-3.com/031504/NDISCompile.htm

[12] KNOWLEDGE BASE LINKS STOP MESSAGES(理解BSOD)
     http://aumha.org/win5/kbestop.htm

[13] Stop 0x0000000A or IRQL_NOT_LESS_OR_EQUAL(介绍了Stop Message的四个参数)
     http://www.microsoft.com/resources/documentation/Windows/XP/all/reskit/en-us/prmd_stp_hwpg.asp

[14] Network Devices and Protocols: Windows DDK

[15] Kernel Driver Frequestly Asked Questions (FAQ)
     http://www.osronline.com/custom.cfm?name=articlePrint.cfm&id=256

[16] INFO: Network Binding Analysis
     http://support.microsoft.com/default.aspx?scid=kb;en-us;192483