PCI设备linux驱动(一)

合集下载

Linux下PCI驱动程序的开发

Linux下PCI驱动程序的开发
算 机 系统 中应 用最 为广 泛 , 且 最 为通 用 的总 线标 准 。 并 Ln x的 内核 能较 好地 支 持 P I iu C 总线 , 论 述基 于 P X 本 L
用 3 2位 数 据 总 线 , 但
PI C 规范 中已 经 给 出 了 6 4位 的扩 展 实现 , 而 从
使 PI C 总线能 够更好 地
气 特性 和行 为 规约 ,并 且 详 细定 义 了计 算 机 系统 中的 各 个不 同部件 之 间应该 如何 正确 地进 行交 互 。
无论 是 在基 于 It 芯 片 的 P ne l C机 中 ,或 是 在 基 于
Apa芯 片 的工 作 站 上 ,C 毫 无 疑 问 都是 目前 使 用最 lh PI 广 泛 的 一种 总 线 接 口标 准 。 同 旧式 的 IA 总 线 不 同 , S
信. 电技 术
2 1 年( 3 卷) 1 00 第 9 第 期
来 完 成 同总线 子系统 的交 互 , 图 1 示 。 见 所
以像普通文件一样来看待 :它们可以使用和操作文件
相 同 的 、 准 的 系统 调 用 接 口来 完 成 打 开 、 闭 、 写 标 关 读 和I / O控制 操作 , 驱动程 序 的 主要任 务也 就是 要 实现 而
1 9
由于 使 用 了更 高 的时 钟频 率 ,因此 P I C 总线 能够
关键 词 : iu ; C 驱 动 ; 动架 构 Ln x P I 驱
Ln x 作 系 统 因 为其 高效 、 全 、 动 态 加 载 及 iu 操 安 可
源代码 开放 等 特点 ,深受 设 备驱 动 程序 开 发人 员 的喜
获得 比 IA总线更 好 的 S 整 体性 能 。P I C 总线 的 时 钟 频 率 一 般 在

一、如何编写LinuxPCI驱动程序

一、如何编写LinuxPCI驱动程序

⼀、如何编写LinuxPCI驱动程序PCI的世界是⼴阔的,充满了(⼤部分令⼈不快的)惊喜。

由于每个CPU体系结构实现不同的芯⽚集,并且PCI设备有不同的需求(“特性”),因此Linux内核中的PCI⽀持并不像⼈们希望的那么简单。

这篇简短的⽂章介绍⽤于PCI设备驱动程序的Linux APIs。

1.1 PCI驱动程序结构PCI驱动程序通过pci_register_driver()在系统中"发现"PCI设备。

事实上,恰恰相反。

当PCI通⽤代码发现⼀个新设备时,具有匹配“描述”的驱动程序将被通知。

详情如下。

pci_register_driver()将设备的⼤部分探测留给PCI层,并⽀持在线插⼊/删除设备[因此在单个驱动程序中⽀持热插拔PCI、CardBus和Express-Card]。

pci_register_driver()调⽤需要传⼊⼀个函数指针表,从⽽指⽰驱动程序的更⾼⼀级结构体。

⼀旦驱动程序知道了⼀个PCI设备并获得了所有权,驱动程序通常需要执⾏以下初始化:启⽤设备请求MMIO / IOP资源设置DMA掩码⼤⼩(⽤于⼀致性DMA和流式DMA)分配和初始化共享控制数据(pci_allocate_coherent())访问设备配置空间(如果需要)注册IRQ处理程序(request_irq())初始化non-PCI(即LAN/SCSI/等芯⽚部分)启⽤DMA /处理引擎当使⽤设备完成时,可能需要卸载模块,驱动程序需要采取以下步骤:禁⽌设备产⽣irq释放IRQ (free_irq())停⽌所有DMA活动释放DMA缓冲区(包括流式DMA和⼀致性DMA)从其他⼦系统注销(例如scsi或netdev)释放MMIO / IOP资源禁⽤该设备下⾯⼏节将介绍这些主题中的⼤部分。

其余部分请查看LDD3或<linux/pci.h>。

如果PCI⼦系统没有配置(没有设置CONFIG_PCI),下⾯描述的⼤多数PCI函数都被定义为内联函数,要么完全空,要么只是返回⼀个适当的错误代码,以避免在驱动程序中出现⼤量ifdefs。

