• 第二十七篇:Windows驱动中的PCI, DMA, ISR, DPC, ScatterGater, MapRegsiter, CommonBuffer, ConfigSpace


    近期有些人问我PCI设备驱动的问题, 和他们交流过后, 我建议他们先看一看<<The Windows NT Device Driver Book>>这本书, 个人感觉, 这本书写得很连贯流畅.


    PCI设备驱动基本包含了PCI的资源获取, 配置空间的读写, 中断的处理, 中断后半部在DPC中的处理.

    同一时候, 也必须了解DMA, ScatterGater, MapRegister, Common Buffer等基础.


    1.1 PCI设备资源获取

    PCI设备的资源是系统依据设备的属性(配置空间中寄存器的值)来动态分配的.

    驱动中仅仅需在PNP START中获取这些系统分配的资源:

    比如: 笔者开发的PCI电视卡驱动中, 就使用到了当中了两类资源, CmResourceTypePort与CmResourceTypeInterrupt.

    Port地址作为设备寄存器首地址, 之后, 能够使用WRITE_PORT_ULONG与READ_PORT_ULONG加上对应的OFFSET来对设备寄存器进行訪问.

    Interrupt资源中解释出来的内容, 则主要作为IoConnectInterrupt系统函数的參数, 将设备的硬件中断与ISR相关联, KINTERRUPT的实例则是设备中断的软件形式的载体.


    1.2 DMA

    DMA设备, 在系统中分为MASTER与SLAVE, 另外一个非常重要的能力就是是否支持Scatter/Gather.

    这些能力终于表如今DEVICE_DESCRIPTION所定义的数据结构的成员中, 比如:DmaWidth, ScatterGather, Master, Dma32BitAddresses, Dma64BitAddresses.

    系统终于将各种不同类型的设备DMA抽象为DMA_ADAPTER的实例, 它是设备DMA软件形式的载体.

    驱动代码通过IoGetDmaAdapter系统调用, 将物理设备对象PDO与DMA描写叙述结构作为參数, 终于得到这个DMA_ADAPTER对象, 作为兴许一系列DMA相关操作的实体对象.


    1.3 Map Register

    用户空间, 内核空间的虚拟内存与物理内存的关联是通过页表来映射的, 驱动程序猿经常会使用MDL, 它也是某一特定区域虚拟内存与物理内存的映射关系.

    而DMA设备则须要从总线地址(MSDN中又叫逻辑地址)与内存物理的映射关系角度去看待系统内存.

    这个映射的关系就是由Map Register承担的.

    只是, 这批Map Register则依据系统而定, 有些是硬件实现, 有些是软件中划分出来的特定的一块内存.

    IoGetDmaAdapter的调用, 也是向系统申请Map Register的过程.


    1.4 Common Buffer

    这也是大家问得最多的问题

    简单地讲, Common buffer是以DMA_ADAPTER为代表所申请的, 申请成功后, 既能通过虚拟地址訪问, 也能够通过DMA控制器所属逻辑地址空间的地址来訪问的连续物理内存.

    它的优点就是物理上连续, 存在的问题是系统中连续物理内存是随着系统的执行时间的流逝, 越来越稀缺.

    AllocateCommonBuffer系统调用是作为DMA_ADAPTER的DmaOperations形式存在的, 所以, 详细的一块Common Buffer能够说, 是与详细的一个DMA控制器所关联的.

    AllocateCommonBuffer成功调用后, 会返回虚拟地址与DMA控制器所属逻辑空间的逻辑地址.

    笔者开发的PCI电视卡, 就是通过AllocateCommonBuffer分配一块较小的连续物理内存, 用来存放Scatter/Gather列表 (某块内存的逻辑地址SCATTER_GATHER_LIST.Elements[i].Address.LowPart 与该内存的长度SCATTER_GATHER_LIST.Elements[i].Length, 对应操作通过common buffer的虚拟地址 ).

    这个Scatter/Gather List列表终于由具有S/G能力的DMA控制器来读取(对应操作通过common buffer的逻辑地址), 依据当中的表项, 进行DMA读/写操作.


    1.5 S/G

    S/G的能力是DMA控制器的特性, 假设具有S/G的能力, 则能够批量地DMA操作, 否则, 必须一次一次地使用MapTransfer来完毕DMA操作.

    系统空间的中虚拟内存与物理内存之间的联系通过IoAllocateMdl与MmBuildMdlForNonPagedPool建立特定的MDL来表示.

    其后,通过DMA_ADAPTER的DmaOperations中的GetScatterGatherList获取MDL所描写叙述的虚拟地址内存的S/G列表, 最后, 在GetScatterGatherList的

    ExecutionRoutine 函数中, 将该列表填入Common buffer的TABLE(起始逻辑地址 与 长度)中, 以供DMA Controller所用.


    1.6 ISR与DPC
    刚才已经提到, ISR是通过IoConnectInterrupt注冊的.
    ISR在设备中断到来时实调用, 但详细的事项则交由(KeInsertQueueDpc)DPC来处理.
    而DPC则是通过KeInitializeDpc系统调用, 将DPC对象KDPC与详细的KDEFERRED_ROUTINE DPC处理函数相关联的.

    1.7 PCI设备配置空间的訪问

    其实, 普通情况下, Windows PCI设备并不须要訪问PCI设备配置空间.
    但作为一个完整的PCI设备驱动, 这里提及一下.

    因为PCI设备的配置空间与IO/MEM空间是分开的, 前面已经提及IO/MEM的訪问方式, 配置空间的訪问例如以下:
    定义变量:BUS_INTERFACE_STANDARD m_BusInterfaceStandard;
    建立: IRP, 主与次分别为IRP_MJ_PNP, IRP_MN_QUERY_INTERFACE, 得到BUS_INTERFACE_STANDARD数据结构.
    之后, 通过BUS_INTERFACE_STANDARD中的SetBusDataGetBusData来进行PCI配置空间的寄存器读写.

    PCI设备驱动全然能够用在PCIe设备上, 毕竟上层来讲, 他们没有太多的差别.

    与USB驱动不同, PCI设备须要考虑驱动设计中的方方面面, 希望这篇文章对大家有所借鉴作用.







  • 相关阅读:
    进程虚拟内存
    非连续内存区缺页异常处理
    请求调页和写时复制
    标签对齐(补充)
    shell数学表达式
    缺页异常的处理
    不错的书籍
    imag database2
    image database
    Apache down了?
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/7105762.html
Copyright © 2020-2023  润新知