关键词不能为空

当前您在: 主页 > 英语 >

PCI设备驱动

作者:高考题库网
来源:https://www.bjmy2z.cn/gaokao
2021-02-13 12:00
tags:

-

2021年2月13日发(作者:ispure)


PCI


设备驱动




设备驱动





一、


PC I


简介



PCI


是一种


外设总线规范


。我们 先来看一下什么是总线:总线是一种传输信号的路径或


信道。


典 型情况是,


总线是连接于一个或多个导体的电气连线,


总线上连 接的所有设备可在


同一时间收到所有的传输内容。总线由电气接口和编程接口组成。本文 讨论


Linux


下的设


备驱动,所以 ,重点关注编程接口。




PCI



Peripheral Component Interconnect


(外围设备互联)

的简称,是普遍使用在桌面及


更大型的计算机上的外设总线。


PCI


架构被设计为


ISA


标准的替 代品,它有三个主要目标:


获得在计算机和外设之间传输数据时更好的性能;

< p>
尽可能的平台无关;


简化往系统中添加和


删除外设 的工作。




二、

PCI


寻址



< p>
从现在开始,我想尽可能通过一些实际的例子来说明问题,而减少理论方面的问题的描


述,因为,相关的理论的东西,可以在其它地方找到。




我们先来看一个例子,


我的电 脑装有


1G



RAM

< br>,


1G


以后的物理内存地址空间都是外部


设备


IO


在系统内存地址空间上的映射。


/proc/iomem


描述了系统中所有的设备


I /O


在内存地


址空间上的映射。我们来看地址从


1G


开始的第一个设备在


/proc/iomem


中是如何描述的:






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



这是一个


PCI


设备,


40000000-400003ff


是它所 映射的内存地址空间,占据了内存地址空


间的


1024 byt es


的位置,而


0000:00:1f.1

则是一个


PCI


外设的地址


,


它以冒号和逗号分隔为


4


个部分,


第一个


16


位表示域,


第二个


8


位表示一个总线编号,


第三个


5


位表示一个设备号,


最后是


3


位,表示功能号。




因为


PCI


规范允许单个系统拥有高达


256


个总线,所以总线编号是


8


位。但对于大型系


统而言,这是不够的,所以,引入了域的概念,每 个


PCI


域可以拥有最多


256


个总线,每


个总线上可支持


32


个设备,所以设备号是


5


位,而每个设备上最多可有


8


种功能,所以功


能号是


3


位。由此,我们可以得出上述的


PCI

< p>
设备的地址是


0


号域


0< /p>


号总线上的


31


号设备

< br>上的


1


号功能。那上述的这个


P CI


设备到底是什么呢?下面是我的电脑上的


lspci


命令的输


出:




00:00.0 Host bridge: Intel Corporation 82845 845 (Brookdale) Chipset Host Bridge (rev 04)



00:01.0 PCI bridge: Intel Corporation 82845 845 (Brookdale) Chipset AGP Bridge(rev 04)



00:1d.0 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #1) (rev 02)



00:1d.1 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #2) (rev 02)



00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev 42)



00:1f.0 ISA bridge: Intel Corporation 82801CAM ISA Bridge (LPC) (rev 02)



00:1f.1 IDE interface: Intel Corporation 82801CAM IDE U100 (rev 02)



00:1f.3 SMBus: Intel Corporation 82801CA/CAM SMBus Controller (rev 02)



00:1f.5 Multimedia audio controller:Intel Corporation 82801CA/CAM AC'97 Audio Controller


(rev 02)



00:1f.6 Modem: Intel Corporation 82801CA/CAM AC'97 Modem Controller (rev 02)



01:00.0 VGA compatible controller: nVidia Corporation NV17 [GeForce4 420 Go](rev a3)



02:00.0 FireWire (IEEE 1394): VIA Technologies, Inc. IEEE 1394 Host Controller(rev 46)



02:01.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+(rev 10)



02:04.0 CardBus bridge: O2 Micro, Inc. OZ6933 Cardbus Controller (rev 01)



02:04.1 CardBus bridge: O2 Micro, Inc. OZ6933 Cardbus Controller (rev 01)



lspci


没有标明域,但对于一台


P C


而言,一般只有一个域,即


0


号域。 通过这个输出我


们可以看到它是一个


IDE interfac e


。由上述的输出可以看到,我的电脑上共有


3



PCI


总线


(0

< p>
号,


1


号,


2

< p>
号)


。在单个系统上,插入多个总线是通过桥(


b ridge)


来完成的,桥是一种用


来连接总线的特殊


PCI


外设。所以,


PCI