Linux系统下PCI设备驱动程序的研究

Linux系统下PCI设备驱动程序的研究

配置头。 目前 P I2 C2 规范定义 了 3 . 种配置头格式 , 分别是 0 1 2 , 和 型配 置头 。其 中类型 0 为标准的 P I C 应用设备 , 指除类型 l 和类 型 2 的所 外
有PI C 设备 。类型 ‘ 为 P IP I I C— C 桥设 备 , 于对 P I 用 C 总线扩展 , 将两条
置寄存器组 成 , P I 在 C 配置空间中对 P I 备信息进行描 述 , C设 包括设备
图 1 PI C 总线的体 系结构
P I 外围设备互联 (epeaC m oetn r net的简称 。 C是 P rhr o pnn Ie onc i l tc ) 作 为一种通用 的总线接 口标准 。 它在 目前的计算机系统中得到了2 P I a u 桥 , 为 C— r s 主要用于笔记本 的插 卡式 CB 总线 。这里只对类 型 0的配置头加以说 明, 配置空间见图 2 。 组成 P I C 配置空 问的寄存器 当中包括 厂商代码 、 设备号 、 版本号 、 类 别 号 、 系统 厂商号和子 系统号 , 些寄存 器标 明 P I 子 这 C 设备类 , 启动 时。 系统将根据这些寄存器辨 别出 P I C 设备 , 并为它加载 相应 的驱动程
20 年 第 1 卷 第 2 期 06 6 3
收稿 日 : 0 — 7 1 期 2 6 0—5 0
Lnx系统 下 P I i u C 设备驱动程序 的研究
贺金平 , 刘永祥 , 杨 吕
( 昆明理工大学信息工程与 自动化学院 , 云南昆明 ,5 0 1 6 05 )
摘 要 :C 总线提 供 了许 多优于其他 总线标准( ES PI 如 IA) 新特性 , 目前 已经成 为计
由于一个计算机系统 中可 能有多个主设备 , 墨 l 由l 可知 . 】 这些设备 都直接或间接地连接在 P I C 总线上 , 它们有可能在同一时间都需要 占用 P1 C 总线。这将发生 P I C 总线的竞 争问题 。为 了解决竞争使 用总线 的问 题, P1 在 C 总线上配备 了总线 仲裁器。 在一个 P 1 C 应用 系统 中。 了总 取得 线控制权的设备称为“ 主设备”而被主设备选中以进 行通信 的设备称为 , “ 从设备” “ 标 设备 ” 或 目 。外部设备如果要使用 P I C 总线 , 首先发 出总线

Linux下PCI设备驱动开发详解

Linux下PCI设备驱动开发详解

一、设备驱动程序概述自Linux在中国发展以来,得到了许多公司的青睐。

在国内的玩家也越来越多了,但目前还是停留在玩的水平上,很少有玩家对Linux的系统进行研究。

因为它的开放,我们可以随时拿来“把玩”。

这也是Linux一个无可比拟的优势,这样我们可以修改后再加入到里面。

但很少有专门的书籍讲到Linux驱动程序的开发,像上海这样的大城市也很少有讲Linux驱动开发的资料,唉,谁让这个是人家的东西呢,我们还是得跟着人家跑。

我现在讲的这些驱动开发的细节,并不特定哪个版本的内核,这只是大体的思路与步骤。

因为大家都知道Linux 2.6.x 与Linux 2.4.x是有不少改动的。

所以,具体的大家可以去参考Linux Device Driver 2.4 和Linux Device Driver 2.6这几本书。

这是我们学习开发驱动必不可少的东西。

好了,下面就开始学习吧。

根据设备的行为,我们可以把设备分为字符设备和块设备,还有网络设备。

字符设备是以字节为单位进行顺序读写,数据缓冲系统对它们的访问不提供缓存。

而块设备则是允许随机访问与读写,每次读写的数据量都是数据块长度的整数倍,并且访问还会经过缓冲区缓存系统才能实现。

与Unix版本不同的是:Linux的内核允许不是数据块长度整数倍的数据量被读取,用官方的语言就是:但这种不同只是纯粹学术方面的东西。

大多数设备驱动程序都要通过文件系统来进行访问的,但网络设备是不同的。

/dev子目录里都是关于设备的特殊文件,但看起来它们与普通的目录没有什么两样。

