linux 枚举(linux嵌入式软件开发)
老铁们,大家好,相信还有很多朋友对于linux 枚举和linux嵌入式软件开发的相关问题不太懂,没关系,今天就由我来为大家分享分享linux 枚举以及linux嵌入式软件开发的问题,文章篇幅可能偏长,希望可以帮助到大家,下面一起来看看吧!
如何让linux重新枚举pci设备
在Linux下,lspci可以枚举所有PCI设备。它是通过读取PCI配置空间(PCI Configuration Space)信息来实现PCI设备的枚举的。这里,我通过两种方式来简单的模拟一下lspci的功能。一种是通过PCI总线的CF8和CFC端口来枚举(参考PCI总线规范);另一种是利用proc filesystem。
方法一:这种方法需要对端口进行操作,在Linux下,普通应用程序没有权限读写I/O端口,需要通过iopl或ioperm来提升权限,我的代码里面使用iopl。
[cpp] view plaincopyprint?
/*
* Enum all pci device via the PCI config register(CF8 and CFC).
*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/io.h>
#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<= PCI_MAX_BUS; bus++){
for(dev= 0; dev<= PCI_MAX_DEV; dev++){
for(fun= 0; fun<= PCI_MAX_FUN; fun++){
addr= 0x80000000L|(bus<<16)|(dev<<11)|(fun<<8);
outl(addr, CONFIG_ADDRESS);
data= inl(CONFIG_DATA);
/* Identify vendor ID*/
if((data!= 0xFFFFFFFF)&&(data!= 0)){
printf("%02X:%02X:%02X", bus, dev, fun);
printf("%04X:%04X", data&0xFFFF, data>>16);
addr= 0x80000000L|(bus<<16)|(dev<<11)|(fun<<8)| PCICFG_REG_RID;
outl(addr, CONFIG_ADDRESS);
data= inl(CONFIG_DATA);
if(data&0xFF){
printf("(rev%02X)\n", data&0xFF);
} else{
printf("\n");
}
}
} end func
}// end device
}// end bus
}
int main()
{
int ret;
/* Enable r/w permission of all 65536 ports*/
ret= iopl(3);
if(ret< 0){
perror("iopl set error");
return 1;
}
list_pci_devices();
/* Disable r/w permission of all 65536 ports*/
ret= iopl(0);
if(ret< 0){
perror("iopl set error");
return 1;
}
return 0;
}
方法二:这种方法需不需要对端口进行操作,而是利用Linux procfs来实现对PCI配置空间的访问。
[cpp] view plaincopyprint?
/*
* Enum all pci device via/proc/bus/pci/.
*/
#include<stdio.h>
#include<stdint.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<unistd.h>
#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<= PCI_MAX_BUS; bus++){
for(dev= 0; dev<= PCI_MAX_DEV; dev++){
for(fun= 0; fun<= PCI_MAX_FUN; fun++){
char proc_name[64];
int cfg_handle;
uint32_t data;
uint16_t vid, did;
uint8_t rid;
snprintf(proc_name, sizeof(proc_name),
"/proc/bus/pci/%02x/%02x.%x", bus, dev, fun);
cfg_handle= open(proc_name, O_RDWR);
if(cfg_handle<= 0)
continue;
lseek(cfg_handle, PCICFG_REG_VID, SEEK_SET);
read(cfg_handle,&data, sizeof(data));
/* Identify vendor ID*/
if((data!= 0xFFFFFFFF)&&(data!= 0)){
lseek(cfg_handle, PCICFG_REG_RID, SEEK_SET);
read(cfg_handle,&rid, sizeof(rid));
vid= data&0xFFFF;
did= data>>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()等。
[cpp] 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文件系统详解
Linux进程管理---实时调度
Linux内核内存管理-缺页异常
Linux内核内存管理-brk系统调用
PCI设备驱动简介:PCI设备驱动遵循设备驱动模型,使用设备模型的相应函数。PCI设备被挂载到PCI总线的device队列,而对应的驱动则挂载到pci总线的driver队列。安装PCI设备驱动与USB设备驱动模式相似,主要复杂之处在于如何发现设备并将其添加到PCI设备队列中。
PIC架构概貌:所有根总线链接在pci_root_buses链表中,pci_bus与device之间建立连接,pci_bus与它的下层总线通过children链表关联。每个pci设备的pci_dev->bus指向所属的pci_bus,而pci_dev->bus_list则链接在它所属bus的device链表上。所有pci设备链接在pci_device链表中。
PIC设备的配置空间:每个PCI设备有最多256个连续配置空间,包含厂商ID、设备ID、IRQ、设备存储区信息等。通过动态查询PCI设备信息的PCI总线功能,我们可以在x86平台上使用保留的0xCF8~0xCFF的8个寄存器进行读写操作。格式包括总线号、设备号、功能号、寄存器号以及有效位。
总线枚举入口分析:PCI代码分为平台相关与平台无关两部分,PCI设备的枚举由pcibios_scan_root()函数完成。在x86平台下,这个过程通常在pci_legacy_init()函数中被调用。通过分析pci_direct_init()函数,我们了解到PCI设备的枚举过程主要依赖于pci_direct_probe()函数的返回值,即使用type1配置机制。
PCI设备的枚举过程:pcibios_scan_root()从根总线开始枚举设备,通过pci_scan_bus_parented()函数进一步扫描子总线,最终通过pci_scan_slot()函数扫描每个设备的所有功能号对应的设备。对于每个设备,pci_scan_single_device()函数检查其是否存在,并将设备添加到所属总线的devices链表上。
PCI设备信息的读取与处理:对于常规设备,读取6个存储区间和一个ROM;PCI桥设备包含2个存储区间和一个ROM;Cardbus设备则只有一个存储区间但没有ROM。设备IRQ号、内部存储区间等信息的确定与处理涉及具体寄存器的读取与解析。
PCI桥的处理:在枚举PCI桥时,除了常规设备配置字段,还需处理过滤窗口配置,以控制地址访问方向和启用内存访问、I/O访问功能。PCI桥提供三个过滤窗口,分别用于控制地址访问方向。读取PCI桥配置信息的pci_read_bridge_bases()函数负责完成这一任务。
总结:Linux的PCI架构采用深度优先遍历算法进行设备枚举。通过这一章的分析,读者应能理解PCI架构并解决设备枚举过程中的疑问。后续章节将深入分析其他PCI架构相关问题。
C语言中的枚举类型和在Linux系统中的作用 是什么
枚举类型在C语言中,用于定义一组命名的整型常数。例如,表示星期的枚举:SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY。枚举类型类似于结构与联合的说明形式,但其成员被赋予特定的整数值。当枚举未初始化时,成员按0、1、2等顺序分配。若成员赋值,后续成员值依次递增。例如,枚举成员x1、x2、x3、x4的值分别为0、1、2、3。
定义枚举时,每个成员后以逗号分隔,最后一个成员可省略逗号。在初始化枚举成员时,可赋予负数,后续成员值仍按递增规则确定。枚举变量只能取枚举说明结构中的成员常量。例如,枚举成员x1赋值5后,x3的值为7。
枚举类型在C语言中的使用广泛,特别是在操作系统代码中,用于定义标记整型常量。它相当于定义了一个整型常量表,便于在应用中查找。枚举的关键字在C语言中使用频繁,其功能通常能通过宏定义实现,但枚举支持类型检查,有助于编译器检测错误。