系统的整体布局组织为树型,我们可以通过上面的


lspci


输出,来画出我的电脑上的


PCI


系统的树型结构:



00:00.0(


主桥)

--00:01.0(PCI


桥)


-----01:00: 0(nVidia


显卡)




|


< br>|---00:1d(USB


控制器


)--00:1d: 0(USB1


号控制器)




|


|



|


|--00:1d:1(USB2


号控制器)

< br>



|



|-00:1e:0( PCI



)--02:00.0(IEEE1394)



|


|



|


|-02:01.0(8139


网卡)




|


|



|


|-02:04(Card Bus



)-02:04.0(



1





|


|



|


|--02:04.1(



2)



|



|-00:1f(


多功能板卡


)-00:1f:0(ISA


桥 )




|



|--00:1f:1(IDE


接口


)



|



|--00:1f:3(SMBus)



|



|--00:1f:5(


多媒体声音控制器

)





|



|--00:1f:6(


调制解调器


)



由上图可以得出,我的电脑上共有

8



PCI


设备,其中

< p>
0


号总线上


(


主桥


)


上连有


4


个,


1


号总线上连有


1


个,


2


号总线上连有


3


个。


00:1f


是一个连有


5


个功能的多功能板卡。




每一个


PCI


设备都有它映射的内存地址空 间和它的


I/O


区域,这点是比较容易理解的。


除此之外,


PCI


设备还有它的配置寄存器。有了配置 寄存器,


PCI


的驱动程序就不需要探测


就能访问设备。


配置寄存器的布局是标准化的,


配置空间的< /p>


4


个字节含有一个独一无二的功



ID



因此,


驱动程 序可通过查询外设的特定



ID


来识别 其设备。


所以,


PCI


接口标准在


ISA


之上的主要创新在于配置地址空间。




设备驱动





前文已讲过,

PCI


驱动程序不需要探测就能访问设备,而这得益于配置地址空间。在系统引


导阶段,


PCI


硬件设备保持未激活状态,


但每个


PCI


主板均配备有能够处理< /p>


PCI


的固件,



件通过读写


PCI


控制器中的寄存器,提供了对设备配置地址 空间的访问。




配置地址空间 的前


64


字节是标准化的,它提供了厂商号,设备号,版本号等 信息,唯一


标识一个


PCI


设备。


同时,它也提供了最多可多达


6


个的


I/O


地址区域,


每个区域可以是内


存也可以是


I/O


地址。这几个


I/O


地址区域是驱动程序找到设备映射到内存和


I/ O


空间的具


体位置的唯一途径。有了这两点,

< br>PCI


驱动程序就完成了相当于探测的功能。关于这


64



字节的配置空间的详细情况,可参阅《


Linux


设备驱动程序第三版》


P306

< br>,不再详述。




下面, 我们来看一下


8139too


网卡设备的配置空间的详细情况。 在


2.6


内核的系统中,可


以在目录< /p>


/sys/bus/pci/drivers/


下看到很多以


PCI


设备名命名的目录,但不是说这些设备都存

< br>在于你的系统中。我们进入


8139too


目录,其中有 一个以它的设备地址


0000:02:01.0


命名的


目录。


在这个目录下可以找到该网卡设备相关的很多信息。


其中


resource


记录了它的

< br>6



I/O


地址区域。内容如下 :




0x3400 0x34ff 0x0101



0x00000000e0000800 0x00000000e00008ff 0x0200



0x0000 0x0000 0x0000



0x0000 0x0000 0x0000



0x0000 0x0000 0x0000



0x0000 0x0000 0x0000



0x0000 0x0000 0x0000



由该文件可以看出,

< br>8139too


设备使用了两个


I/O

< br>地址区域,第一个是它映射的


I/O


端口


范围,第二个是它映射的内存地址空间。关于这两个值可以在


/proc/io mem



/proc/ioport



得到验证。





设备驱动





为了能看到实际的运行效果,我们 选择


8139too


网卡作为示例


,< /p>


从该网卡的


linux


驱动程序中


裁剪相关代码。



< br>一个


PCI


设备的驱动程序必须要向内核中的

< p>
PCI


核心描述自己。


同时,

它也必须告诉


PCI


核心自己能够驱动哪些设备。下面,就 介绍两个相关的重要数据结构。




struct pci_device_id {




__u32 vendor, device;


/* Vendor and device ID or PCI_ANY_ID*/




__u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */




__u32 class, class_mask;


/* (class,subclass,prog-if) triplet */




kernel_ulong_t driver_data; /* Data private to the driver */



};





struct pci_driver {




struct list_head node;




char *name;




struct module *owner;




const struct pci_device_id *id_table; //


驱动所能操纵的设备


id


列表。





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


插入新设备





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


//


移除设备。





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




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




int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable);




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




struct device_driver


driver;




struct pci_dynids dynids;



};



