LDD之PCI设备

一,PCI总线:

         1,PCI总线是系统总线;Host-Briage(PCI总线0)——PCI设备,PCI-PCI briage ——PCI总线1(图片来自精通Linux设备驱动程序开发)

                                                                                          


                Host-Briage 集成在北条芯片组中,PCI-PCI-Briage是透明设备,PCI设备通过Host-Briage与CPU通信,CPU将信息传递给Host-Briage,然后Host-Briage将信息传递给PCI设备,所以PCI总线与体系结构无关,PCI总线上链接各种PCI控制器(PCI设备),与北条芯片链接的PCI总线是编号为0是主PCI总线;

                  如下图:PCI总线结构图(图片来自网络:http://blog.sina.com.cn/s/blog_6472c4cc0100qbx5.html

                                                                  

                PCI设备有三类:主设备,从设备,桥设备,每个设备同一时刻只能是一种主设备或从设备,主设备能够取得总线控制权;PCI总线是多主的。

         2,PCI设备信息查看及寻址方式:

                   系统中的PCI设备信息可以从/proc/bus/pci/device , /sys/bus/pci/device 文件系统中查看;

                 如下截图:

                       


                          


                其中设备信息的表示格式为:    总线域(16位):总线编号(8位):设备编号(5位):功能编号(3位)

               由以上信息可以知道:每个总线域最多有256个PCI总线,每个总线最多有32个PCI设备,每个设备最多有8个功能;

             

                PCI设备配置寄存器地址表示:1位使能位:7位保留位:8位总线编号:5位设备编号:3位功能编号:6位配置寄存器偏移量:2位标志位

                访问PCI设备配置寄存器地址表示如图:

                 

3130-2423-1615-1110-87-210
使能位保留位8位总线编号5位设备编号3位功能编号6位寄存器偏移量00
               

                使能位决定是否允许访问配置空间;

                第一位和第二位表示每次读取4字节; 

         3,PCI设备配置空间结构,如下图(图片来自Linux设备驱动程序第三版):

                                                                                 


              PCI规范规定PCI配置空间大小位256字节,所以每个配置寄存器的位宽为8位,前64字节格式是固定的标准化的,剩下的由厂商决定;

              如上图前64字节配置空间前面四个字节为VendorId , DeviceId;

              偏移量为0x9的寄存器3个字节保存了ClassCode;

              偏移量为0xe的寄存器1个字节保存了HeaderType:0表示PCI设备,1表示PCI Briage;

              偏移量为0x10的寄存器之后每4个字节为一个BAR寄存器(IO内存或IO端口空间基地址寄存器),一共6个BAR寄存器;

                   每个BAR的地址格式如下图(图片来自网络):

                   如果BAR的地址映射的是IO地址空间:最低位为0,第二位预留,从第三位开始表示为地址值;

                  

                   如果BAR的地址映射的是内存地址空间:最低位为1;第二位和第三位表示体系架构,00表示32位架构,10表示64位架构;第三位表示是否可预取,对于具有边际效应的寄存器该值设为不可预取,比如某些控制寄存器;

                   

               

               地址最后一位表示为IO映射或内存映射:如果为1则为IO映射,如果为0则为内存映射;


               前64字节空间中剩下的寄存器中保存了subVendorId,subDeviceId,IRQ LINE,IRQ PIN等等;

               

               struct pci_device_id;

                Linux 将配置空间中的venderId,deviceId,classcode,subvendorId,subdeviceId,class_mask,driver_data(kernel_ulong_t,不是必须的)

                抽象为struct pci_device_id结构体,PCI驱动程序用该结构体告诉内核,本身支持什么样的PCI设备列表;

                MODULE_DEVICE_TABLE(pci,ids); 

                使用该宏将pci驱动程序支持的设备列表导出到用户空间,供热插拔系统为设备查找驱动程序使用;

                在编译模块的时候编译系统会抽取该宏数据并导出到用户空间;

         4,Linux PCI设备配置空间访问:

                在上面介绍的PCI规范的配置空间结构基础上,再介绍Linux 访问PCI配置空间的api操作函数:

                头文件:<linux/pci.h>

                读取配置空间:

                int pci_read_config_byte(struct pci_dev *dev,int where,u8 *val);

                int pci_read_config_word(struct pci_dev *dev,int where,u16 *val);

                int pci_read_config_dword(struct pci_dev *dev,int where,u32 *val);

                写入配置空间:              

                 int pci_write_config_byte(struct pci_dev *dev,int where,u8 val);

                 int pci_write_config_word(struct pci_dev *dev,int where,u16 val);

                 int pci_write_config_dword(struct pci_dev *dev,int where,u32 val);

                

                struct pci_device 为PCI设备结构,int where 为寄存器相对配置空间起始地址的偏移量,val保存读取的值或写入的值;

                 参数where通常传入pci.h中定义的寄存器偏移量的符号,例如:PCI_VERSION_ID,PCI_INTERRUPT_LINE;


                通过Linux resource来获取PCI设备配置空间信息:

                在Linux 系统中将设备地址映射的IO端口空间或IO内存空间均抽象为资源来管理,用struct resource 数据结构来表示;

                 因此PCI驱动程序获取PCI设备配置空间信息无需访问底层的PCI设备配置空间,而直接通过Linux资源管理来获取配置信息:

                 操作如下:

                  unsigned long pci_resource_start(struct pci_dev *dev,int bar);

                  该函数返回编号为bar的寄存器映射的区域范围的起始地址,参数bar为寄存器编号(0~5);

                  unsigned long pci_resource_end(struct pci_dev *dev,int bar);

                 该函数返回编号为bar的寄存器映射的区域的结束地址,bar的编号范围(0~5);

                  unsigned long pci_resource_flags(struct pci_dev *dev,int bar);

                  该函数返回编号为bar的寄存器映射的区域标志,这些标志定义在<linux/ioport.h>中,重要的有以下几个:

                  IORESOURCE_IO:映射到IO端口空间;

                  IORESOURCE_MEM:映射到内存空间;

                  IORESOURCE_PREFETCH:表示该区域是否为可预取的;

                  IORESOURCE_READONLY:表示该区域只读;


         5,Linux PCI设备驱动程序结构表示:

                Linux用struct pci_driver 结构体表示驱动程序,该结构体是PCI设备与PCI设备驱动程序的联系桥梁,通过该结构体可以查询到驱动程序的设备并初始化设备;

                比较重要的字段如下:

                struct pci_driver{

                           char *name;

                           struct pci_device_id *ids;

                           int (*probe)(struct pci_dev *dev,struct pci_device_id *id);

                           void (*remove)(struct pci_dev *dev);

                           int (*suspend)(struct pci_dev *dev,u32 state);

                           int (*resume)(struct pci_dev *dev);

                           ... ...

                 };

         6,Linux PCI设备驱动程序探测及注册:

                   int pci_probe(struct pci_dev *dev,struct pci_device_id *id){

                                  struct pci_privdata *data=NULL;

                                  struct resource *resource=NULL;

                                   if(pci_device_is_present(dev)){

                                                //1,不支持pci总线;

                                   }

                                      

                                   if(pci_enable_device(dev)){

                                               //2,使能设备;

                                    }


                                    //3,从配置空间获取信息;

                                    data=kmalloc(sizeof(struct pci_privdata),GFP_KERNEL);

                                    data->phy_addr=pci_resource_start(dev,1);

                                    data->size          =pci_resource_len(dev,1);

                                    data->flags         =pci_resource_flags(dev,1);

                                    if(data->flags != IORESOURCE_MEM){

                                             //4,判断是否为内存空间或IO端口空间;

                                     }


                                    //resource=request_mem_region(data->phy_addr,data->size,NAME);

                                      resource=pci_request_region(data->phy_addr,data->size,NAME);

                                     if(!resource){

                                             //5,独占设备;

                                     }


                                      //6,io内存映射,映射到内核虚拟地址空间;

                                     data->addr=ioremap(data->phy_addr,data->size);

                                       //7,设置PCI设备私有数据;

                                      pci_set_drvdata(dev,data);

                                      //8,设置PCI设备为DMA主设备;

                                     pci_set_master(dev);

                                    return0;

                    }


                   int  pci_remove(struct pci_dev *dev){

                         //与probe函数相反的顺序将probe函数中的资源都释放掉;

                         return 0;

                  }


                  //在驱动模块中注册驱动数据结构;

                  static int __init pci_module_init(void){

                          return pci_register_driver(&pci_driver);

                  }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值