对一个虚拟网卡驱动程序的剖析(三):网卡的初始化函数MiniportInitialize
2008-11-16 13:45
477 查看
前面的驱动程序入口函数DriverEntry的作用是向系统说明这个驱动程序的结构。初始化函数则是为了使我们的网卡能够正常工作而进行各种准备工作。
系统在调用网卡的初始化函数的时候,会传进来一个输入参数MediumArray,这是一个包含一系列介质类型的数组,初始化函数要在这个数组中间选一种类型返回给系统,告诉系统该驱动支持的类型。做法就是将系统传进来的一个输出参数SelectedMediumIndex指向的地方赋上选择的类型在数组中的索引值。我们的虚拟网卡选择的是NdisMedium802_3,也就是告诉系统,我们的网卡是一块标准的以太网网卡。
接下来要做的也是最重要的准备工作就是想办法使一些公共信息能够在驱动程序的各个部分进行传递。通常使用的方法是定义一个结构,把驱动程序各个部分要用的信息都包含进去,驱动程序的各个部分根据这个结构的指针获取自己需要的信息。我们的这个虚拟网卡的驱动也定义了一个这样一个结构,D100_ADAPTER。
NdisAllocateMemoryWithTag(&Adapter,sizeof(D100_ADAPTER),'0000');
上面的系统的Ndis库函数,它负责分配内存,注意要检查它返回的状态,如果不成功的话,整个初始化函数也应该向系统返回对应的失败状态。然后将MiniportAdapterHandle,这是另外一个输入参数保存起来。日后调用很多Ndis库函数的时候都要提供这个Handle,以便让系统了解是哪个网卡要调用这些库函数。
接下来可以调用NdisOpenConfiguration函数打开注册表读取一些配置。系统允许每块网卡在注册表中的指定位置保留一些配置信息,每次初始化的时候可以去读取,也可以在需要的时候进行改变。当然,目前我们的虚拟网卡并没有什么信息需要保存在注册表。
下面应该对这个结构中的其它成员进行初始化,例如,如果用到同步互斥锁NDIS_SPIN_LOCK的话,要调用NdisAllocateSpinLock进行初始化,用到同步事件KEVENT也要调用KeInitializeEvent对其进行初始化。在我们的网卡中,为了使接收和发送的数据包的申请更加方便,预先申请了一个发送数据包的PacketPool和BufferPool,以及接收数据包的相应类型缓冲池。这样以后要申请一个数据包描述对象(NDIS_PACKET)或者缓存区描述对象(NDIS_BUFFER)的时候就可以在上面这个缓存池中申请了。
最后,我们使用了NdisMRegisterDevice向系统注册了一个支持各种Dispatch函数的设备对象,这样是为了使虚拟网卡能和用户态的代理(Agent)程序进行直接地通讯。例如,用户态的程序可以将我们注册的符号链接名放到CreateFile函数中去直接打开设备句柄,可以用ReadFile或者WriteFile这样的接口对设备进行读写,可以用DeviceIoControl对设备进行一些操纵。这些都是要以驱动程序中实现相应的Dispatch函数为基础的。
如果一切顺利的话,下面这条语句一定是出现在这个函数的最后的:
return NDIS_STATUS_SUCCESS;
系统在调用网卡的初始化函数的时候,会传进来一个输入参数MediumArray,这是一个包含一系列介质类型的数组,初始化函数要在这个数组中间选一种类型返回给系统,告诉系统该驱动支持的类型。做法就是将系统传进来的一个输出参数SelectedMediumIndex指向的地方赋上选择的类型在数组中的索引值。我们的虚拟网卡选择的是NdisMedium802_3,也就是告诉系统,我们的网卡是一块标准的以太网网卡。
接下来要做的也是最重要的准备工作就是想办法使一些公共信息能够在驱动程序的各个部分进行传递。通常使用的方法是定义一个结构,把驱动程序各个部分要用的信息都包含进去,驱动程序的各个部分根据这个结构的指针获取自己需要的信息。我们的这个虚拟网卡的驱动也定义了一个这样一个结构,D100_ADAPTER。
NdisAllocateMemoryWithTag(&Adapter,sizeof(D100_ADAPTER),'0000');
上面的系统的Ndis库函数,它负责分配内存,注意要检查它返回的状态,如果不成功的话,整个初始化函数也应该向系统返回对应的失败状态。然后将MiniportAdapterHandle,这是另外一个输入参数保存起来。日后调用很多Ndis库函数的时候都要提供这个Handle,以便让系统了解是哪个网卡要调用这些库函数。
接下来可以调用NdisOpenConfiguration函数打开注册表读取一些配置。系统允许每块网卡在注册表中的指定位置保留一些配置信息,每次初始化的时候可以去读取,也可以在需要的时候进行改变。当然,目前我们的虚拟网卡并没有什么信息需要保存在注册表。
下面应该对这个结构中的其它成员进行初始化,例如,如果用到同步互斥锁NDIS_SPIN_LOCK的话,要调用NdisAllocateSpinLock进行初始化,用到同步事件KEVENT也要调用KeInitializeEvent对其进行初始化。在我们的网卡中,为了使接收和发送的数据包的申请更加方便,预先申请了一个发送数据包的PacketPool和BufferPool,以及接收数据包的相应类型缓冲池。这样以后要申请一个数据包描述对象(NDIS_PACKET)或者缓存区描述对象(NDIS_BUFFER)的时候就可以在上面这个缓存池中申请了。
最后,我们使用了NdisMRegisterDevice向系统注册了一个支持各种Dispatch函数的设备对象,这样是为了使虚拟网卡能和用户态的代理(Agent)程序进行直接地通讯。例如,用户态的程序可以将我们注册的符号链接名放到CreateFile函数中去直接打开设备句柄,可以用ReadFile或者WriteFile这样的接口对设备进行读写,可以用DeviceIoControl对设备进行一些操纵。这些都是要以驱动程序中实现相应的Dispatch函数为基础的。
如果一切顺利的话,下面这条语句一定是出现在这个函数的最后的:
return NDIS_STATUS_SUCCESS;
相关文章推荐
- 对一个虚拟网卡驱动程序的剖析
- Linux下Rtl8139too网卡设备驱动程序关键函数剖析
- 网卡驱动程序的框架以及一个简单的虚拟网卡驱动程序
- Linux下Rtl8139too网卡设备驱动程序关键函数剖析
- Linux下Rtl8139too网卡设备驱动程序关键函数剖析
- Linux下Rtl8139too网卡设备驱动程序关键函数剖析
- ceph存储 centos下添加多个虚拟ip地址的方法(一个网卡)
- 虚拟网卡 TUN/TAP 驱动程序设计原理
- 剖析一个java对象初始化顺序问题
- 深度剖析WinPcap之(六)——驱动程序的初始化与清除(2)
- More Effective C++ Item M31:让函数根据一个以上的对象来决定怎么虚拟
- 创建一个数组, 实现函数init()初始化数组、 实现empty()清空数组、 实现reverse()函数完成数组元素的逆置。
- LWIP network interface 网卡 初始化 以 STM32 为例子 后面会有 用 2G 或者4G 模块 用 PPP拨号的 形式 虚拟出网卡 所以先以 这个为 前提
- 学习笔记 “写一个函数获取某一张指定的网卡的IP地址”
- 深度剖析WinPcap之(六)――驱动程序的初始化与清除(4)
- 网卡驱动10-做一个与外界交互的虚拟网卡6(突发奇想!搞个网络鼠标!)
- 创建一个数组 实现函数init()初始化数组 empty()清空数组 reverse()函数完成数组元素的逆置
- 创建一个数组, 实现函数init()初始化数组实现empty()清空数组实现reverse()函数完成数组元素的逆置。