PCI设备驱动开发

news/2024/11/14 15:36:41/

PCI总线协议

PCI(外设部件互连标准)总线标准是一种将系统外部设备连接起来的总线标准,它是PC中最重要的总线。
其他总线如ISA总线、USB等总线都挂在PCI总线之上。

PCI ( Peripheral Component Interconnect) 总线是当前最流行的总线之一,是由Intel
公司首先推出的一种局部总线。它定义了32位数据地址总线,并且可扩展为64位,其支
持突发读写操作,也同时可以支持多组外围设备。

PCI局部总线不能兼容在其之前出现的ISA、EISA、MCA (Micro Channel Architecture)
等总线。

但是大多数基于上述总线的设备可以通过自行设计PCI桥接电路挂接在PCI局部
总线之上。实际上在当前的PC体系结构内,几乎所有外部设备采用的各种各样的接口总
线,均是通过桥接电路挂接在PCI系统内的。

在这种PCI系统中,Host/PCI 桥称为北桥,连接主处理器总线到基础PCI局部总线。使用北桥来连接CPU是为了CPU的安全,其他外设如果直接连接CPU,电压的不稳定容易烧毁CPU。
PCI-ISA 桥称为南桥,连接基础PCI总线到ISA总线。其中南桥通常还含有中断控制器、IDE控制器、USB控制器和DMA控制器等设备。

南桥和北桥组成主板的芯片组。通过芯片组的扩展,实现了多种总线与基础PCI局部总线的桥接,从而实现了多种总线对整个PC系统的挂接。在基础PCI局部总线或PCI插入卡上,可以嵌入一个或多个PCI-PCI桥,从而实现在基础PCI局部总线上挂接多个PCI设备。如图16-1所示为PCI总线、扩展总线、处理器和存储器总线间的基本关系。

在这里插入图片描述

PCI配置空间简介

PCI有三个相互独立的物理地址空间:设备存储器地址空间、I/O 地址空间和配置空
间。配置空间是PCI所特有的一个物理空间。由于PCI支持设备即插即用,所以PCI设备
不占用固定的内存地址空间或IO地址空间,而是可以由操作系统决定其映射的基址。

系统加电时,BIOS 检测PCI总线,确定所有连接在PCI总线上的设备以及它们的配
置要求,并进行系统配置。所以,所有的PCI设备必须实现配置空间,从而能够实现参数
自动配置,实现真正的即插即用。

PCI总线规范定义的配置空间总长度为256个字节,配置信息按一定的顺序和大小依
次存放。前64个字节的配置空间称为配置头,对于所有的设备都-一样, 配置头的功能主
要是用来识别设备,定义主机访问PCI卡的方式(I/O访问或者存储器访问,还有中断信
息)。其余的192 个字节空间称为本地配置空间,主要定义卡上局部总线的特性、本地空间基地址及范围等。
PC12.2规范定义了三种配置头格式:类型0、类型1和类型2。其中类型1适用于
PCI-to-PCI桥设备,用于将两条PCI总线进行连接;类型2适用于PCI-CardBus (主要用
于笔记本的插卡式总线)桥,在PC Card规范中进行定义:类型0用于除类型1和类型2
以外所有的PCI设备。一般设备都采用类型0的配置头。

在这里插入图片描述
PCI大大地提高了系统的易配置性,这些是通过由PCI设备提供相应的功能来实现的。
虽然寄存器的具体位的设置因设备而异,但是所有的寄存器必须能够被读写。这些寄存器
的操作要能反映以下的信息:

(1) VendorID:厂商ID。知名的设备厂商的ID。 FFFFh是一个非法厂商ID, 它判断
PCI设备是否存在。

(2) DeviceID:设备ID。某厂商生产的设备的ID.操作系统就是凭着Vendor ID和
Device ID找到对应驱动程序的。

(3) ClassCode: 类代码。共三个字节,分别是类代码、子类代码及编程接口。类代
码不仅用于区分设备类型,还是编程接口的规范,这就是为什么会有通用驱动程序的原因。

(4) IRQLine: IRQ编号。PC机以前是靠两片8259芯片来管理16个硬件中断。现在
为了支持对称多处理器,有了APIC (高级可编程中断控制器),它支持管理24个中断。

(5) IRQ Pin:中断引脚。PCI 有4个中断引脚,该寄存器表明该设备连接的是哪个引脚。

