Linux PCI 总线驱动详解 (linux pci 总线驱动)

PCI(Peripheral Component Interconnect)总线是计算机中的一种总线接口标准,它是连接计算机主板和外部设备的接口。而Linux是一种自由软件操作系统,它支持多种硬件体系结构,能够运行在各种设备上,包括PC、服务器、嵌入式设备等。在Linux系统中,PCI总线驱动是一种非常重要的设备驱动程序,它负责管理PCI总线,并控制PCI外设的工作。本文将详细介绍Linux PCI总线驱动的工作原理、使用方法以及开发流程等内容。

一、PCI总线驱动概述

PCI总线驱动是Linux内核中的一个模块,它负责管理PCI总线上的所有设备,包括PCI桥、PCI设备等。这个模块通常被称为pci模块,用户可以通过pci模块中提供的API来进行PCI设备的控制和管理。

PCI总线驱动的主要工作有以下几个方面:

1. PCI设备的发现和枚举:当Linux系统启动时,pci模块会扫描PCI总线,寻找PCI设备,并分配资源。PCI设备的地址和IRQ等资源都是由pci模块分配的。

2. PCI设备的初始化和控制:在PCI设备被发现后,pci模块会执行设备的初始化代码,并将设备的信息保存在内核数据结构中,供其他驱动程序使用。用户程序可以通过pci模块提供的API来控制PCI设备的工作,例如读写PCI设备的寄存器、发送控制命令等。

3. PCI设备的中断处理:当PCI设备有数据到达时,它会产生中断信号,通知操作系统来处理数据。PCI总线驱动会接收中断信号,并将其转发给相应的设备驱动。设备驱动在收到中断信号后,就可以处理数据,并发送回应给PCI设备。

二、使用PCI总线驱动

在Linux系统中,用户可以使用pci模块提供的api来访问PCI设备。下面是一些常用的libpci API的介绍:

1. pci_enable_device():该API用于在PCI设备上启用总线主人控制器。当PCI设备被发现时,它可能处于禁用状态,需要使用此函数来启用它。

2. pci_request_regions():使用此API请求PCI设备的I/O端口和资源,访问这些资源可以进行读写操作。当系统中同时存在多个设备时,必须为每个设备分配不同的I/O端口和资源。

3. pci_alloc_consistent():使用此API为PCI设备分配连续的DMA内存区域,通常用于数据传输。PCI设备使用DMA传输数据,需要分配一段连续的物理内存用于数据传输,否则将会出现内存碎片等问题。

4. pci_irq_*():使用此API来管理PCI设备的中断。PCI设备产生中断时,需要将中断信号转发给相应的驱动程序来处理。与IRQ相关的API包括pci_enable_msi()和pci_request_irq()。

5. pci_read_config_*()和pci_write_config_*():使用此API访问PCI设备的配置空间,这个配置空间是一个硬件专用寄存器,用于存储PCI设备的各种配置信息,例如厂商ID、设备ID、IRQ等信息。

三、开发PCI驱动的流程

在Linux系统中,开发PCI驱动的流程主要包括以下步骤:

1. 设计PCI设备:这个过程需要我们设计PCI设备的硬件电路、定义各种寄存器和接口等。根据设计的PCI设备硬件电路来编写相应的硬件描述文件,例如Verilog或VHDL等。

2. 编写驱动程序:驱动程序是连接硬件设备和操作系统的中间件,它负责管理硬件设备和操作系统之间的通信。驱动程序通常包括一个设备驱动和一个总线驱动。设备驱动用于处理PCI设备的特定功能,总线驱动则用于管理PCI总线和PCI设备。驱动程序通常使用C语言编写。

3. 编译驱动程序:驱动程序一般是一个内核模块,需要将其编译成共享库,可以使用make命令进行编译。

4. 安装驱动程序:使用inod命令将驱动程序加载到Linux内核中。驱动程序会根据其规格进行配置,可以在/etc/modprobe.conf文件中进行配置。

