-
PCI
设备驱动
设备驱动
一
一、
PC
I
简介
PCI
是一种
外设总线规范
。我们
先来看一下什么是总线:总线是一种传输信号的路径或
信道。
典
型情况是,
总线是连接于一个或多个导体的电气连线,
总线上连
接的所有设备可在
同一时间收到所有的传输内容。总线由电气接口和编程接口组成。本文
讨论
Linux
下的设
备驱动,所以
,重点关注编程接口。
PCI
是
Peripheral
Component Interconnect
(外围设备互联)
的简称,是普遍使用在桌面及
更大型的计算机上的外设总线。
PCI
架构被设计为
ISA
标准的替
代品,它有三个主要目标:
获得在计算机和外设之间传输数据时更好的性能;
尽可能的平台无关;
简化往系统中添加和
删除外设
的工作。
二、
PCI
寻址
从现在开始,我想尽可能通过一些实际的例子来说明问题,而减少理论方面的问题的描
述,因为,相关的理论的东西,可以在其它地方找到。
我们先来看一个例子,
我的电
脑装有
1G
的
RAM
< br>,
1G
以后的物理内存地址空间都是外部
设备
IO
在系统内存地址空间上的映射。
/proc/iomem
描述了系统中所有的设备
I
/O
在内存地
址空间上的映射。我们来看地址从
1G
开始的第一个设备在
/proc/iomem
p>
中是如何描述的:
40000000-400003ff : 0000:00:1f.1
这是一个
PCI
设备,
40000000-400003ff
是它所
映射的内存地址空间,占据了内存地址空
间的
1024 byt
es
的位置,而
0000:00:1f.1
则是一个
PCI
外设的地址
,
p>
它以冒号和逗号分隔为
4
个部分,
第一个
16
位表示域,
第二个
8
位表示一个总线编号,
第三个
5
位表示一个设备号,
最后是
3
位,表示功能号。
因为
PCI
规范允许单个系统拥有高达
256
个总线,所以总线编号是
8
p>
位。但对于大型系
统而言,这是不够的,所以,引入了域的概念,每
个
PCI
域可以拥有最多
256
个总线,每
个总线上可支持
32
个设备,所以设备号是
5
位,而每个设备上最多可有
8
种功能,所以功
能号是
3
位。由此,我们可以得出上述的
PCI
设备的地址是
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
号,
1
号,
2
号)
。在单个系统上,插入多个总线是通过桥(
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(
桥
p>
1
)
|
|
|
|--02:04.1(
桥
2)
|
|-00:1f(
p>
多功能板卡
)-00:1f:0(ISA
桥
)
|
|--00:1f:1(IDE
接口
)
|
|--00:1f:3(SMBus)
|
|--00:1f:5(
多媒体声音控制器
)
|
|--00:1f:6(
调制解调器
)
由上图可以得出,我的电脑上共有
8
个
PCI
设备,其中
0
号总线上
(
主桥
)
上连有
4
个,
p>
1
号总线上连有
1
个,
2
号总线上连有
3
个。
00:1f
是一个连有
5
个功能的多功能板卡。
p>
每一个
PCI
设备都有它映射的内存地址空
间和它的
I/O
区域,这点是比较容易理解的。
除此之外,
PCI
设备还有它的配置寄存器。有了配置
寄存器,
PCI
的驱动程序就不需要探测
就能访问设备。
配置寄存器的布局是标准化的,
配置空间的<
/p>
4
个字节含有一个独一无二的功
能
ID
,
因此,
驱动程
序可通过查询外设的特定
ID
来识别
其设备。
所以,
PCI
接口标准在
p>
ISA
之上的主要创新在于配置地址空间。
设备驱动
二
前文已讲过,
PCI
驱动程序不需要探测就能访问设备,而这得益于配置地址空间。在系统引
p>
导阶段,
PCI
硬件设备保持未激活状态,
但每个
PCI
主板均配备有能够处理<
/p>
PCI
的固件,
固
件通过读写
PCI
控制器中的寄存器,提供了对设备配置地址
空间的访问。
配置地址空间
的前
64
字节是标准化的,它提供了厂商号,设备号,版本号等
信息,唯一
标识一个
PCI
设备。
p>
同时,它也提供了最多可多达
6
个的
I/O
地址区域,
每个区域可以是内
存也可以是
I/O
地址。这几个
I/O
地址区域是驱动程序找到设备映射到内存和
I/
O
空间的具
体位置的唯一途径。有了这两点,
< br>PCI
驱动程序就完成了相当于探测的功能。关于这
64
个
字节的配置空间的详细情况,可参阅《
Linux
设备驱动程序第三版》
P306
< br>,不再详述。
下面,
我们来看一下
8139too
网卡设备的配置空间的详细情况。
在
2.6
内核的系统中,可
以在目录<
/p>
/sys/bus/pci/drivers/
下看到很多以
p>
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
设备的驱动程序必须要向内核中的
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
的驱动程序定义它的
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(
p>
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)
{
//
这里可插入各种调试代码,下文会有专门描述。
return 0;
}
void __devexit
rtl8139_remove_one (struct pci_dev *pdev)
-
-
-
-
-
-
-
-
-
上一篇:电脑主板各种插槽详解
下一篇:电脑主板结构图解