(6)设备控制:这是由Command 寄存器来实现的,提供了控制设备对于PCI访问的
响应以及执行的能力,Command寄存器的具体各位的信息可以通过PCI规范查得。

(7)设备状态: Status寄存器用来记录一个PCI设备当前的状态,Status寄存器的具
体各位的信息也可以通过PCI规范查到。

(8)基地址:基地址寄存器用来存放PCI设备映射的存储器地址或者使用的I/O空间
的首地址。PCI规范提供一种机制使得I/O和存储器分开,即在基地址寄存器的最低位上,
如果是0,表示该基地址寄存器指向的是一个存储器空间,而如果是1,就是指向一个I/O
空间。

通过I/O端口直接读取PCI配置空间

访问PCI配置空间可通过访问两个寄存器,CONFIG_ADDRESS寄存器和CONFIG_
DATA寄存器。这两个寄存器在PC中分别对应着CF8h和CFCh端口,并且是32位端口,
即读写要用32位的IN和OUT汇编指令。其中CONFIG_ ADDRESS寄存器的内容如图16-3
所示。

在这里插入图片描述
每个PCI设备可应用三个信息进行定位,即Bus Number、Device Number及Function
Number.另外,PCI配置空间- -共是256个字节,被分割成64个4字节的寄存器,从0-63
编号。
每次要访问PCI配置空间时,先设置CONFIG_ADDRESS寄存器.这时CONFIG_DATA
寄存器中的内容就对应着该PCI配置空间中的相应寄存器。
例如,要访问Bus Number=0,Device Number=1, Function Number=2的PCI配置空间
中的Status和Command。

首先查看Status 和Command在PCI配置空间位于第1号寄存器(最开始的寄存器是
0号),然后将Bus Number(总线号), Device Number(设备号), Function Number(功能号)按照图16-3中的格式填好,
即0x80000A04 (二进制是1000 0000 0000 0000 0000 1010 0000 0100)。2-7位的值代表寄存器号,这里是1,说明就是04h寄存器(Status和Command)

然后设置CONFIG ADDRESS寄存器(0xCF8 端口)为0x80000A04,然后查看CONFIG_ DATA寄
存器(0xCFC端口),得出的内容即Status和Command (Status 在高16位,Command 在
低16位)。

