Windows_PCI设备驱动开发

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

PCI设备驱动开发

PCI设备驱动开发

2008-08-28 13:07

1. PCI 简介

PCI 总线标准是一种将系统外部设备连接起来的总线标准,是PC 中最重要的总线,实际上是系统的各个部分如何交互的接口。传输速率可达到133MB/s。在当前的PC 体系结构中,几乎所有的外部设备采用的各种各样的接口总线,均是通过桥接电路挂接到PCI 系统上。在这种PCI 系统中,Host/PCI 桥称为北桥,连接主处理器总线到基础PCI 局部总线。PCI 与其他总线的接口称为南桥,其中南桥还通常含有中断控制器、IDE 控制器、USB 控制器和DMA 控制器等。南桥和北桥组成主板的芯片组。

2. PCI配置空间

每个PCI设备都有自己的配置空间,用于支持即插即用,使之满足现行的系统配置结构。下面对PCI配置空间做一下简要介绍。

配置空间是一容量为256字节并具有特定结构的地址空间。这个空间又分为头标区和设备有关区两部分。头标区的长度是64字节,每个设备都必须配置该区的寄存器。该区中的各个字段用来唯一地识别设备。其余的192字节因设备而异。配置空间的头标区64个字节的使用情况如图1示。

为了实现即插即用,系统可根据硬件资源的使用情况,为PCI设备分配新的资源。因此编写设备驱动程序重点是获得基址寄存器(Base Address)和中断干线寄存器的内容。配置空间共有六个基址寄存器和一个中断干线寄存器,具体用法如下:

PCI Base Address 0寄存器:系统利用此寄存器为PCI接口芯片的配置寄存器分配一段PCI地址空间,通过这段地址我们可以以内存映射的形式访问PCI接口芯片的配置寄存器。

PCI Base Address 1寄存器:系统利用此寄存器为PCI接口芯片的配置寄存器分配一段PCI地址空间,通过这段地址我们可以以I/O的形式访问PCI接口芯片的配置寄存器。

PCI Base Address 2、3、4、5寄存器:系统BIOS利用这些寄存器分配PCI地址空间以支持PCI接口芯片的局部配置寄存器0、1、2、3的访问。

在所有基址寄存器中,第0位均为只读位,表示这段地址映射到存储器空间还是I/O空间,如果是“1”表示映射到I/O空间,如果是“0”则表示映射到存储器空间。

中断干线寄存器(Interrupt Line):用于说明中断线的连接情况,这个寄存器的值与标准8259的IRQ编号(0~15)对应。

表1 PCI配置空间

3.设备初始化

PCI 设备驱动程序要完成识别PCI器件、寻找PCI硬件的资源和对PCI器件中断的服务。在驱动程序初始化过程中,使用HalGetBusData()函数完成寻找PCI设备的工作。在初始化过程中,使用器件识别号(Device ID)和厂商识别号(Vendor ID),通过遍历总线上的所有设备,寻找到指定的PCI设备,并获取设备的总线号,器件号与功能号。通过这些配置信息,可以在系统中寻址该设备的资源配置列表。

在此之后,驱动程序需要从配置空间获取硬件的参数。PCI设备的中断号、端口地址的范围(I/O)方式、存储器的地址与映射方式等,都可以从硬件资源列表数据结构中获取。在Windows NT中,调用HalAssignSlotResources()函数来获得指定设备的资源列表数据结构指针,然后通过遍历该列表中的所有资源描述符,获取该设备的I/O端口基地址与长度,中断的中断级、中断向量与模式,存储器基地址与长度等硬件资源数据。我们设计的DMA通信采用总线主控方式进行通信,在设备初始化时需要对DMA适配器进行初始化,使用HalGetAdapter()获得操作系统分配的适配器对象指针。

示例代码如下:

// 遍历总线,获得指定设备的总线号,器件号与功能号

for ( busNumber = 0; busNumber < MAX_PCI_BUSES; busNumber++ )