5. 测试驱动程序:在驱动程序加载后,需要进行测试以确保其正常工作。测试过程包括读写内存、发送命令、观察输出等操作。

在Linux系统中,PCI总线驱动是连接计算机主板和外部设备的关键技术之一。它负责管理PCI总线和PCI设备,控制数据传输和中断处理。本文简要介绍了PCI总线驱动的工作原理、使用方法和开发流程等内容,希望对读者有所帮助。

相关问题拓展阅读:

如何让linux重新枚举pci设备

在Linux下,lspci可以枚举所有PCI设镇梁备。它是通过读取PCI配置空间(PCI Configuration Space)信息来实现PCI设备的枚举的。这里,我通过两种方式来简单的模拟一下lspci的功能。一种是通过PCI总线的CF8和CFC端口来枚举(参考PCI总线规范);另一种是利用proc filesystem。

  方法一:这种方法需要对端口进行操作,在Linux下,普通应用程序没有权限读写I/O 端口,需圆仿要通过iopl或ioperm来提升权限,我的代码里面使用iopl。

  

   view plaincopyprint?

  /*

  * Enum all pci device via the PCI config register(CF8 and CFC).

  */

  #include

  #include

  #include

  #include

  

  #define PCI_MAX_BUS 255 /* 8 bits (0 ~ 255) */

  #define PCI_MAX_DEV 31 /* 5 bits (0 ~ 31) */

  #define PCI_MAX_FUN 7 /* 3 bits (0 ~ 7) */

  

  #define CONFIG_ADDRESS 0xCF8

  #define CONFIG_DATA 0xCFC

  

  #define PCICFG_REG_VID 0x00 /* Vendor id, 2 bytes */

  #define PCICFG_REG_DID 0x02 /* Device id, 2 bytes */

  #define PCICFG_REG_CMD 0x04 /* Command register, 2 bytes */

  #define PCICFG_REG_STAT 0x06 /* Status register, 2 bytes */

  #define PCICFG_REG_RID 0x08 /* Revision id, 1 byte */

  

  

  void list_pci_devices()

  {

  unsigned int bus, dev, fun;

  unsigned int addr, data;

  

  //printf(“BB:DD:FF VID:DID\n”);

  

  

  for (bus = 0; bus >16);

  addr = 0xL | (bus

  #include

  #include

  #include

  #include

  #include

  

  #define PCI_MAX_BUS 255 /* 8 bits (0 ~ 255) */

  #define PCI_MAX_DEV 31 /* 5 bits (0 ~ 31) */

  #define PCI_MAX_FUN 7 /* 3 bits (0 ~ 7) */

  

  /*

  * PCI Configuration Header offsets

  */

  #define PCICFG_REG_VID 0x00 /* Vendor id, 2 bytes */

  #define PCICFG_REG_DID 0x02 /* Device id, 2 bytes */

  #define PCICFG_REG_CMD 0x04 /* Command register, 2 bytes */

  #define PCICFG_REG_STAT 0x06 /* Status register, 2 bytes */

  #define PCICFG_REG_RID 0x08 /* Revision id, 1 byte */

  #define PCICFG_REG_PROG_INTF 0x09 /* Programming interface code, 1 byte */

  #define PCICFG_REG_SUBCLASS 0x0A /* Sub-class code, 1 byte */

  #define PCICFG_REG_BASCLASS 0x0B /* Base class code, 1 byte */

  #define PCICFG_REG_CACHE_LINESZ 0x0C /* Cache line size, 1 byte */

  #define PCICFG_REG_LATENCY_TIMER 0x0D /* Latency timer, 1 byte */

  #define PCICFG_REG_HEADER_TYPE 0x0E /* Header type, 1 byte */

  #define PCICFG_REG_BIST 0x0F /* Builtin self test, 1 byte */

  #define PCICFG_REG_BAR0 0x10 /* Base addr register 0, 4 bytes */

  #define PCICFG_REG_BAR1 0x14 /* Base addr register 1, 4 bytes */

  #define PCICFG_REG_BAR2 0x18 /* Base addr register 2, 4 bytes */

  #define PCICFG_REG_BAR3 0x1C /* Base addr register 3, 4 bytes */

  #define PCICFG_REG_BAR4 0x20 /* Base addr register 4, 4 bytes */

  #define PCICFG_REG_BAR5 0x24 /* Base addr register 5, 4 bytes */

  #define PCICFG_REG_CIS 0x28 /* Cardbus CIS Pointer */

  #define PCICFG_REG_SVID 0x2C /* Subsystem Vendor ID, 2 bytes */

  #define PCICFG_REG_SDID 0x2E /* Subsystem ID, 2 bytes */

  #define PCICFG_REG_ROMBAR 0x30 /* ROM base register, 4 bytes */

  #define PCICFG_REG_CAPPTR 0x34 /* Capabilities pointer, 1 byte */

  #define PCICFG_REG_INT_LINE 0x3C /* Interrupt line, 1 byte */

  #define PCICFG_REG_INT_PIN 0x3D /* Interrupt pin, 1 byte */

  #define PCICFG_REG_MIN_GNT 0x3E /* Minimum grant, 1 byte */

  #define PCICFG_REG_MAX_LAT 0x3F /* Maximum lat, 1 byte */

  

  

  void list_pci_devices()

  {

  unsigned int bus, dev, fun;

  

  //printf(“BB:DD:FF VID:DID(RID)\n”);

  

  

  for (bus = 0; bus >16;

  

  printf(“%02X:%02X:%02X”, bus, dev, fun);

  if (rid > 0) {

  printf(” %04X:%04X (rev %02X)\n”, vid, did, rid);

  } else {

  printf(” %04X:%04X\n”, vid, did);

  }

  }

  } // end func

  } // end device

  } // end bus

  }

  

  int main(int argc, char **argv)

  {

  list_pci_devices();

  

  return 0;

  }

  

  这两种方法各有优缺点,之一种方法方便移植到其他OS,第二种就只适用于Linux。但是,之一种方法需要对I/O port进行直接操作。第二种就不需要。

  注意:执行这两段代码时,需要超级用户(root) 权限。

  补充:今天在枚举 Westmere-EP Processor(Intel Xeon Processor 5500 Series(Nehalem-EP))的 IMC(Integrated Memory Controller)时发现一个问题。lspci无法枚举到IMC设备。Westmere-EP 是 Intel 新的处理器架构。和以往的CPU不一样,它把Memory Controller集成到了CPU里面。IMC控制器被映射到了PCI总线上,Bus Number 是0xFE~0xFF,procfs(/proc/bus/pci/)下没有这几个设备。但是,通过 CF8/CFC 端口可以枚举到这些设备。

  3. 这段代码是在驱动中可以用来查找特定的pci device,并且返回一个pci_dev的结构体变量。通过这样一个struct变量,内核提供的接口函数可以直接套用,如pci_read_config_word(),pci_write_config_word()等。

   view plaincopyprint?

  void list_pci_device()

  {

  struct pci_dev *dev;

  struct pci_bus *bus,*childbus;

  

  list_for_each_entry(bus, &pci_root_buses, node) { //globle pci_root_buses in pci.h

  list_for_each_entry(dev, &bus->devices, bus_list) { // for bus 0

  printk(“%02X:%02X:%02X %04X:%04X\n”,dev->bus->number,dev->devfn >> 3, dev->devfn & 0x07,dev->vendor,dev->device);

  }

  list_for_each_entry(childbus, &bus->children,node) { // for bus 1,2,3,…

  list_for_each_entry(dev, &childbus->devices, bus_list) {

  printk(“%02X:%02X:%02X %04X:%04X\n”,dev->bus->number,dev->devfn >> 3, dev->devfn & 0x07,dev->vendor,dev->device);

  }

  }

linux pci 总线驱动的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux pci 总线驱动,Linux PCI 总线驱动详解,如何让linux重新枚举pci设备的信息别忘了在本站进行查找喔。


数据运维技术 » Linux PCI 总线驱动详解 (linux pci 总线驱动)