#include <ntddk.h>#ifdef _AMD64_
ULONG
HalGetBusData(
_In_ BUS_DATA_TYPE BusDataType,
_In_ ULONG BusNumber,
_In_ ULONG SlotNumber,
_Out_writes_bytes_(Length) PVOID Buffer,
_In_ ULONG Length
);
#endif
VOID Unload(IN PDRIVER_OBJECT DriverObject)
{KdPrint(("驱动卸载\n"));}VOID DisplayConfig(int bus,int dev,int func)
{PCI_COMMON_CONFIG Config;PPCI_COMMON_HEADER pConfigHeader;PCI_SLOT_NUMBER SlotNumber;ULONG Addr = 0;ULONG Data;ULONG i;SlotNumber.u.AsULONG = 0;SlotNumber.u.bits.DeviceNumber = dev;SlotNumber.u.bits.FunctionNumber = func;Addr = 0x80000000 | (bus << 16) | (SlotNumber.u.AsULONG << 8);for (i = 0; i < 40; i += 4){Addr |= i;WRITE_PORT_ULONG((PULONG)0xcf8, Addr|i);Data = READ_PORT_ULONG((PULONG)0xcfc);((PULONG)&Config)[i>>2] = Data;}pConfigHeader = (PPCI_COMMON_HEADER)&Config;if (pConfigHeader->VendorID == 0xffff || pConfigHeader->VendorID == 0){return;}KdPrint(("VendorID:%x\n", pConfigHeader->VendorID));KdPrint(("DeviceID:%x\n", pConfigHeader->DeviceID));KdPrint(("Command:%x\n", pConfigHeader->Command));KdPrint(("Status:%x\n", pConfigHeader->Status));KdPrint(("RevisionID:%x\n", pConfigHeader->RevisionID));KdPrint(("ProgIf:%x\n", pConfigHeader->ProgIf));KdPrint(("SubClass:%x\n", pConfigHeader->SubClass));KdPrint(("BaseClass:%x\n", pConfigHeader->BaseClass));KdPrint(("CacheLineSize:%x\n", pConfigHeader->CacheLineSize));KdPrint(("LatencyTimer:%x\n", pConfigHeader->LatencyTimer));KdPrint(("HeaderType:%x\n", pConfigHeader->HeaderType));KdPrint(("BIST:%x\n", pConfigHeader->BIST));for (i = 0; i < 6;i++){KdPrint(("Bar:%x\n", pConfigHeader->u.type0.BaseAddresses[i]));}KdPrint(("VendorID:%x\n", pConfigHeader->u.type0.InterruptLine));KdPrint(("VendorID:%x\n", pConfigHeader->u.type0.InterruptPin));
}VOID DisplayConfig11111(int bus, int dev, int func)
{PCI_SLOT_NUMBER SlotNumber;PCI_COMMON_CONFIG Config;PPCI_COMMON_HEADER pConfigHeader;ULONG Size;ULONG i;SlotNumber.u.AsULONG = 0;SlotNumber.u.bits.DeviceNumber = dev;SlotNumber.u.bits.FunctionNumber = func;Size = HalGetBusData(PCIConfiguration, bus, SlotNumber.u.AsULONG, &Config, PCI_COMMON_HDR_LENGTH);if (Size == PCI_COMMON_HDR_LENGTH){pConfigHeader = (PPCI_COMMON_HEADER)&Config;KdPrint(("VendorID:%x\n", pConfigHeader->VendorID));KdPrint(("DeviceID:%x\n", pConfigHeader->DeviceID));KdPrint(("Command:%x\n", pConfigHeader->Command));KdPrint(("Status:%x\n", pConfigHeader->Status));KdPrint(("RevisionID:%x\n", pConfigHeader->RevisionID));KdPrint(("ProgIf:%x\n", pConfigHeader->ProgIf));KdPrint(("SubClass:%x\n", pConfigHeader->SubClass));KdPrint(("BaseClass:%x\n", pConfigHeader->BaseClass));KdPrint(("CacheLineSize:%x\n", pConfigHeader->CacheLineSize));KdPrint(("LatencyTimer:%x\n", pConfigHeader->LatencyTimer));KdPrint(("HeaderType:%x\n", pConfigHeader->HeaderType));KdPrint(("BIST:%x\n", pConfigHeader->BIST));for (i = 0; i < 6; i++){KdPrint(("Bar:%x\n", pConfigHeader->u.type0.BaseAddresses[i]));}KdPrint(("VendorID:%x\n", pConfigHeader->u.type0.InterruptLine));KdPrint(("VendorID:%x\n", pConfigHeader->u.type0.InterruptPin));}else{}}VOID GetAllPciDevice()
{PCI_SLOT_NUMBER     slotData;PCI_COMMON_CONFIG   pciData;PCI_COMMON_CONFIG   MyPciData = {0};ULONG               pciBus;ULONG               slotNumber;ULONG               functionNumber;PPCI_COMMON_HEADER pConfigHeader = NULL;ULONG               length;slotData.u.AsULONG = 0;for (pciBus = 0; pciBus < PCI_MAX_BRIDGE_NUMBER; pciBus++){for (slotNumber = 0; slotNumber < PCI_MAX_DEVICES; slotNumber++){slotData.u.bits.DeviceNumber = slotNumber;for (functionNumber = 0; functionNumber < PCI_MAX_FUNCTION; functionNumber++){slotData.u.bits.FunctionNumber = functionNumber;RtlZeroMemory(&pciData,sizeof(PCI_COMMON_CONFIG));RtlZeroMemory(&MyPciData, sizeof(PCI_COMMON_CONFIG));//和函数HalGetBusData功能差不多,HalGetBusDataByOffset可以控制读取的起始位置length = HalGetBusDataByOffset(PCIConfiguration,pciBus,slotData.u.AsULONG,&pciData.VendorID,FIELD_OFFSET(PCI_COMMON_CONFIG, VendorID), //读取的起始位置PCI_COMMON_HDR_LENGTH);	   //读取的大小if (length == 0){continue;}pConfigHeader = (PPCI_COMMON_HEADER)&pciData;if (PCI_INVALID_VENDORID == pConfigHeader->VendorID){continue;}KdPrint(("VendorID:%x\n", pConfigHeader->VendorID));KdPrint(("DeviceID:%x\n", pConfigHeader->DeviceID));KdPrint(("HeaderType:%x\n", pConfigHeader->HeaderType));KdPrint(("Command:%x\n", pConfigHeader->Command));KdPrint(("Status:%x\n", pConfigHeader->Status));KdPrint(("RevisionID:%x\n", pConfigHeader->RevisionID));KdPrint(("ProgIf:%x\n", pConfigHeader->ProgIf));KdPrint(("SubClass:%x\n", pConfigHeader->SubClass));KdPrint(("BaseClass:%x\n", pConfigHeader->BaseClass));KdPrint(("CacheLineSize:%x\n", pConfigHeader->CacheLineSize));KdPrint(("LatencyTimer:%x\n", pConfigHeader->LatencyTimer));KdPrint(("BIST:%x\n", pConfigHeader->BIST));for (int i = 0; i < 6; i++){KdPrint(("Bar:%x\n", pConfigHeader->u.type0.BaseAddresses[i]));}KdPrint(("InterruptLine:%x\n", pConfigHeader->u.type0.InterruptLine));KdPrint(("InterruptPin:%x\n\n\n", pConfigHeader->u.type0.InterruptPin));}}}
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{int bus, dev, func;KdPrint(("驱动加载\n"));DriverObject->DriverUnload = Unload;for (bus = 0; bus < PCI_MAX_BRIDGE_NUMBER;bus++){for (dev = 0; dev < PCI_MAX_DEVICES;dev++){for (func = 0; func < PCI_MAX_FUNCTION;func++){DisplayConfig(bus, dev, func);}}}return STATUS_SUCCESS;
}

WDM 方式三:转发IRP_MN_START_DEVICE

与枚举设备资源代码一样.

使用PCI配置空间

在这里插入图片描述

typedef struct _DEVICE_EXTENSION
{PDEVICE_OBJECT fdo;PDEVICE_OBJECT NextStackDevice;UNICODE_STRING interfaceName;	// 接口名//中断对象PKINTERRUPT InterruptObject;PUCHAR BarMem0;PUCHAR BarMem1;PUCHAR PortBase;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;BOOLEAN
InterruptService(
_In_ struct _KINTERRUPT *Interrupt,
_In_opt_ PVOID ServiceContext
)
{PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)ServiceContext;char Buffer[1024] = { 0 };READ_PORT_BUFFER_UCHAR(pdx->BarMem0, Buffer,1024);WRITE_PORT_BUFFER_UCHAR(pdx->BarMem0, Buffer, 1024);UCHAR c=READ_PORT_UCHAR(pdx->PortBase);return TRUE;
}NTSTATUS InitMyPci(PDEVICE_EXTENSION pdx,PCM_PARTIAL_RESOURCE_LIST resource)
{NTSTATUS status = STATUS_SUCCESS;KdPrint(("------------------------\n"));//枚举资源ULONG nres = resource->Count;ULONG i;BOOLEAN bFirst = TRUE;for (ULONG i = 0; i < resource->Count;i++){switch (resource->PartialDescriptors[i].Type){case CmResourceTypeMemory:KdPrint(("内存%11x:%x", resource->PartialDescriptors[i].u.Memory.Start.QuadPart,resource->PartialDescriptors[i].u.Memory.Length));if (bFirst){pdx->BarMem0 = (PUCHAR)MmMapIoSpace(resource->PartialDescriptors[i].u.Memory.Start,//要映射的物理地址resource->PartialDescriptors[i].u.Memory.Length,//映射的长度MmNonCached);bFirst = FALSE;}else{pdx->BarMem1 = (PUCHAR)MmMapIoSpace(resource->PartialDescriptors[i].u.Memory.Start,//要映射的物理地址resource->PartialDescriptors[i].u.Memory.Length,//映射的长度MmNonCached);}break;case CmResourceTypePort:KdPrint(("端口%11x:%x", resource->PartialDescriptors[i].u.Port.Start.QuadPart,resource->PartialDescriptors[i].u.Port.Length));//判断是否需要映射  端口一般不需要映射if (resource->PartialDescriptors[i].Flags==CM_RESOURCE_PORT_IO){pdx->PortBase =(PUCHAR)resource->PartialDescriptors[i].u.Port.Start.LowPart;}else{pdx->PortBase = (PUCHAR)MmMapIoSpace(resource->PartialDescriptors[i].u.Port.Start,//要映射的物理地址resource->PartialDescriptors[i].u.Port.Length,//映射的长度MmNonCached);}break;case CmResourceTypeInterrupt:KdPrint(("中断亲缘性:%11x 中断等级:%x 中断向量:%x\n", resource->PartialDescriptors[i].u.Interrupt.Affinity,resource->PartialDescriptors[i].u.Interrupt.Level,resource->PartialDescriptors[i].u.Interrupt.Vector));IoConnectInterrupt(&pdx->InterruptObject, InterruptService, pdx, NULL,resource->PartialDescriptors[i].u.Interrupt.Vector,(KIRQL)resource->PartialDescriptors[i].u.Interrupt.Level,(KIRQL)resource->PartialDescriptors[i].u.Interrupt.Level,resource->PartialDescriptors[i].Flags == CM_RESOURCE_INTERRUPT_LATCHED ? Latched : LevelSensitive,TRUE,resource->PartialDescriptors[i].u.Interrupt.Affinity,FALSE);break;default:DbgPrint("unknown resource\n");break;}}return status;
}

http://www.ppmy.cn/news/274656.html

相关文章

PCI驱动框架简单分析

一、PCI 概念介绍 PCI是CPU和外围设备通信的高速传输总线。PCI规范能够实现32位并行数据传输&#xff0c;工作频率为 33MHz 或 66MHz &#xff0c;最大吞吐率高达266MB/s,PCI的衍生物包括 CardBus、mini-PCI、PCI-Express、cPCI等。 PCI总线体系结构是一种层次式的体系结构。在…

深入分析Linux PCI驱动框架分析(二)

说明&#xff1a; Kernel版本&#xff1a;4.14ARM64处理器使用工具&#xff1a;Source Insight 3.5&#xff0c; Visio 1. 概述 本文将分析Linux PCI子系统的框架&#xff0c;主要围绕Linux PCI子系统的初始化以及枚举过程分析&#xff1b;如果对具体的硬件缺乏了解&#xf…

Linux驱动学习--初识PCI驱动(一)

PCI是什么 PCI—Peripheral Component Interconnect&#xff0c;外围设备互联总线。是一种广泛采用的总线标准&#xff0c;它提供了许多优于其它总线标准&#xff08;如EISA&#xff09;的新特性&#xff0c;目前已经成为计算机系统中应用最为广泛&#xff0c;并且最为通用的总…

Linux驱动学习--PCI设备驱动

目录 一、引言 二、PCI总线介绍 三、PCI设备驱动组成 四、查看本机上的PCI设备 一、引言 PCI总线在linux中应用的十分广泛&#xff0c;本文就来简单介绍一下 二、PCI总线描述 1、PCI总线结构 PCI是CPU和外围设备通信的高速传输总线。普通PCI总线带宽一般为132MB/s(在3…

PCI驱动

一、PCI总线 1、PCI桥 Host/PCI桥&#xff1a;用于连接CPU与PCI根总线&#xff0c;即“北桥芯片组” PCI/ISA桥&#xff1a;用于连接PCI与旧的ISA总线&#xff0c;即“南桥芯片组” PCI-to-PCI桥&#xff1a;用于连接PCI主总线与次总线 2、pci_bus结构体&#xff08;PCI总线结构…

pci驱动框架

PCI 总线架构主要被分成三部分&#xff1a; 1.PCI 设备。符合 PCI 总线标准的设备就被称为 PCI 设备&#xff0c;PCI 总线架构中可以包含多个 PCI 设备。图中的 Audio 、LAN 都是一个 PCI 设备。PCI 设备同时也分为主设备和目标设备两种&#xff0c;主设备是一次访问操作的发…

pci设备驱动

原文地址&#xff1a;https://www.cnblogs.com/xiaoya901109/archive/2012/12/14/2818057.html 一&#xff0c;初始化设备模块 当Linux内核启动并完成对所有PCI设备进行扫描、登录和分配资源等初始化操作的同时&#xff0c;会建立起系统中所有PCI设备的拓扑结构&#xff0c;此后…

PCI驱动开发学习笔记(一)

PCIE简介 PCIe作为串行总线的一种&#xff0c;它的发展必然和另一种总线架构密不可分&#xff1a;并行总线。 像PCIe接口的显卡、声卡、网卡&#xff0c;都属于功能设备&#xff0c;在PCIe规范中&#xff0c;我们统称为Endpoint&#xff08;简称EP&#xff09;。还有其他两类设…