Chinaunix首页 | 论坛 | 博客
  • 博客访问: 254204
  • 博文数量: 52
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1538
  • 用 户 组: 普通用户
  • 注册时间: 2013-04-24 07:45
个人简介

生活就像海洋,只有意志坚强的人,才能到达彼岸。

文章存档

2013年(52)

分类: LINUX

2013-09-08 18:00:19

一、PCI总线概述

    总线是一种传输信号的信道;总线是连接一个或多个导体的电气连线。总线由电气接口编程接

口组成,我们重点关注编程接口。

    PCI是Peripheral Component Interconnect(外围设备互联)的简称,是在桌面及更大型的计算

机上普遍使用的外设总线。PCI总线具有三个非常重要的显著特点:

1、在计算机和外设间传输数据时具有更好的性能

2、能够尽量独立于具体的平台

3、可以方便地实现即插即用

体系结构-1
从结构上看,PCI总线是一种不依附于某个具体处理器的局部总线,它是在CPU和原来的系统总线之间

插入的一级总线,具体由一个桥接电路实现这一层的管理,并实现上下之间的接口以协调数据传送。


体系结构-2

系统的各个部分通过PCI总线和PCI-PCI桥连接在一起。CPU和RAM通过PCI桥连接到PCI总线0(即PCI总

线),而具有PCI接口的显卡直接连接到主PCI总线上。PCI-PCI桥式一个特殊的PCI设备,它负责将

PCI总线0和PCI总线1连接在一起。图中连接到PCI1号总线上的是SCSI卡和以太网卡。为了兼容就的

ISA总线标准,PCI总线还可以通过PCI-ISA桥来连接ISA总线,从而支持以前的ISA设备,图中ISA上连

接着一个多功能I/O控制器,用于控制键盘、鼠标和软驱等。

1>PCI设备寻址

    每个PCI设备由一个总线号、一个设备号、和一个功能号确定。PCI规范允许一个系统最多拥有

256条总线,没条总线最多带32个设备,但每个设备可以是最多9个功能的多功能板(如一个音频设备

带一个CD-ROM驱动器)。

/proc/iomem描述了系统中所有的设备I/O在内存地址空间上的映射。我们来看地址从1G开始的第一个

设备在/proc/iomem中是如何描述的:

    40000000-400003ff : 0000 : 00 : 1f.1

这是一个PCI设备,40000000-400003ff是它所映射的内存空间地址,占据了内存地址空间1024bytes

的位置,而0000 : 00 : 1f.1则是这个PCI外设的地址,它以冒号和逗号分隔为4个部分,第一个16位

表示域,第二个8位表示一个总线号,第三个5位表示一个设备号,最后3位,表示功能号。

因为PCI规范允许单个系统拥有最多256条总线,所以总线编号是8位。每个总线上可支持32个设备,

所以设备号是5位,而每个设备上最多可有8种功能,所以功能号是3位。由此可以得出上述的PCI设备

的地址是0号总线上的31号设备上的1号功能。使用lspci命令可以查看系统中的PCI设备


2>配置寄存器

    每个PCI设备都有一组固定格式的寄存器,即配置寄存器,配置寄存器由Linux内核中的PCI初始

化代码与驱动程序共同使用。内核在启动时负责对配置寄存器进行初始化,包括设置中断号以及I/O

基址等。


4>配置空间

00H-01H Vendor ID 制造商标识

02H-03H Device ID 是被标识

04H-05H Command 命令寄存器

06H-07H Status状态寄存器

08H Revision ID 版本识别号寄存器

09H-0bH Class Code 分类代码寄存器

0cH Cache Line Size CACHE 行长度寄存器

0dH Latency Timer主设备延迟时间寄存器

0eH Header Type 头标类型寄存器

0fH Bulit-in-teset- Register 自测试寄存器

10H-13H Base Address Register 0 基地址寄存器0

14H-17H Base Address Register 1 基地址寄存器1

18H-1bH Base Address Register 2 基地址寄存器2

1cH-1eH Base Address Register 3 基地址寄存器3

20H-23H Base Address Register 4 基地址寄存器4

24H-27H Base Address Register 5 基地址寄存器5

28H-2bH Cardbus CIS Pointer 设备总线CIS指针寄存器

2cH-2dH Subsystem Vendor ID 子设备制造商标识

2eH-2fH Subsystem Device ID 子设备标识

30H-33H Expasion ROM Base Address 扩展ROM基地址

34H-3b-保留

3cH Interrupt Line 中断线寄存器

3dH Interrupt Pin 中断引脚寄存器

3eH MIn_Gnt最小授权寄存器

3fH Max_Lat 最大延迟寄存器

厂商标识(Vendor id)

用来标识PCI设备生产厂家的数值。Inter的厂商标识为0x8086,全球厂商标识由PCI Special Interest Group 来分配。

设备标识(Device id)

用来标识设备的数值。Digital 21141快速以太网设备的标识为0x0009

基地址寄存器(Base Address Registers)记录次设备使用的I/O与内存空间的位置

