DriverEntry
主要任务:
生成控制设备,
填写协议特征(协议的派遣例程),
初始化一个全局的链表和锁(用于存放绑定好的网卡),还有一个绑定完成的事件
注册协议返回一个全局的协议句柄(NdisProtocolHandle)。
协议与网卡的绑定
VOID
NdisProtBindAdapter(
OUT PNDIS_STATUS pStatus,
IN NDIS_HANDLE BindContext,
IN PNDIS_STRING pDeviceName,
IN PVOID SystemSpecific1,
IN PVOID SystemSpecific2
)
在DriverEntry中填写了协议特征,这个例程就是用来绑定协议与网卡的。
如果一个协议驱动绑定了一个网卡,那么
1.网卡收到的所有数据都会提交给这个协议
2.协议可以使用这个网卡发送数据
当Windows检测到有网卡时,就会调用每个注册过的协议的BindAdpaterHandler函数(本驱动中用NdisProtBindAdapter填写)。
NdisProtBindAdapter主要是对PNDISPROT_OPEN_CONTEXT的初始化工作,一个打开上下文就是一个网卡的绑定,里面存储了这次绑定的信息和资源,读写队列,包队列,自旋锁,电源事件等等。
结构如下:
typedef struct _NDISPROT_OPEN_CONTEXT
{
LIST_ENTRY Link; // Link into global list
ULONG Flags; // State information
ULONG RefCount;
NPROT_LOCK Lock;
PFILE_OBJECT pFileObject; // Set on OPEN_DEVICE
NDIS_HANDLE BindingHandle;
NDIS_HANDLE BindingHandle;
NDIS_HANDLE SendNetBufferListPool;
// let every net buffer list contain one net buffer(don't know how many net buffers can be include in one list.
NDIS_HANDLE RecvNetBufferListPool;
ULONG MacOptions;
ULONG MaxFrameSize;
LIST_ENTRY PendedWrites; // pended Write IRPs
ULONG PendedSendCount;
LIST_ENTRY PendedReads; // pended Read IRPs
ULONG PendedReadCount;
LIST_ENTRY RecvPktQueue; // queued rcv packets
ULONG RecvPktCount;
NET_DEVICE_POWER_STATE PowerState;
NDIS_EVENT PoweredUpEvent; // signalled iff PowerState is D0
NDIS_STRING DeviceName; // used in NdisOpenAdapter
NDIS_STRING DeviceDescr; // friendly name
NDIS_STATUS BindStatus; // for Open/CloseAdapter
NPROT_EVENT BindEvent; // for Open/CloseAdapter
BOOLEAN bRunningOnWin9x;// TRUE if Win98/SE/ME, FALSE if NT
ULONG oc_sig; // Signature for sanity
UCHAR CurrentAddress[NPROT_MAC_ADDR_LEN];
PIRP StatusIndicationIrp;
} NDISPROT_OPEN_CONTEXT, *PNDISPROT_OPEN_CONTEXT;
在函数中为OpenContext分配空间,清空内存,设置特征数据oc_sig,便于识别判错。
初始化自旋锁,三个队列,和一个电源事件。
增加一个引用计数。
将这个打开上下文(绑定的一个网卡)添加到全局链表。 为避免同步问题。这个操作需要调用全局自旋锁。
函数结尾调用ndisportCreateBinding进行最终绑定。
ndisportCreateBinding
这是一个比较复杂的函数,因为要使用同步手段防止多线程竞争
NDIS_STATUS
ndisprotCreateBinding(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
__in_bcount(BindingInfoLength) IN PUCHAR pBindingInfo,
IN ULONG BindingInfoLength
)
{
……
//首先在全局打开上下文链表查询网卡是否已经绑定, *** 注意这个操作应增加打开上下文的引用计数,并在成功返回处减少引用计数***
ndisprotLookupDevice(pBindingInfo, BindingInfoLength);
……
//获取pOpenContext的锁,因为只对空闲上下文进行操作
NPROT_ACQUIRE_LOCK(&pOpenContext->Lock);
//因为是使用自旋锁,所以尽量进行简单的操作,用标志位判断同步信息
//如果不是空闲状态,或者收到了解除绑定要求就直接返回失败,并解开自旋锁
if (!NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_IDLE) ||
NPROT_TEST_FLAGS(pOpenContext->Flags, NUIOO_UNBIND_FLAGS, NUIOO_UNBIND_RECEIVED))
{
……
}
//设置标记,表示开始使用打开上下文,并释放锁
NPROT_SET_FLAGS(pOpenContext->Flags, NUIOO_BIND_FLAGS, NUIOO_BIND_OPENING);
NPROT_RELEASE_LOCK(&pOpenContext->Lock);
//接下来就是开始绑定了
分别分配设备名
……
分配接收包池
……
分配发送包池
……
//NDIS中每个以太网包都用一个NDIS_PACKET描述,内容由NDIS_BUFFER描述;
在这里分配的包池大小就是缓冲区能容纳的包的个数。以后就不用动态分配了。
//
// Assume that the device is powered up.
//
pOpenContext->PowerState = NetDeviceStateD0;
//初始化打开事件(绑定)
NPROT_INIT_EVENT(&pOpenContext->BindEvent);
//填写一个局部变量
NPROT_ZERO_MEM(&OpenParameters, sizeof(NDIS_OPEN_PARAMETERS));
OpenParameters.Header.Revision = NDIS_OPEN_PARAMETERS_REVISION_1;
OpenParameters.Header.Size = sizeof(NDIS_OPEN_PARAMETERS);
OpenParameters.Header.Type = NDIS_OBJECT_TYPE_OPEN_PARAMETERS;
OpenParameters.AdapterName = BindParameters->AdapterName;
OpenParameters.MediumArray = &MediumArray[0];
OpenParameters.MediumArraySize = sizeof(MediumArray) / sizeof(NDIS_MEDIUM);
OpenParameters.SelectedMediumIndex = &SelectedMediumIndex;
OpenParameters.FrameTypeArray = &FrameTypeArray[0];
OpenParameters.FrameTypeArraySize = sizeof(FrameTypeArray) / sizeof(NET_FRAME_TYPE);
NDIS_DECLARE_PROTOCOL_OPEN_CONTEXT(NDISPROT_OPEN_CONTEXT)
//在这里终于绑定了!!!
NdisOpenAdapter(
&Status,
&OpenErrorCode,
&pOpenContext->BindingHandle, //绑定事件
&SelectedMediumIndex,
&MediumArray[0], //以太网介质类型NdisMedium802_3
sizeof(MediumArray) / sizeof(NDIS_MEDIUM),
Globals.NdisProtocolHandle,
(NDIS_HANDLE)pOpenContext,
&pOpenContext->DeviceName, //要绑定的网卡名
0,
NULL);
//等待绑定事件受信
if (Status == NDIS_STATUS_PENDING)
{
NPROT_WAIT_EVENT(&pOpenContext->BindEvent, 0);
Status = pOpenContext->BindStatus;
}
//如果不成功
…………
fOpenComplete = TRUE;
//
// Get the friendly name for the adapter. It is not fatal for this
// to fail.
//
(VOID)NdisQueryAdapterInstanceName(
&pOpenContext->DeviceDescr,
pOpenContext->BindingHandle
);
//OID:想下层驱动发送请求并返回结果
//往下层驱动查询MAC地址,最大帧,连接状态,网卡选项....
…………
//一些尾部处理,别忘记减少OPEN_CONTEXT的引用计数
}
查询OID request
OID是NDIS Object identifilers,上层发送OID_XXX请求,下层返回约定结果
例如查询最大帧长,返回结果 TCP/IP进行打包发送。
在Ndisport中封装了查询函数
// 获得最大帧长
Status = ndisprotDoRequest(
pOpenContext,
NdisRequestQueryInformation,
OID_GEN_MAXIMUM_FRAME_SIZE,
&pOpenContext->MaxFrameSize,
sizeof(pOpenContext->MaxFrameSize),
&BytesProcessed
);
if (Status != NDIS_STATUS_SUCCESS)
{
DEBUGP(DL_WARN, ("CreateBinding: qry max frame failed: %x\n",
Status));
break;
}
NDIS_STATUS
ndisprotDoRequest(
IN PNDISPROT_OPEN_CONTEXT pOpenContext,
IN NDIS_REQUEST_TYPE RequestType,
IN NDIS_OID Oid,
IN PVOID InformationBuffer,
IN ULONG InformationBufferLength,
OUT PULONG pBytesProcessed
)
{
NDISPROT_REQUEST ReqContext; //port自定义的结构 一个请求,一个事件,一个状态
PNDIS_REQUEST pNdisRequest = &ReqContext.Request; //填充ndis请求
NDIS_STATUS Status;
// 初始化一个事件。这个事件会在请求完成函数中被设置,
// 以便通知请求完成了。
NPROT_INIT_EVENT(&ReqContext.ReqEvent); //填充事件
pNdisRequest->RequestType = RequestType;//填充ndis请求中的请求类型,
//例如查询请求
switch (RequestType)
{
case NdisRequestQueryInformation:
pNdisRequest->DATA.QUERY_INFORMATION.Oid = Oid;
pNdisRequest->DATA.QUERY_INFORMATION.InformationBuffer =
InformationBuffer;
pNdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength =
InformationBufferLength;
break;
case NdisRequestSetInformation:
pNdisRequest->DATA.SET_INFORMATION.Oid = Oid;
pNdisRequest->DATA.SET_INFORMATION.InformationBuffer =
InformationBuffer;
pNdisRequest->DATA.SET_INFORMATION.InformationBufferLength =
InformationBufferLength;
break;
default:
NPROT_ASSERT(FALSE);
break;
}
// 发送请求
NdisRequest(&Status,
pOpenContext->BindingHandle,
pNdisRequest);
//尚未完成 则等待事件受信,受信事件在RequestComplete函数(在协议函数中填写)中设置
if (Status == NDIS_STATUS_PENDING)
{
NPROT_WAIT_EVENT(&ReqContext.ReqEvent, 0);
Status = ReqContext.Status;
}
// 如果成功了的话...
if (Status == NDIS_STATUS_SUCCESS)
{
// 获得结果的长度。这个结果的长度是实际需要的长度。可
// 能比我们实际提供的长度要长。
// 请求中记录了完成时的字节长度
*pBytesProcessed = (RequestType == NdisRequestQueryInformation)?
pNdisRequest->DATA.QUERY_INFORMATION.BytesWritten:
pNdisRequest->DATA.SET_INFORMATION.BytesRead;
// 如果结果长度比实际上我们提供的缓冲区要长,那么就简
// 单的设置为输入参数中缓冲区的最大长度。
if (*pBytesProcessed > InformationBufferLength)
{
*pBytesProcessed = InformationBufferLength;
}
}
return (Status);
}
解除网卡绑定
是Bind的逆操作。