{

for ( deviceNumber = 0;deviceNumber < PCI_MAX_DEVICES;deviceNumber++ )

{

slotNumber.u.bits.DeviceNumber = deviceNumber;

for ( functionNumber = 0; functionNumber < PCI_MAX_FUNCTION; functionNumber++ )

{

slotNumber.u.bits.FunctionNumber = functionNumber;

if (!HalGetBusData(PCIConfiguration,

busNumber,

slotNumber.u.AsULONG,

&pciData,

sizeof(ULONG)

) )

{

deviceNumber = PCI_MAX_DEVICES;

break;

}

if (pciData.VendorID == PCI_INVALID_VENDORID )

{

continue;

}

if ( ( VendorId != PCI_INVALID_VENDORID ) &&

( pciData.VendorID != VendorId || pciData.DeviceID != DeviceId ))

{

continue;

}

pPciDeviceLocation->BusNumber = busNumber;

pPciDeviceLocation->SlotNumber = slotNumber;

pPciDeviceLocation = &PciDeviceList->List[++count];

status = STATUS_SUCCESS;

}

}

}

// 获取设备的资源列表数据指针

status = HalAssignSlotResources(RegistryPath,

&pDevExt->ClassUnicodeString,

DriverObject,

DeviceObject,

pDevExt->InterfaceType,

pDevExt->BusNumber,

pDevExt->SlotNumber,

&pCmResourceList

);

4. I/O端口访问

在PC机上,I/O寻址方式与内存寻址方式不同,所以处理方法也不同。I/O空间是一个64K字节的寻址空间,I/O寻址没有实模式与保护模式之分,在各种模式下寻址方式相同。在Windows NT下,系统不允许处于Ring3级的用户程序和用户模式驱动程序直接使用I/O指令,对I/O端口进行访问,任何对I/O的操作都需要借助内核模式驱动来完成。在访问I/O端口时,使用READ_PORT_XXX与WRITE_PORT_XXX 函数来进行读写。I/O端口基地址使用从配置空间基址寄存器PCI Base Address 1中返回的I/O端口基地址。

示例代码如下:

RegValue = READ_PORT_ULONG(pBaseAddr+RegOffSet);

WRITE_PORT_ULONG(pBaseAddr+ RegOffset, RegValue);

5. 设备内存访问

Winsows 工作在32位保护模式下,保护模式与实模式的根本区别在于CPU寻址方式上的不同,这也是Windows驱动程序设计中需要着重解决的问题。Windows采用了分段、分页机制,使得一个程序可以很容易地在物理内存容量不一样的、配置范围差别很大的计算机上运行,编程人员使用虚拟存储器可以写出比任何实际配置的物理存储器都大得多的程序。每个虚拟地址由16位的段选择字和32位段偏移量组成。通过分段机制,系统由虚拟地址产生线性地址。再通过分页机制,由线性地址产生物理地址。线性地址被分割成页目录(Page Directory)、页表(Page Table)和页偏移(Offset)三个部分。当建立一个新的Win32进程时,操作系统会为它分配一块内存,并建立它自己的页目录、页表,页目录的地址也同时放入进程的现场信息中。当计算一个地址时,系统首先从CPU控制器CR3中读出页目录所在的地址,然后根据页目录得到页表所在的地址,再根据页表得到实际代码/数据页的页帧,最后再根据页偏移访问特定的单元。硬件设备读写的是物理内存,但应用程序读写的是虚拟地址,所以存在着将物理内存地址映射到用户程序线性地址的问题。

从物理内存到线性地址的转换是驱动程序需要完成的工作,可以在初始化驱动程序的进行。在已经获得设备的存储器基地址后,首先调用HalTranslateBusAddress()函数将总线相关的内存地址转换成系统的物理地址,然后调用MmMapIoSpace()函数将系统的物理地址映射到线性地址空间。在需要访问设备内存时,

相关文档
最新文档