如下:$ ls -l /dev...brw-rw--- 1 root disk 22, 1 May 5 1998 hdc1crw-rw--- 1 root daemon 6 0 May 5 1998 lp0与普通文件有所不同是开头的“C” 和“B”,即char 和block的意思,即字符设备和块设备。

再后面的“22,1” 和“6,0”即设备的主设备号和次设备号,主设备号表明它是哪一种设备,这与你在Windows里添加硬件时看到的那些是一个意思。

linux pci 设备命名规则

linux pci 设备命名规则

linux pci 设备命名规则Linux PCI设备命名规则PCI (Peripheral Component Interconnect)是一种计算机总线标准,它允许多个设备通过一个通用的总线连接到计算机系统。

在Linux系统中,PCI设备在内核中被识别和管理。

本文将介绍Linux中PCI设备的命名规则,并逐步回答有关此主题的问题。

第一部分:Linux PCI设备命名规则在Linux系统中,每个PCI设备都被分配了一个唯一的标识符,该标识符被称为“BDF”(Bus, Device, Function)。

BDF标识符由以下三个字段组成:1. 总线(Bus):表示设备所连接的总线号码,范围为0-255。

在Linux 中,总线号码通常以`/sys/bus/pci/devices/`目录下的子目录名的形式存在,例如,`/sys/bus/pci/devices/0000:0e:00.0`。

2. 设备(Device):表示同一总线上的设备号码,范围为0-31。

设备号码以`.0`结尾。

在Linux中,每个设备都有一个唯一的目录,其路径为`/sys/bus/pci/devices/0000:0e:00.0`。

3. 功能(Function):表示设备上的功能号码,范围为0-7。

功能号码通常以`.0`结尾,并且在同一设备的功能之间以句点分隔,例如,`0000:0e:00.0`和`0000:0e:00.1`。

根据这些规则,Linux系统中任何一个PCI设备都可以通过BDF标识符进行唯一地识别和访问。

第二部分:回答问题1. 如何找到PCI设备的BDF标识符?在Linux系统中,可以使用以下命令来找到PCI设备的BDF标识符:lspci -nn该命令将列出系统中所有PCI设备的信息,包括设备的BDF标识符。

2. 如何确定PCI设备的总线号码?可以使用以下命令来确定PCI设备的总线号码:lspci -s <PCI地址> -t其中,`<PCI地址>`是设备的PCI地址。

Linux下PCI设备流式DMA驱动开发

Linux下PCI设备流式DMA驱动开发

1 前

Lu i x内核与外部设备之 间的接 口。在 L u n i x操作 n 系统下有三类主要的设备文件类型 , 字符设备 , 块设 备和网络设备 。在 L u 系统 中, ix n 对于设 备文件 和 普通文 件 的访 问都 是通 过其 IO子 系统 向 内核 中的 / 其他部分提供统一 的标准设 备接 口——包含在 i n . e d/iu/sh中 的 数 据 结 构 fe oe tn 来 完 l el xf. u n i — pr i s l ao 成的:
发复杂。通过对 v 2 1 C 实验板 L u 驱动程序 w 00 I P ix n 的分析 , 介绍 了 Lnx下 P I 动 的框 架 结 构 , iu C驱 并且 重 点分析 了编 程接 口和 一种 先 进 的 流媒 体 D A实 M
现机制 。
} ቤተ መጻሕፍቲ ባይዱ

这个结构的每一个成员 的名字都对应着一个系 统调用 , 系统调用通过设备文件的主设备号找到相 应的设备驱动程序 , 然后读取 这个数据结构相应的 函数指针 , 接着 把控制权 交给该 函数。这是 L u ix n
e p cal he i lme tto fo i d o h  ̄e m s e i l t mp e n ai n o ne k n ft e s a DMA. y Ke r s: nu Vw2 0; iv r PCI DMA y wo d Li x; 01 Dr e ; ;
摘 要 : 在简要介绍了 L u 和具有 M E 1 一 , 4 ix n P G一 , 2 一 硬件编解码功能的 v 2 1 w 00芯片后, 分 析了 v2 1 w 00芯片的 P I i x C n 驱动程序, Lu 然后从驱动程序开发 的角度讨论 了在24 X内核版本下 .. PI C 驱动程序的结构框架和 内核驱动程序编程接 口, 较为详细的分析 了一种流媒体 D A的实现方 M