中断连线(Interrupt Line)记录次设备使用的中断号

中断引脚(Interrupt Pin)记录次PCI设备使用的引脚(A,B,C,D)

二、PCI驱动程序设计

    在Linux内核中,PCI驱动使用struct pci_driver结构来描述

struct pci_driver
{
    ..........
    const struct pci_device_id* id_table;
    int(*probe)(struct pci_dev* dev, const struct pci_device_id* id);
    void(*remove)(struct pci_dev* dev);
    /*Device remove (NULL if not a hot-plug capable driver)*/
    .........
}

1>注册驱动

    注册PCI驱动,使用如下函数:
    
    pci_register_driver(struct pci_driver* drv)

2>使能设备

    在PCI驱动使用PCI设备的任何资源(I/O区或者中断)之前,驱动必须调用如下函数来使能设备:int pci_enable_device(struct pci_dev* dev)

3>获取基地址

    一个PCI设备最多可以实现6个地址区域,大多数PCI设备在这些区域实现I/O寄存器。Linux提供

了一组函数来获取这些区间的基地址:

    pci_resource_start(struct pci_dev* dev,int bar)

返回指定区域起始地址,这个区域通过参数bar指定,范围从0-5,表示6个PCI区域中的一个。

pci_resource_end(struct pci_dev* dev , int bar)返回指定区域的末地址。

三、PCI网卡驱动程序分析

1>概述
    该分析报告针对GNIC-II的千兆以太网卡,源程序文件:drivers/net/hamachi.c,由于该分析报告旨在对介绍PCI驱动程序结构,所以程序中关于硬件操作的具体部分不作介绍。


点击(此处)折叠或打开

  1. /*2、初始化*/
  2. static int __init hamachi_init(void)
  3. {
  4.     if(pci_register_drivers(&hamachi_driver)>0)
  5.         return 0;
  6.         
  7.     pci_unregister_driver(&hamachi_driver);
  8.         return -ENODEV;
  9. }
  10. /*在模块初始化时采用pci_register_driver注册pci驱动程序*/

  11. static struct pci_driver_hamachi_driver = {
  12. name: DRV_NAME,
  13. id_table: hamachi_pci_tbl,
  14. probe: hamachi_init_one,
  15. remove: __devexit_p(hamachi_remove_one),
  16. };

  17. static struct pci_device_id hamachi_pci_tbl[] __initdata = {
  18. {0x1318,0x0911,PCI_ANY_ID,PCI_ANYID,},
  19. {0x1319,0x0901,PCI_ANY_ID,PCI_ANYID,},
  20. }

  21. /*该表记录的是该驱动能耐够支持的PCI设备,分别是厂商号,设备号,子厂商号,子设备号,其中子厂商号,子设备号为PCI_ANY_ID,表示支持各种子类型。该表到底有什么实际用处需要通过分析函数pci_register_driver得出结论*/

  22. /*probe:hamachi_init_one(该函数的调用时机可通过PCI_register_driver得出)*/

  23. static int __init hamachi_init_one(struct pci_dev* pdev,const struct pci_device_id* ent)
  24. {
  25.     /*使能PCI设备*/
  26.     if(pci_enable_device(pdev))
  27.     {
  28.         ret = -EIO;
  29.         goto err_out;
  30.     }
  31.     
  32.     /*获取基地址*/
  33.     ioaddr = pci_resource_stare(pdev,0);
  34.     
  35.     
  36.     /*申请将要使用的地址空间*/
  37.     
  38.     i = pci_resource_regions(pdev,DRV_NAME);
  39.     if(i)
  40.         return i;
  41.         
  42.     /*获取中断号*/
  43.     irq = pdev->irq;
  44.     
  45.     /*ioremap 是完成物理地址映射为虚拟地址*/
  46.     ioaddr = (long)ioremap(ioaddr,0x400);
  47.     
  48.     if(!ioaddr)
  49.         goto err_out_release;
  50.         
  51.         
  52.     dev = alloc_etherdev(sizeof(struct hamachi_private));
  53.     
  54.     if(!dev)
  55.         goto err_out_iounmap;
  56.         
  57.     
  58.     SET_MODILE_OWNER(dev);
  59.     
  60.     /*硬件相关操作,省略*/
  61.     
  62.     /*The Hamachi_specific entries in the device structe.*/
  63.     dev->open = &hamachi_open;
  64.     dev->hard_start_xmit = &hamachi_start_xmit;
  65.     dev->stop = &hamachi_close;
  66.     dev->get_stat = &hamachi_get_stats;
  67.     dev->set_multicast_list = &set_rx_mode;
  68.     dev->do_ioctl = &netdev_ioctl;
  69.     dev->watchdog_timeo = TX_TLMEOUT;
  70.     
  71.     if(mtu)
  72.     dev->mtu = mtu;
  73.     
  74.     i = register_netdev(dev);
  75.     if(i){
  76.         ret = i;
  77.         goto err_out_unmap_rx;
  78.     }
  79. }

阅读(2178) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~