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

尝试描述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);
2. 系统会调用上面的初始化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) 【参考https://blog.csdn.net/xiangbaohui/article/details/105179813
    {
      case CmResourceTypePort:
        Adapter->IoBaseAddress = pResDesc->u.Port.Start ; // 得到了一个IoBaseAddress
        Adapter->IoRange = pResDesc->u.Port.Length; //
    ...
  }

3. 调用函数 NdisMRegisterIoPortRange 去注册一个I/O Port,以后通过返回的PortOffse去操控网卡
  Status
= NdisMRegisterIoPortRange( (PVOID *)&Adapter->PortOffset, // 返回一个portoffset,就是通过这个portoffset去操控硬件的,和以前的in out端口指令一样                        Adapter->AdapterHandle,                        NdisGetPhysicalAddressLow ( Adapter->IoBaseAddress ) , // 上面我们得到的IoBaseAddress                        Adapter->IoRange); `
4. 有了上面的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 ) );//`
5. 地址设置好了,当有数据要处理的时候,网卡会从 发包地址里读取要发送的数据 或 向收包地址里写入收到的数据。
    收包 --- 网卡写数据到收包地址,生成一个硬件中断通知中断处理函数,中断处理函数先关闭中断,然后取数据,通过NdisMIndicateReceiveNetBufferLists函数把数据传递到上层,
       再打开中断,等待下一次中断( 数据传递路径:miniport驱动-> (filter) -> protocol驱动 -> 应用层recv(),驱动之间还可以有过滤驱动,如防火墙就是在底层驱动
         之间装一个filter driver,这样数据包会在recv之前被抓到 )     发包
--- 把上层传递下来的数据写到发包地址,通过portoffse+reg给网卡下指令,通知网卡去取数据 ( 路径很上面相反,send() -> protocol -> (filter) ->miniport )
6. 传给网卡的地址,参考 https://blog.csdn.net/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; 参考:NDIS 6.0的miniport设备驱动 ---- http://www.codesoso.com/code/NDIS-miniport-driver.aspx
posted @ 2020-10-30 13:01  一条小鳄鱼  阅读(800)  评论(0)    收藏  举报