Linux操作系统下的PCI驱动开发


PI C 驱动 通常可 以看 做字符 设备 的驱动 来设计 。
Ln x iu 的操 作 过程 分 为 两个 步骤 :首 先用 P 1 C 驱 动将 内核 与设备 挂接 起来 :紧接着 通过 应用 程
合 P I 地 总 线 规 范 22 ,并 配 有 可 选 的 串行 C本 .版
E PO E R M接 口 。芯 片 的本 地 总 线 时钟 可 与P 1 C 时 钟 异 步 ,其 内部有 6 可编 程 FF 种 IO,可 以实 现零 等 待 突发传输 及 本地 总线 与P 1 C 总线 的异 步操 作 , 同时支持 主模 式 、从 模式 、D 传 输模 式 ,可广 MA
消费 电子等领 域 的主流操 作 系统 。 本 文 主要 对Ln x iu 环境 下 开 发P I0 4 片 驱 C9 5 芯
2 Ln x 的P I 动 程 序 iu 下 C驱
Ln x iu 将所 有 外 部设 备 看成 是 一类 特 殊文 件 ,
动 的具体方 法 进行描 述 ,并 给 出了如何 将 驱动 程
泛用 于嵌人式 系统 中。
P I0 4 C 9 5 的地 址 资 源是 由其 基 址 寄 存 器来 设
置 的 ,P I0 4 C 9 5 的配 置 空 间 有 六 个 基 址 寄存 器 : B R~A 5 A 0 B R .其 中B R 映射 到 配置 空 间 的基 地 A 0 址 .B R1 映 射 到 Y A 为 O空 间 的基 地 址 ,B R ~ A 2
收 稿 日期 :0 9 1 — 6 2 0 — 2 1
序来 根据 设备 文件 所属 类型 并通 过驱 动 提供 的接
口 函数 来 操 作 设备 。这 使得 P I 动 至少 应 该 包 C驱 含 两部 分 内容 :一 是 P 1 备驱 动 ,二 是具 体 设 C设 备本 身 的驱 动 。

PCie 驱动

PCie驱动Pcie设备上有三种地址空间:PCI的I/O空间、PCI的存储空间和PCI的配置空间。

Pce的配置空间:PCI有三个相互独立的物理地址空间:设备存储器地址空间、I/O地址空间和配置空间。

配置空间是PCI 所特有的一个物理空间。

由于PCI支持设备即插即用,所以PCI设备不占用固定的内存地址空间或I/O地址空间,而是由操作系统决定其映射的基址。

系统加电时,BIOS检测PCI总线,确定所有连接在PCI总线上的设备以及它们的配置要求,并进行系统配置。

所以,所有的PCI设备必须实现配置空间,从而能够实现参数的自动配置,实现真正的即插即用。

PCI总线规范定义的配置空间总长度为256个字节,配置信息按一定的顺序和大小依次存放。

前64个字节的配置空间称为配置头,对于所有的设备都一样,配置头的主要功能是用来识别设备、定义主机访问PCI卡的方式(I/O访问或者存储器访问,还有中断信息)。

其余的192个字节称为本地配置空间,主要定义卡上局部总线的特性、本地空间基地址及范围等。

一般来说,基于pcie总线的驱动,需要涉及到pci_driverpci_devpci_device_id.pci_device_id:用于标识pcie设备,通过上图的厂商Id设备Id功能号等唯一确定一个pcie设备,内核通过这个结构体确认驱动与设备是否匹配。

pci_dev:一般pcie设备都具有热拔插功能,当内核检测到有pcie设备插入时,会与相应的Pci_driver:当有相应的设备匹配会调用驱动的相关方法,驱动中通常要做的是读出Base AdrressRegister1-6的值,这是pcies设备6个内存空间的基地址,然后通过ioremap方法映射成虚拟地址,至于6个内存空间的具体含义需要依赖于设备。

在用模块方式实现PCI设备驱动程序时,通常至少要实现以下几个部分:初始化设备模块、设备打开模块、数据读写和控制模块、中断处理模块、设备释放模块、设备卸载模块。

Linux网络设备驱动_PCI网卡