pci_device_id


唯一标识一个

PCI


设备。它的几个成员依次分别表示:厂商号,设备号,


子厂商号,子设备号,类别,类别掩码(类可分为基类,子类)


,私有数据。每一个< /p>


PCI



备的驱动程序都有一个


pci_device_id


的数组,用于告诉


PCI


核心自己能够驱动哪些设备。


8139too

< p>
的驱动程序定义它的


pci_device_id


数组如下:





static struct pci_device_id rtl8139_pci_tbl[];



该数组被初 始化为


8139


系列的一组网卡,


当< /p>


PCI


核心得到这个数组后,


会拿数组中 的每


一项跟从


PCI


配置空间中读取到 的数据进行比对,从而为该驱动程序找到正确的设备。而


pci_driver


代表一个


pci


驱动程序。成员


id_talbe


即是指向


pci_device_i d


数组的指针。


name


是驱动程序的 名字,


probe


完成探测工作,


即拿


pci_device_id


数组与内核中的数据进行比对。< /p>


remove


完成驱动程序的移除工作。关键的成员就这几个。< /p>




驱动程序通过


pci_module_init


向内核注册自己


(我 们有时会看到


pci_register_driver


函数,


其实它们是同一个,在内核代码中会看到,只是一个简单的


#d efine






pci_module_init(&pci_driver);



调用函数后,如果


pci_d evice_id


数组中标识的设备存在于系统中,并且该设备恰好还没


有驱动程序,则该驱动程序会被安装。下面我们来看从


8139too


驱动代码中裁剪的


pci


设备

< br>初始化代码:



pci_driver.h:



/* pci_driver.h


* helinqiang@


* 2006-3-5


*/


#ifndef PCI_DRIVER_H


#define PCI_DRIVER_H



#include


//for struct pci_device_id


#include


//for MODULE_DEVICE_TABLE


#include


//for struct pci_driver



#define DRV_NAME



#define DRV_VERSION


#define RTL8139_DRIVER_NAME


DRV_NAME



typedef enum{



RTL8139 = 0,



RTL8129,


}board_t;



static struct pci_device_id rtl8139_pci_tbl[] = {



{0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



{0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },



#ifdef CONFIG_SH_SECUREEDGE5410



/* Bogus 8139 silicon reports 8129 without external PROM :-( */



{0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },


#endif


#ifdef CONFIG_8139TOO_8129



{0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 },


#endif


/* some crazy cards report invalid vendor ids like



* 0x0001 here.


The other ids are valid and constant,



* so we simply don't match on the main vendor id.



*/



{PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 },



{PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 },



{PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 },



{0,}


};


MODULE_DEVICE_TABLE(pci, rtl8139_pci_tbl);


static int __devinit rtl8139_init_one(struct pci_dev *pdev, const struct pci_device_id *id);


static void __devexit rtl8139_remove_one(struct pci_dev *pdev);



static struct pci_driver rtl8139_pci_driver = {



.name


= DRV_NAME,



.id_table


= rtl8139_pci_tbl,



.probe


= rtl8139_init_one,



.remove


= __devexit_p(rtl8139_remove_one),


};



#endif //PCI_DRIVER_H



pci_driver.c:


/* pci_driver.c


* helinqiang@


* 2006-3-5


*/



#include



#include



MODULE_AUTHOR(


MODULE_LICENSE(



static int __init rtl8139_init_module(void)


{



/* when we're a module, we always print a version message,



* even if no 8139 board is found.



*/


#ifdef MODULE



printk (KERN_INFO RTL8139_DRIVER_NAME


#endif




return pci_module_init(&rtl8139_pci_driver);


}




static void __exit rtl8139_cleanup_module (void)


{



pci_unregister_driver(&r tl8139_pci_driver);


}



module_init(rtl8139_init_module);


module_exit(rtl8139_cleanup_module);



int __devinit rtl8139_init_one(struct pci_dev *pdev, const struct pci_device_id *id)


{



//


这里可插入各种调试代码,下文会有专门描述。

< p>



return 0;


}



void __devexit rtl8139_remove_one (struct pci_dev *pdev)

-


-


-


-


-


-


-


-



本文更新与2021-02-13 12:00,由作者提供,不代表本网站立场,转载请注明出处:https://www.bjmy2z.cn/gaokao/650029.html

PCI设备驱动的相关文章