LDD读书笔记第十二章-PCI驱动程序

PCI接口

PCI的设计目标:

  • 更好的传输性能
  • 平台无关性
  • 简化往系统中添加删除外设的工作

更多关于PCI的内容查看PCI规范

PCI寻址

Linux支持PCI域(16位),每个PCI域可以有256个总线(8位)每个总线上32个设备(5位)每个设备8种功能(3位)。
在硬件级每个功能由16位地址来表示(没有域),在linux中因为添加了域每个功能应该用32位来表示。
PCI总线中I/O空间使用32位地址总线,而内存空间可通过32位或64位来访问。

点击查看大图

配置寄存器和初始化

所有的PCI设备都至少包含256个字节的配置地址空间,前64字节是标准化的后面的字节是设备相关的。
PCI寄存器始终是小头的,使用时用相关的转换宏转换,详情查看第十章。
标准化PCI配置寄存器图示:

标准化PCI配置寄存器结构图

标准化PCI配置寄存器结构图


点击查看大图
各字段含义:
vendorID
制造商ID,每个制造商从PCI Special Interest Group申请一个唯一的ID
deviceID
设备ID,又制造商指定
class
设备所属的组。
subsystem vendorID
subsystem deviceID
用来进一步识别硬件,用于通用芯片链接到PCI接口的情况。
struct pci_device_id 结构体表示驱动程序所支持的PCI设备。该结构体有如下字段,这些字段可以使用 PCI_ANY_ID来表示通用。
__u32 vendor;
__u32 device;
指定厂商和设备ID。
__u32 subvendor;
__u32 subdevice;
子系统厂商和子系统ID
__u32 class;
__u32 class_mask;
设备类型
kernel_ulong_t driver_data;
用来区分设备。
初始化struct pci_device_id结构体所用的宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
PCI_DEVICE(vendor, device)
PCI_DEVICE_CLASS(device_class, device_class_mask)
/*分别使用厂家ID,设备ID来和设备类型来表示所支持的硬件*/
/*例子*/
/*drivers/usb/host/ehci-hcd.c:*/
static const struct pci_device_id pci_ids[ ] = { {
	/* handle any USB 2.0 EHCI controller */
	PCI_DEVICE_CLASS(
		((PCI_CLASS_SERIAL_USB << 8) | 0x20), ~0),
	.driver_data = (unsigned long) &ehci_driver,
	},
{ /* end: all zeroes */ }
};
/*drivers/i2c/busses/i2c-i810.c:*/
static struct pci_device_id i810_ids[ ] = {
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL,
 			PCI_DEVICE_ID_INTEL_82810_IG1)
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL,
 			PCI_DEVICE_ID_INTEL_82810_IG3)
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL,
 			PCI_DEVICE_ID_INTEL_82810E_IG)
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL,
 			PCI_DEVICE_ID_INTEL_82815_CGC)
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL,
 			PCI_DEVICE_ID_INTEL_82845G_IG)
	{ 0, },
};

MODULE_DEVICE_TABLE

和模块的初始化和拆除函数一样需要到处此结构让系统知道那个模块针对那个硬件

1
	MODULE_DEVICE_TABLE(pci, i810_ids);

注册PCI驱动程序

pci主要结构体:struct pci_driver结构体。个字段含义为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const char *name;
/*驱动程序的名字*/
const struct pci_device_id *id_table;
/*pci_device_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, u32 state);
/*挂起函数的指针*/
int (*resume) (struct pci_dev *dev);
/*恢复函数的指针*/
/*例子*/
static struct pci_driver pci_driver = {
	.name = "pci_skel",
	.id_table = ids,
	.probe = probe,
	.remove = remove,
};
/*注册结构*/
static int __init pci_skel_init(void)
{
	return pci_register_driver(&pci_driver);
}
/*拆除*/
static void __exit pci_skel_exit(void)
{
	pci_unregister_driver(&pci_driver);
}

老式PCI探测

相关函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*查找特定设备:*/
struct pci_dev *pci_get_device(unsigned int vendor, unsigned 
			int device,struct pci_dev *from);
 
/*查找后打开计数器会+1所以必须调用pci_dev_put函数来减少计数器*/
/*例子*/
struct pci_dev *dev;
dev = pci_get_device(PCI_VENDOR_FOO, PCI_DEVICE_FOO, NULL);
if (dev) {
	/* Use the PCI device */
	...
	pci_dev_put(dev);
}
/*查找设备时指定子系统:*/
struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device,unsigned int ss_vendor, unsigned int ss_device,struct pci_dev *from);
/*在struct pci_bus 上的系统PCI设备列表中查询指定设备和功能编号:*/
struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn);

激活PCI设备

1
int pci_enable_device(struct pci_dev *dev);

访问配置空间

1
2
3
4
5
6
7
8
9
10
11
12
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);
int pci_bus_read_config_byte (struct pci_bus *bus, unsigned int devfn, int
where, u8 *val);
int pci_bus_read_config_word (struct pci_bus *bus, unsigned int devfn, int
where, u16 *val);
int pci_bus_read_config_dword (struct pci_bus *bus, unsigned int devfn, int
where, u32 *val);

访问I/O和内存空间

PCI有6个I/O区他们的符号名称分别为PCI_BASE_ADDRESS_0到PCI_BASE_ADDRESS_5

获取I/O区域基地址

1
2
3
4
5
6
7
8
9
10
11
unsigned long pci_resource_start(struct pci_dev *dev, int bar);
unsigned long pci_resource_end(struct pci_dev *dev, int bar);
unsigned long pci_resource_flags(struct pci_dev *dev, int bar);
/*这些函数分别返回头地址,未地址和标志。*/
/*资源标志存储在<linux/ioport.h>中。其中重要的项目*/
IORESOURCE_IO
IORESOURCE_MEM
<h4>PCI中断</h4>
PCI的中断号能直接在配置寄存器中读取
<pre lang="C">
result = pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &myirq);
----------------- 全文结束 -----------------
© 该日志作者:Senghoo, 于2010年07月30日发表于分类"Linux Kernel"下;
© 除非另有注明本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。
© 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
© 转载请注明: LDD读书笔记第十二章-PCI驱动程序 | 转自: Senghoo's Blog
© Tag: , , ,

你可以继续围观

  • LDD读书笔记第六章-高级字符驱动程序操作(一)
    ioctl ioctl是用于控制设备的公共接口,设备除了需要处理读取写入之外还需要一些相关控制信息。这些控制信息就是由ioctl调用来完成 用户空间的ioctl原型为 int ioctl(int fd,unsigned long cmd,...); 后面的“...“表示函数的参数个数是可...
  • LDD读书笔记第二章-构造和运行模块
    模块的编译 编译模块时使用GNU make的扩展语法。 关于GNU make 扩展语法请看:http://lsec.cc.ac.cn/~xmwu/myfile/gnu_make.htm obj-m  := <模块名>.o           假如要生成mymod.ko则命令...

发表评论