⏹ Linux 网络设备驱动结构Linux的加载和卸载设备的注册初始化和注销设备的打开和释放据包的发送和接收络连接状况数设置和统计数据此驱动所支持的网卡系列初始化网络设备注销网络设备设备挂起函数设备恢复函数打开网络设备关闭网络设备读取包的网卡收发包的状态,统计数据用户的ioctl 命令系统调用硬件处理数据包发送ISR 数据包发送和接收⏹ struct pci_driver如果网络设备(包括wireless )是PCI 规范的,则先是向内核注册该PCI 设备(pci_register_driver),然后由pci_driver 数据结构中的probe 函数指针所指向的侦测函数来初始化该PCI 设备,并且同时注册和初始化该网络设备。

如果网络设备(包括wireless )是PCMCIA 规范的,则先是向内核注册该PCMCIA 设备(register_pccard_driver),然后driver_info_t 数据结构中的attach 函数指针所指向的侦测函数来初始化该PCMCIA 设备,并且同时注册和初始化该网络设备。

1. 申明为PCI 设备:static struct pci_driver tg3_driver = {.name = DRV_MODULE_NAME,//此驱动所支持的网卡系列,vendor_id, device_id.id_table = tg3_pci_tbl,//初始化网络设备的回调函数.probe = tg3_init_one,//注销网络设备的回调函数.remove = __devexit_p(tg3_remove_one),//设备挂起函数.suspend = tg3_suspend,//设备恢复函数.resume = tg3_resume};2. 驱动模块的加载和卸载static int __init tg3_init(void){//先注册成PCI设备,并初始化,如果是其他的ESIA,PCMCIA,用其他函数return pci_module_init(&tg3_driver);}static void __exit tg3_cleanup(void){pci_unregister_driver(&tg3_driver);//注销PCI设备}module_init(tg3_init); //驱动模块的加载module_exit(tg3_cleanup); //驱动模块的卸载3. PCI设备探测函数probe,初始化网络设备主要工作:申请并设置pci资源(内存),申请并设置net_device网络设备结构,IO映射,注册网络设备static int __devinit tg3_init_one(struct pci_dev *pdev, const struct pci_device_id *ent){//初始化设备,使I/O,memory可用,唤醒设备pci_enable_device(pdev);//申请内存空间,配置网卡的I/O,memory资源pci_request_regions(pdev, DRV_MODULE_NAME);pci_set_master(pdev);//设置DMA属性pci_set_dma_mask(pdev, (u64) 0xffffffffffffffff);//网卡 I/O,memory资源的启始地址tg3reg_base = pci_resource_start(pdev, 0);//网卡I/O,memory资源的大小tg3reg_len = pci_resource_len(pdev, 0);//分配并设置网络设备dev = alloc_etherdev(sizeof(*tp));//申明为内核设备模块SET_MODULE_OWNER(dev);//初始化私有结构中的各成员值tp = dev->priv;tp->pdev = pdev;tp->dev = dev;……//锁的初始化spin_lock_init(&tp->lock);//映射I/O,memory地址到私有域中的寄存器结构tp->regs = (unsigned long) ioremap(tg3reg_base, tg3reg_len);dev->irq = pdev->irq;//网络设备回调函数赋值dev->open = tg3_open;dev->stop = tg3_close;dev->get_stats = tg3_get_stats;dev->set_multicast_list = tg3_set_rx_mode;dev->set_mac_aDDRess = tg3_set_mac_addr;dev->do_ioctl = tg3_ioctl;dev->tx_timeout = tg3_tx_timeout;dev->hard_start_xmit= tg3_start_xmit;//网卡的MAC地址赋值dev->addrtg3_get_device_address(tp);//注册网络设备register_netdev(dev);//把网络设备指针地址放入PCI设备中的设备指针中pci_set_drvdata(pdev, dev);}4. 注销网络设备主要工作:注销并释放网络设备,取消地址映射,释放PCI资源static void __devexit tg3_remove_one(struct pci_dev *pdev){struct net_device *dev = pci_get_drvdata(pdev);//注销网络设备unregister_netdev(dev);//取消地址映射iounmap((void *) ((struct tg3 *)(dev->priv))->regs);//释放网络设备kfree(dev);//释放PCI资源pci_release_regions(pdev);//停用PCI设备pci_disable_device(pdev);//PCI设备中的设备指针赋空pci_set_drvdata(pdev, NULL);}5. 网络设备挂起主要工作:停用网卡的中断寄存器,停止网卡收发包,停用网卡某些硬件,设置电源状态static int tg3_suspend(struct pci_dev *pdev, u32 state){//停用网卡的中断寄存器tg3_disable_ints(tp);//停止网卡收发包netif_device_detach(dev);//停止网卡某些硬件,fireware的一些功能tg3_halt(tp);//设置网卡的电源状态tg3_set_power_state(tp, state);}6. 网络设备恢复主要工作:恢复网卡电源,允许收发包,初始化收发包的缓冲区,初始化网卡硬件,打开网卡中断寄存器static int tg3_resume(struct pci_dev *pdev){//恢复网卡电源tg3_set_power_state(tp, 0);//允许网卡收发包netif_device_attach(dev);//初始化收发包的缓冲区tg3_init_rings(tp);//初始化网卡硬件tg3_init_hw(tp);//打开网卡中断寄存器tg3_enable_ints(tp);}struct net_device1. 打开网络设备主要工作:分配中断及注册中断处理函数,初始化硬件及收发缓冲区,初始化定时器及注册超时函数,允许网卡开始传送包static int tg3_open(struct net_device *dev){//分配一个中断request_irq(dev->irq, tg3_interrupt, SA_SHIRQ, dev->name, dev);/* int request_irq(unsigned int irq,void (*handler)(int irq, void *dev_id, struct pt_regs *regs),unsigned long irqflags,const char * devname,void *dev_id);irq是要申请的硬件中断号。

linux中pcie设备初始化流程

linux中pcie设备初始化流程PCIe(Peripheral Component Interconnect Express)是一种高速串行总线接口,用于连接计算机主板和外部设备。

在Linux系统中,PCIe设备的初始化是一个重要的过程,它确保设备能够正常工作并与系统进行通信。

本文将介绍Linux中PCIe设备初始化的流程。

1. 设备检测与识别在Linux系统启动时,内核会进行设备检测与识别的过程。

对于PCIe设备,内核会扫描PCIe总线,识别连接在总线上的设备。

这个过程是通过读取PCIe设备的配置空间来完成的。

配置空间是一块特殊的内存区域,包含了设备的各种信息,如设备ID、厂商ID、设备类别等。

2. 分配资源一旦设备被识别,内核会为其分配必要的资源,如内存空间、中断线等。

这些资源的分配是通过解析设备的配置空间来完成的。

内核会根据设备的需求和系统的可用资源进行分配,以确保设备能够正常工作。

3. 驱动加载设备的驱动程序是用来控制和管理设备的软件模块。

在Linux系统中,驱动程序是以内核模块的形式存在的。

一旦设备被识别并分配了资源,内核会加载与之对应的驱动程序。

驱动程序会与设备进行通信,配置设备的寄存器、中断等,并提供相应的接口供用户空间程序使用。

4. 设备初始化设备初始化是指对设备进行一系列的配置和初始化操作,以使其能够正常工作。

设备初始化的具体过程是由设备的驱动程序来完成的。

驱动程序会根据设备的特性和需求,对设备进行相应的配置和初始化。

这包括设置设备的工作模式、参数、中断处理等。

5. 设备注册设备注册是将设备与系统进行关联的过程。

在Linux系统中,设备注册是通过设备模型来完成的。

设备模型是一种用于描述和管理设备的框架,它提供了一套标准的接口和方法,用于设备的注册、管理和访问。

设备注册的过程包括将设备添加到设备模型中,并分配设备号等。

6. 设备启动设备启动是指设备开始正常工作的过程。

在Linux系统中,设备启动是由设备的驱动程序来完成的。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

PCI设备linux驱动(一)收藏一、PCI简介PCI是一种外设总线规范。

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

典型情况是,总线是连接于一个或多个导体的电气连线,总线上连接的所有设备可在同一时间收到所有的传输内容。

总线由电气接口和编程接口组成。

本文讨论Linux 下的设备驱动,所以,重点关注编程接口。

PCI是Peripheral Component Interconnect(外围设备互联)的简称,是普遍使用在桌面及更大型的计算机上的外设总线。

PCI架构被设计为ISA标准的替代品,它有三个主要目标:获得在计算机和外设之间传输数据时更好的性能;尽可能的平台无关;简化往系统中添加和删除外设的工作。

二、PCI寻址我们先来看一个例子,我的电脑装有1G的RAM,1G以后的物理内存地址空间都是外部设备IO在系统内存地址空间上的映射。

/proc/iomem描述了系统中所有的设备I/O在内存地址空间上的映射。

我们来看地址从1G开始的第一个设备在/proc/iomem中是如何描述的:40000000-400003ff : 0000:00:1f.1这是一个PCI设备,40000000-400003ff是它所映射的内存地址空间,占据了内存地址空间的1024 bytes的位置,而0000:00:1f.1则是一个PCI外设的地址,它以冒号和逗号分隔为4个部分,第一个16位表示域,第二个8位表示一个总线编号,第三个5位表示一个设备号,最后是3位,表示功能号。

因为PCI规范允许单个系统拥有高达256个总线,所以总线编号是8位。

但对于大型系统而言,这是不够的,所以,引入了域的概念,每个PCI域可以拥有最多256个总线,每个总线上可支持32个设备,所以设备号是5位,而每个设备上最多可有8种功能,所以功能号是3位。

由此,我们可以得出上述的PCI设备的地址是0号域0号总线上的31号设备上的1号功能。

那上述的这个PCI设备到底是什么呢?下面是我的电脑上的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没有标明域,但对于一台PC而言,一般只有一个域,即0号域。

通过这个输出我们可以看到它是一个IDE interface。

由上述的输出可以看到,我的电脑上共有3个PCI总线(0号,1号,2号)。

在单个系统上,插入多个总线是通过桥(bridge)来完成的,桥是一种用来连接总线的特殊PCI外设。

所以,PCI系统的整体布局组织为树型,我们可以通过上面的lspci输出,来画出我的电脑上的PCI系统的树型结构:00:00.0(主桥)--00:01.0(PCI桥)-----01:00:0(nVidia显卡)||---00:1d(USB控制器)--00:1d:0(USB1号控制器)| || |--00:1d:1(USB2号控制器)|-00:1e:0(PCI桥)--02:00.0(IEEE1394)| || |-02:01.0(8139网卡)| || |-02:04(CardBus桥)-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设备,其中0号总线上(主桥)上连有4个,1号总线上连有1个,2号总线上连有3个。

00:1f是一个连有5个功能的多功能板卡。

每一个PCI设备都有它映射的内存地址空间和它的I/O区域,这点是比较容易理解的。

除此之外,PCI设备还有它的配置寄存器。

有了配置寄存器,PCI的驱动程序就不需要探测就能访问设备。

配置寄存器的布局是标准化的,配置空间的4个字节含有一个独一无二的功能ID,因此,驱动程序可通过查询外设的特定ID来识别其设备。

所以,PCI接口标准在ISA之上的主要创新在于配置地址空间。

前文已讲过,PCI驱动程序不需要探测就能访问设备,而这得益于配置地址空间。

在系统引导阶段,PCI硬件设备保持未激活状态,但每个PCI主板均配备有能够处理PCI的固件,固件通过读写PCI控制器中的寄存器,提供了对设备配置地址空间的访问。

配置地址空间的前64字节是标准化的,它提供了厂商号,设备号,版本号等信息,唯一标识一个PCI设备。

同时,它也提供了最多可多达6个的I/O地址区域,每个区域可以是内存也可以是I/O地址。

这几个I/O地址区域是驱动程序找到设备映射到内存和I/O空间的具体位置的唯一途径。

有了这两点,PCI驱动程序就完成了相当于探测的功能。

关于这64个字节的配置空间的详细情况,可参阅《Linux设备驱动程序第三版》P306,不再详述。

下面,我们来看一下8139too网卡设备的配置空间的详细情况。

在2.6内核的系统中,可以在目录/sys/bus/pci/drivers/下看到很多以PCI设备名命名的目录,但不是说这些设备都存在于你的系统中。

我们进入8139too目录,其中有一个以它的设备地址0000:02:01.0命名的目录。

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

其中resource记录了它的6个I/O地址区域。

内容如下:0x0000000000003400 0x00000000000034ff 0x00000000000001010x00000000e0000800 0x00000000e00008ff 0x00000000000002000x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000 0x0000000000000000由该文件可以看出,8139too设备使用了两个I/O地址区域,第一个是它映射的I/O端口范围,第二个是它映射的内存地址空间。

关于这两个值可以在/proc/iomem和/proc/iopor t中得到验证。

一个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); //移除设备。

相关文档
最新文档