Ndis网卡驱动是如何操控硬件的

  1. 在 DriverEntry 里设置一个_NDIS_MINIPORT_DRIVER_CHARACTERISTICS结构,初作为参数提供给 NdisMRegisterMiniportDriver 函数
  NDIS_MINIPORT_DRIVER_CHARACTERISTICS MPChar;
    MPChar.InitializeHandlerEx          = MPInitialize;  // 初始化handle
  
   ...

   // 注册小端口驱动 
    Status = NdisMRegisterMiniportDriver(DriverObject,
                                         RegistryPath,
                                         (PNDIS_HANDLE)MiniportDriverContext,
                                         &MPChar,
                                         &NdisMiniportDriverHandle);
  1. 系统会调用上面的初始化handle, 函数的模型如下
 NDIS_STATUS MPInitialize( NDIS_HANDLE NdisMiniportHandle, 
                   NDIS_HANDLE MiniportDriverContext,
                   PNDIS_MINIPORT_INIT_PARAMETERS MiniportInitParameters // 由系统传来的一个 _NDIS_MINIPORT_INIT_PARAMETERS 参数                    
                 )

其中 _NDIS_MINIPORT_INIT_PARAMETERS 结构里包含一项(PNDIS_RESOURCE_LIST AllocatedResources)

  typedef struct _CM_PARTIAL_RESOURCE_LIST {  // PNDIS_RESOURCE_LIST 结构
    USHORT Version;
    USHORT Revision;
    ULONG Count;
    CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1]; //
  }
  resList = MiniportInitParameters->AllocatedResources; // 取得参数里的resource
  for (index=0; index < resList->Count; index++)    
  {
   pResDesc = &resList->PartialDescriptors[index];     // 取得resource里的PartialDescriptors
    switch(pResDesc->Type) 
    {
      case CmResourceTypePort:
        Adapter->IoBaseAddress = pResDesc->u.Port.Start ; // 得到了一个IoBaseAddress
        Adapter->IoRange = pResDesc->u.Port.Length; //
    ...
  }

硬件资源参考:https://blog.youkuaiyun.com/xiangbaohui/article/details/105179813

3 调用函数 NdisMRegisterIoPortRange 去注册一个I/O Port,以后通过返回的PortOffse去操控网卡

 Status = NdisMRegisterIoPortRange( (PVOID *)&Adapter->PortOffset, // 返回一个portoffset,就是通过这个portoffset去操控硬件的,和以前的in out端口指令一样 
                        Adapter->AdapterHandle,
                        NdisGetPhysicalAddressLow ( Adapter->IoBaseAddress ) , // 上面我们得到的IoBaseAddress 
                       Adapter->IoRange); `
  1. 有了上面的portoffset,我们给网卡设置收包/发包的数据存放地址,通过写 PortOffset+reg 来给网卡下命令。 不同的reg对应不同的命令
enum RTL8169_registers { // 硬件网卡的reg命令表 ,
    /* PHY access */
    TxDescStartAddr = 0x20,
    RxDescStartAddr = 0xE4, 

    ...
  };

  ULONG_PTR ioaddr = A->PortOffset;  // PortOffset由上面调用 NdisMRegisterIoPortRange 函数得到的
  #define RTL_W32(reg, val32) WRITE_PORT_ULONG((PULONG)(ioaddr + reg),(ULONG)val32 ) // 

  // 传递命令
  case SCB_RUC_LOAD_BASE : // 写入收包地址
    RTL_W32 ( RxDescStartAddr, NdisGetPhysicalAddressLow ( Adapter->HwRbdBasePa ) );     //  Adapter->HwRbdBasePa 是收包数据地址
    RTL_W32 ( RxDescStartAddr + 4, NdisGetPhysicalAddressHigh ( Adapter->HwRbdBasePa )); // 
  case SCB_TBD_LOAD_BASE : // 写入发包地址`
    RTL_W32 ( , NdisGetPhysicalAddressLow ( Adapter->HwTbdBasePa ) );  TxDescStartAddr   // Adapter->HwTbdBasePa 是发包数据的存放地址`
    RTL_W32 ( TxDescStartAddr + 4, NdisGetPhysicalAddressHigh ( Adapter->HwTbdBasePa ) );//`
  1. 地址设置好了,当有数据要处理的时候,网卡会从 发包地址里读取要发送的数据 或 向收包地址里写入收到的数据。
      收包 — 网卡写数据到收包地址,生成一个硬件中断通知中断处理函数,中断处理函数先关闭中断,然后取数据,通过NdisMIndicateReceiveNetBufferLists函数把数据传递到上层, 再打开中断,等待下一次中断( 数据传递路径:miniport驱动-> (filter) -> protocol驱动 -> 应用层recv(),驱动之间还可以有过滤驱动,如防火墙就是在底层驱动 之间装一个filter driver,这样数据包会在recv之前被抓到 )
     发包 — 把上层传递下来的数据写到发包地址,通过portoffse+reg给网卡下指令,通知网卡去取数据 ( 路径很上面相反,send() -> protocol -> (filter) ->miniport )

  2. 传给网卡的地址,参考 https://blog.youkuaiyun.com/hz5034/article/details/79794615 这篇文章,是一串的连续的描述符,addr —> [描述符][描述符][描述符]…[描述符]
      把这个addr通过传给网卡,网卡/dma能够认识这个描述符的,他们会自动工作。【 这文章对这个描述符说的很好 https://www.cnblogs.com/CasonChan/p/5166239.html 】

数据在驱动中是以net_buffer_lists(NBL)结构存在的,一个NBL又有N个net_buffer(NB),一个NB下又有N个MDL,这Mdl存放的就是数据,
  描述符描述的就是每个Mdl的地址。一连串的Mdl组成一个NB,一连串的NB组成一个NBL。
  描述符的结构大概如下:

  typedef struct _TBD_STRUC { // 发包的描述符,
    UINT16        FrameLength ;
    UINT16        status;
    UINT32        notused;
    UINT32        TbdBufferAddress;       // 网卡会去这个地址取发包的数据
    UINT32        TbdBufferAddressHigh ;
  } TBD_STRUC, *PTBD_STRUC;

  typedef struct _RTK_RECEIVE_BUFFER_DESCRIPOR_STRUC { //  收包的描述符
    UINT32        status;
    UINT32        vlan_tag;
    UINT32        buf_addr;      // 网卡会把数据写到这个地址
    UINT32        buf_Haddr;
  } HW_RBD, *PHW_RBD;

6.参考资料 NDIS sample - 6.0 miniport driver for realtek 168/8169/8111/8110
https://www.codeproject.com/Articles/24384/NDIS-sample-miniport-driver-for-realtek

  对于在“蓝网之家”影响下蠢蠢而动搞 Windows 95 远程启动的朋友可能不少,那么大家一定对 NDIS 这几个字母不会感到陌生。其实不只是在远程启动这一层,只要是网卡驱动盘,大家都会在里面发现有类似 NDISNDIS2、NDIS3、NDIS4一样的目录,只是大家在 Windows 9x 或 NT 中安装、设置网卡时没有注意到它罢了。但即使大搞特搞 RPL 的朋友对其大概也是只知其然而不其所以然。    NDIS 是什么?有什么作用?       NDIS 的全称是 Network Device Interface Specification,中文意思就是网络设备接口规范。    根据 OSI 七层模型,物理层定义了对网卡、线缆以及其它物理硬件设备进行配置的标准。节点间建立通信时是由物理层在传输介质上传送二进制信息实现的,因此,在发送端和接收端都还必须有一个程序来格式化这种信息流并将其传送给上一层。NDIS 的作用就是避免在访问网卡每次进行传输时都编写相应的代码。由此说来,NDIS 本质上是一种软件接口,有了 NDIS ,所有的传输就可以采用一种通用的方式来访问由不同厂商制造的网卡了,即它是用来连接协议堆栈和网卡的。   与此相关的软件还有重定向器(Redirector)和服务器(Server)。前者的目的是截获来自 OSI 会话层的网络调用,并通过将其传送到相应的协议驱动程序接口而格式化成 NDIS 能够识别和使用的命令。后者则负责接收从重向器传过来的来自于远程计算机的请求,再将这一请求传送给相应的本地文件系统驱动程序,最后再由该“服务器”将数据沿协议堆栈向下传递给客户机。    TCP协议也是通过调用 NDIS 接口服务来完成传输操作的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

站长漫谈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值