PCI设备驱动
一、如何编写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。
PCIePort总线驱动
PCIePort总线驱动2.1 关于本指南本指南介绍了PCI Express Port Bus驱动程序的基础知识,并提供了如何使服务驱动程序向PCI Express Port Bus driver注册/注销的信息。
2.2 什么是PCIe端⼝总线驱动程序PCI Express Port是⼀种逻辑PCI-PCI桥接结构。
有两种类型的PCI Express端⼝:Root Port 和 Switch Port。
Root Port从PCI Express根Complex⽣成PCI Express链接,⽽Switch Port将PCI Express链接连接到内部逻辑PCI总线。
switch端⼝,它的副总线代表了switch的内部路由逻辑,被称为swicth的上⾏端⼝。
switch的下⾏端⼝从switch的内部路由总线桥接到PCI Express switch下⾏PCI Express链路的总线。
根据端⼝类型的不同,PCI Express Port最多可以提供四种不同的功能,在本⽂档中称为服务。
PCI Express Port的服务包括本地热插拔⽀持(HP)、电源管理事件⽀持(PME)、⾼级错误报告⽀持(AER)和虚拟通道⽀持(VC)。
这些服务可以由单个复杂的驱动程序处理,也可以由相应的服务驱动程序单独分发和处理。
2.3 为什么使⽤PCI Express端⼝总线驱动程序?在现有的Linux内核中,Linux设备驱动程序模型只允许单个驱动程序处理物理设备。
PCI Express Port是具有多种不同服务的PCI-PCI桥接设备。
为了维护⼀个⼲净和简单的解决⽅案,每个服务可能都有⾃⼰的软件服务驱动程序。
在这种情况下,多个服务驱动程序将竞争单个PCI-PCI桥设备。
例如,如果⾸先加载PCI Express根端⼝热插拔服务驱动程序,那么它将声明⼀个PCI-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里添加硬件时看到的那些是一个意思。
PCIE驱动开发流程
#ifdef CONFIG_PM
.suspend = xxx_pm_suspend,
.resume = xxx_pm_resume,
#endif /* CONFIG_PM */
};
结构体中 name 指明 PCIE 模块的名称,id_table 指明了 PCIE 的设备驱动号也
就是为哪个设备进行驱动等。
PCIE 开发流程
前言:对于 USB、PCIE 设备这种挂接在总线上的设备而言,USB、PCI 只是它们 的”工作单位”,它们需要向”工作单位”注册(使用 usb_driver,pci_driver),并接 收 ” 工 作 单 位 ” 的 管 理 ( 被 调 入 probe() 、 调 出 disconnect/remove() 、 放 假 suspend()/shutdown()、继续上班 resume()等),但设备本身可能是一个工程师、 一个前台或者一个经理,因此做好工程师,前台或者经理是其主题工作,这部分 对应于字符设备驱动,tty 设备驱动,网络设备驱动等。
对于 linux 来说这些调用都会变成系统调用,并指向改设备对应的 open(),read()
函数,对于该设备来说,指向了 xxx_open 和 xxx_read。
static struct file_operations xxx_fops = {
.owner = THIS_MODULE,
.llseek
第二节 PCIE 设备实现细节
由于 PCIE 设备的驱动的开发都是按照一个统一规范的框架进行的。因此以一 个字符设备为例说明这个框架的实现机制。在所有 PCIE 驱动开发的过程中 2.1 驱动程序的初始化和注销
涉及的பைடு நூலகம்数为 module_init(xxx_init_module),并在 init 中完成的功能为注册 PCIE 设备,具体函数内容如下所示:
PCI驱动编程
目录一、字符设备和块设备 (2)二、设备驱动程序接口 (2)三、设备驱动程序模块 (3)四、设备驱动程序结构 (4)1.驱动程序的注册与注销 (4)2.设备的打开与释放 (4)3.设备的读写操作 (4)4.设备的控制操作 (5)5.设备的中断和轮询处理 (5)五、PCI驱动程序框架 (5)1.关键数据结构 (5)a. pci_driver (5)b. pci_dev (6)2.基本框架 (9)六、框架的具体实现之模块操作 (12)1.struct pci_device_id (12)2.初始化设备模块 (12)3.卸载设备模块: (15)4.中断处理: (16)七、框架的具体实现之设备文件操作 (16)1.设备文件操作接口 (16)2.打开设备模块 (17)3.释放设备模块 (17)4.设备数据读写和控制信息模块 (18)5.内存映射模块 (19)八、附录 (19)1.PCI设备私有数据结构 (19)2.PCI配置寄存器 (20)参考资料: (21)一、字符设备和块设备Linux抽象了对硬件的处理,所有的硬件设备都可以像普通文件一样来看待:它们可以使用和操作文件相同的、标准的系统调用接口来完成打开、关闭、读写和I/O控制操作,而驱动程序的主要任务也就是要实现这些系统调用函数。
Linux系统中的所有硬件设备都使用一个特殊的设备文件来表示,例如,系统中的第一个IDE硬盘使用/dev/hda表示。
每个设备文件对应有两个设备号:一个是主设备号,标识该设备的种类,也标识了该设备所使用的驱动程序;另一个是次设备号,标识使用同一设备驱动程序的不同硬件设备。
设备文件的主设备号必须与设备驱动程序在登录该设备时申请的主设备号一致,否则用户进程将无法访问到设备驱动程序。
在Linux操作系统下有两类主要的设备文件:一类是字符设备,另一类则是块设备。
字符设备是以字节为单位逐个进行I/O操作的设备,在对字符设备发出读写请求时,实际的硬件I/O紧接着就发生了,一般来说字符设备中的缓存是可有可无的,而且也不支持随机访问。
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是要申请的硬件中断号。
PCI网卡驱动程序分析
PCI网卡驱动程序分析驱动程序的架构通常包括以下几个模块:初始化模块、数据传输模块、中断处理模块和控制模块。
初始化模块负责识别和配置网卡,并进行必要的初始化操作。
数据传输模块负责处理数据的收发,包括数据的发送和接收。
中断处理模块用于处理网卡发出的中断信号,通知操作系统有数据可处理。
控制模块负责向网卡发送命令以及接收网卡的状态信息。
在操作系统启动时,PCI网卡驱动程序会加载到内核中,并通过操作系统提供的接口进行初始化。
驱动程序首先会进行设备识别,通过读取设备的PCI配置空间来获取设备的基本信息,如设备ID、厂商ID等。
然后驱动程序会检测设备的状态并进行必要的配置,如设置中断向量和启用设备。
初始化完成后,驱动程序会注册中断处理函数,以便在网卡有数据到达时能及时响应。
数据传输模块负责处理数据的收发。
在接收数据时,驱动程序会设置网卡的接收缓冲区,并等待中断信号。
当网卡接收到数据后,会触发一个中断信号,驱动程序会响应该中断并读取网卡的接收缓冲区来获取数据。
在发送数据时,驱动程序会设置网卡的发送缓冲区,并将数据写入该缓冲区。
然后驱动程序会发送一个命令给网卡,通知其开始发送数据。
发送完成后,网卡会触发一个中断信号,驱动程序会响应该中断并检查发送状态。
中断处理模块用于处理网卡发出的中断信号。
当网卡有数据到达或发送完成时,会触发一个中断信号。
驱动程序会通过中断控制器来识别该中断,并调用相应的中断处理函数。
中断处理函数会读取网卡的状态信息,如接收缓冲区中的数据长度、发送状态等,并进行相应的处理。
控制模块用于向网卡发送命令以及接收网卡的状态信息。
驱动程序会向网卡发送各种命令,如启动接收、停止接收、启动发送等。
同时,驱动程序也会定期检查网卡的状态信息,如发送缓冲区是否可用、接收缓冲区中是否有数据等。
在分析PCI网卡驱动程序时,还需要考虑一些其他的因素,如总线传输、内存管理、中断处理竞争等。
总线传输是指驱动程序通过PCI总线与网卡进行数据传输的过程,包括地址传输、数据传输等。
基于Windows操作系统PCI设备驱动程序通用设计方法
主要 问题及常用解决方法 ,并介绍一种封装设备驱动 的方法 。
关键词 : 驱动程序 ; C ;内存 映射 ;中断处理 ; PI 封装
PCIDe i e Drv rf r Un v r a sg e h d Ba e n W i d wsOS v c i e o i e s l De i n M t o s d o n o
Ke r s y wo d :Dr e P ;Me r p;I tru t r c s P c a i g i r; CI v mo yma n e r p o e s; a k g n p
在设 计和使 用 P I C 设备 时 ,经 常要在计 算机 的软件 中访 问和控制硬件 设备 ,但 Widw 操作 系统 ( 括 Widw 9 / nos 包 n o s5 9 、Widw T 8 n o sN 、Widw 0 0X n o s2 0 / P等为 了保 证 系 统 的安 全 性 、稳定 性和可移 植性 ,对 应用程序访 问硬件 资源加 以限制 , 这就要求开 发设 备驱 动程序 以实现计算 机软件 对 P I C 设备 的
( IA和 P I 如 S C)在许 多硬件工作机 制上是不 同的 ,所 以驱 动 能模块与主机 P I C 总线接 口功能。
22 寄 存 器 .
程序设 计也不同。P I0 2是一种 P I C9 5 C 总线设 备 ,提供 板卡功
1 开发 工具 的选择
开发设 备驱动采 用的 主要 开发工 具是微 软为设 备开发者 提供 的软件包 D vc r e i ( D ) e i D vr t D K 。这个软件包包括有关 e i K 设 备开发 的文档 、编译需 要 的头 文件和库 文件 、调 试工具 和
VxWorks下PCI桥接设备驱动设计
a eo i ie adm l f 一娜 g T ia c ec bst r e ds npoeso eP I g ei l 6 6O p84 r bcm n wdr n Oe a e g r n . h tl dsr e ed vr ei r s f t C de v ep 9 5 iM c50 sr e i i h i g c rh b d c x l
T 3 P1
’
D ie ee rcs rteP IB ig ei n e x r s r rD s rP oesf C r eD v eu drV Wok v t o h d c
Xu Ro g e g n fn 刁l 哪 u GI
(e I ̄ )
n o ] t n sadI o ao , U T Wua 407 ) t f e r i n f m tn H S , hn 304 E co c nr i
述了 VW r 下 PI x o s C 桥接设备 p95 在 M ̄5 下驱 动程序设计过 程 , k l 66 x r8 0 4 完成 P I C 设备 的初始化和驱动接 口函数 , 实现 D A M
数据传输 和中断服务 。
关键词 vWo s P I D x r C MA k
中图 分 类 号
F GA的 R P AM 缓冲区
在通信、 航空 、 航天 等高精尖技术及实时性要求极
高 的 领 域 中 。本 课 题 , 主控 部 分 , 用 V wrs 其 选 xo k 为操 作 系统 , M e50和 P( 5 作 为平 台 , 以 p84 b 66 9 以实
现 如上要 求 。
图 1 B G系 统 组 成 框 图 3
Ab ta t Aln i p l a o so V Wok OSi mb d e se , h e eo me t n p l a in f r d csu d rV Wok s c r o gw t a p i t n f x r s h ci ne e d d s t y ms t ed v lp n d a pi t so p o u t n e x r s a c o
PCI驱动开发
Byte3
Byte2
Byte1
Byte0
Device ID
Vendor ID
PCI Status
PCI Command
Class Code
Revision ID
Built-In Self Test
Header Tyine Size
Base Address Register 0 ~ 5
PCI 驱动开发 1. PCI 简介 PCI 总线标准是一种将系统外部设备连接起来的总线标准,是 PC 中最重要的总线,实际上 是系统的各个部分如何交互的接口。传输速率可达到 133MB/s。在当前的 PC 体系结构中, 几乎所有的外部设备采用的各种各样的接口总线,均是通过桥接电路挂接到 PCI 系统上。 在这种 PCI 系统中,Host/PCI 桥称为北桥,连接主处理器总线到基础 PCI 局部总线。PCI 与其他总线的接口称为南桥,其中南桥还通常含有中断控制器、IDE 控制器、USB 控制器和 DMA 控制器等。南桥和北桥组成主板的芯片组。
{ slotNumber.u.bits.DeviceNumber = deviceNumber; for ( functionNumber = 0; functionNumber < PCI_MAX_FUNCTION;
functionNumber++ ) { slotNumber.u.bits.FunctionNumber = functionNumber; if (!HalGetBusData(PCIConfiguration, busNumber, slotNumber.u.AsULONG, &pciData, sizeof(ULONG) )) { deviceNumber = PCI_MAX_DEVICES; break; } if (pciData.VendorID == PCI_INVALID_VENDORID ) { continue; } if ( ( VendorId != PCI_INVALID_VENDORID ) && ( pciData.VendorID != VendorId || pciData.DeviceID != DeviceId )) { continue; } pPciDeviceLocation->BusNumber = busNumber;
QNX下PCI设备实时驱动的设计
Q X作 为 一 个 分 布式 实 时操 作 系统 . 为用 户提 供 良好 N 能
的 实 时 控 制 性 能 、 靠 的稳 定 性 与 分 布 式 处 理 , 可 特别 是 在 高 复杂 度 的 系统 中更 能 体 现 其 优 势 。对 于较 复 杂 的 系统 来 说 ,
一
定 , 够 满 足 实 时性 要 求 , 出 结 果 正确 。最 后 将 该 设 备 驱 动 能 输
来 的 不便 。介 绍 了 Q X 下 P I 线设 备 的 实时 驱 动 的 开发 流程 , 以 某 P I 备 为例 . 出 了 实 时驱 动设 计 中 包括 N C 总 并 C设 给 定位 设 备 、 获取 总线 信 息 、 址 映射 、 始化 配置 以及 伺 服 中断 等 功 能 的 具 体 实现 . 后 将 该 驱 动 应 用到 某 Q X 目标 地 初 最 N
应 用 在 某 课 题 的 Q X 实 时 目标 机 系 统 中 , 过 试 验 验 证 . N 经 达
cr i Q X t gtyt r e s d at e e a N ress m po sta vna . t n a e v i g
K yw rs Q X;C u ; a t ed vrm p ig d rs e od : N P I sr li r e ; apn des b e —m i a
Q X evrn etT e e o o ig u co s feli edi r ei r epandi ldn n igdv e gtn u N ni m n. h nt lwn nt n a t r e s nae xli cu ig dn ei , e ig s o h fl f i o r —m v d g e n i f c t b
RTX环境下PCI设备实时驱动的开发
De e o m e t fRe ltm eDr v fPCIDe i e v lp n a —i i e o o vc Un e d rRTX v r n e t En io m n
HUANG i n P J a . ANG - u . Ya h a XUE S u . u h nh
高 效 的 扩展 性 和 可 靠 的 稳 定性 。作 为 实 时 平 台 ,R X 只 提 供 T
R X 作 为 一 个 完 全 的 Wid w 扩 展 系 统 , 并 不 对 原 有 T no s
Wid w 系统架构作任 何修 改与封装 , no s 而是对 Wid ws n o 硬件 抽象层的扩展 。精确的时间执行机制对系统相 当重要 ,R X T 提供的定时器周期最 低可 以做到 1¨ 。 O s通过应用高速 的 IC D P 信息和同步机制 , T R X可 以实现与 Wid ws 间的数据通信 no 之 以及进程 间同步。R X提供了对 I 以及内存的精确控制 , T / 0
为 实 时任 务 的执行 提 供 了 10 0 %的 可 靠性 。 R X 提 供 了对 微软 Vsa Su i 列 开 发平 台 的全 面 支 T iul tdo系
了部分外 围硬件的驱动支持,如部分 网卡、US B、串并 E等 , l 而大部 分 的第 三方硬件 也 只是提供 了 Widws iu 或 n o 、Ln x V Wok 等操作系统 的支持 , R X的支持并不理想 。 x rs 对 T 因此 , 在构建基于 R X的实时应用系统时 , T 需要考虑数据采 集或总 线通信等外 围设备的 R X驱动 问题 。 T 对于硬件驱动支持 ,一般 有 2种选择 :() R X代 理 1从 T 商 购买或委托其开发 ,这种选择成本较 高。() 2选择开放性较 好 的硬件厂商进行 自主开发 ,这种选择 必须 要求硬件厂商提 供 完整 的硬件资料和技术 方案书 。
pci总线驱动
在总结的过程中参考了下面一些资料,在此表示感谢:[1] <Understanding Linux Network Internals>[2] <Linux那些事儿之我是U盘>[3] <Essential Linux Device Drivers>[4] <Linux Device Driver> 3rd Edition.1. 总线、设备和驱动1.1 简单介绍Linux设备模型中三个很重要的概念就是总线、设备和驱动,即bus,device和driver。
它们分别对应的数据结构分别为structbus_type,struct device和structdevice_driver。
总线是处理器与一个或多个设备之间的通道,在设备模型中,所有的设备都通过总线相连。
在最底层,Linux 系统中的每一个设备都用device结构的一个实例来表示。
而驱动则是使总线上的设备能够完成它应该完成的功能。
在系统中有多种总线,如PCI总线、SCSI总线等。
系统中的多个设备和驱动是通过总线让它们联系起来的。
在bus_type中两个很重要的成员就是structkset drivers和structkset devices。
它分别代表了连接在这个总线上的两个链,一个是设备链表,另一个则是设备驱动链表。
也就是说,通过一个总线描述符,就可以找到挂载到这条总线上的设备,以及支持该总线的不同的设备驱动程序。
1.2 总线、设备与驱动的绑定在系统启动时,它会对每种类型的总线创建一个描述符,并将使用该总线的设备链接到该总线描述符的devices链上来。
也即是说在系统初始化时,它会扫描连接了哪些设备,并且为每个设备建立一个struce device变量,然后将该变量链接到这个设备所连接的总线的描述符上去。
另一方面,每当加载了一个设备驱动,则系统也会准备一个structdevice_driver结构的变量,然后再将这个变量也链接到它所在总线的描述符的drivers链上去。
35、PCI设备驱动简介
35、PCI设备驱动简介PCI(Peripheral Component Interconnect)总线标准是⼀种将系统外部设备连接起来的总线标准,速度可以达到133MB/s,它是PC中最重要的总线,其他总路线如ISA总线,USB总线等,都挂载在PCI总线上(通过桥接电路)。
由Intel推出的⼀种局部总线,为32位数据地址总线,可以扩展为64位,⽀持突发读写,及多组外围设备。
在PCI系统中,Host/PCI称为北桥,连接主处理器总线到基础PCI局部总线;PCI-ISA桥称为南桥,连接基础PCI总线到ISA总线。
其中南桥通常还含有中断控制器,IDE控制器,USB控制器和DMA控制器等设备。
图⽰ P412PCI有三个相互独⽴的物理地址空间:设备存储器地址空间,I/O地址空间和配置空间。
由于PCI⽀持设备即插即⽤,所以PCI设备不占⽤固定的内存地址空间或I/O地址空间,⽽是可以由操作系统决定其映射的基址。
PCI总线规范定义的配置Hha总长度为256个字节,配置信息按⼀定的顺序和⼤⼩依次存放。
根据读取PCI配置空间,可以得到PCI设备的所有资源。
[1]中讲述了多种读取PCI配置空间的⽅法,包括通过最基本的I/O端⼝操作进⾏读取,通过DDK提供的函数HalGetBusData,HalSetBusData在NT式驱动中进⾏读取,在WDM驱动中的读取⽅法,等。
⼀般程序所看到的内存指针都是虚拟内存,如果想操作物理内存,必须使⽤DDK提供的内核函数WRITE_REGISTER_XX,READ_REGISTER_XX系列函数。
MmAllocateContiguousMemory分配连续的物理地址,MmGetPhysicalAddress得到连续的物理内存地址。
代码#pragma PAGEDCODENTSTATUS InitMyPCI(IN PDEVICE_EXTENSION pdx,IN PCM_PARTIAL_RESOURCE_LIST list){PDEVICE_OBJECT fdo = pdx->fdo;ULONG vector;KIRQL irql;KINTERRUPT_MODE mode;KAFFINITY affinity;BOOLEAN irqshare;BOOLEAN gotinterrupt = FALSE;PHYSICAL_ADDRESS portbase;BOOLEAN gotport = FALSE;PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = &list->PartialDescriptors[0];ULONG nres = list->Count;BOOLEAN IsMem0 = TRUE;for (ULONG i = 0; i < nres; ++i, ++resource){ // for each resourceswitch (resource->Type){ // switch on resource typecase CmResourceTypePort:portbase = resource->u.Port.Start;pdx->nports = resource->u.Port.Length;pdx->mappedport = (resource->Flags & CM_RESOURCE_PORT_IO) == 0;gotport = TRUE;break;case CmResourceTypeMemory:if (IsMem0){pdx->MemBar0 = (PUCHAR)MmMapIoSpace(resource->u.Memory.Start,resource->u.Memory.Length,MmNonCached);pdx->nMem0 = resource->u.Memory.Length;IsMem0 = FALSE;}else{pdx->MemBar1 = (PUCHAR)MmMapIoSpace(resource->u.Memory.Start,resource->u.Memory.Length,MmNonCached);pdx->nMem1 = resource->u.Memory.Length;}break;case CmResourceTypeInterrupt:irql = (KIRQL) resource->u.Interrupt.Level;vector = resource->u.Interrupt.Vector;affinity = resource->u.Interrupt.Affinity;mode = (resource->Flags == CM_RESOURCE_INTERRUPT_LATCHED)Latched : LevelSensitive;irqshare = resource->ShareDisposition == CmResourceShareShared;gotinterrupt = TRUE;break;default:KdPrint(("Unexpected I/O resource type %d\n", resource->Type));break;} // switch on resource type} // for each resourceif (!(TRUE&& gotport&& gotinterrupt )){KdPrint((" Didn't get expected I/O resources\n"));return STATUS_DEVICE_CONFIGURATION_ERROR;}if (pdx->mappedport){ // map port address for RISC platformpdx->portbase = (PUCHAR) MmMapIoSpace(portbase, pdx->nports, MmNonCached);if (!pdx->mappedport){KdPrint(("Unable to map port range %I64X, length %X\n", portbase, pdx->nports));return STATUS_INSUFFICIENT_RESOURCES;}} // map port address for RISC platformelsepdx->portbase = (PUCHAR) portbase.QuadPart;NTSTATUS status = IoConnectInterrupt(&pdx->InterruptObject, (PKSERVICE_ROUTINE) OnInterrupt, (PVOID) pdx, NULL, vector, irql, irql, LevelSensitive, TRUE, affinity, FALSE);if (!NT_SUCCESS(status)){KdPrint(("IoConnectInterrupt failed - %X\n", status));if (pdx->portbase && pdx->mappedport)MmUnmapIoSpace(pdx->portbase, pdx->nports);pdx->portbase = NULL;return status;}#define IMAGE_LENGTH (640*480)//申请⼀段连续物理地址来读取图像PHYSICAL_ADDRESS maxAddress;maxAddress.u.LowPart = 0xFFFFFFFF;maxAddress.u.HighPart = 0;pdx->MemForImage = MmAllocateContiguousMemory(IMAGE_LENGTH,maxAddress);PHYSICAL_ADDRESS pycialAddressForImage = MmGetPhysicalAddress(pdx->MemForImage);WRITE_REGISTER_BUFFER_UCHAR((PUCHAR)pdx->MemBar0+0x10000,(PUCHAR)&pycialAddressForImage.u.LowPart,4);return STATUS_SUCCESS;}⽰例代码 P428参考[1] Windows 驱动开发技术详解,张帆。
PCI总线驱动
PCI总线驱动的机制1.总线子系统初始化系统初始化时会挪用■void __init driver_init(void){。
buses_init();。
}■★I nclude/linux/#define decl_subsys(_name,_type,_uevent_ops) \ struct subsystem _name##_subsys = { \.kset = { \.kobj = { .name = __stringify(_name) }, \.ktype = _type, \.uevent_ops =_uevent_ops, \} \}★Drivers/base/static struct kobj_type ktype_bus = {.sysfs_ops = &bus_sysfs_ops,};★static decl_subsys(bus, &ktype_bus, NULL);bus_subsys={.ket = {.kobj = { .name = “bus”,.ktype = &ktype_bus,.uevent_ops = NULL,}}★int __init buses_init(void){return subsystem_register(&bus_subsys);}在buses_init中挪用subsystem_register时,bus_subsys作为sysfs root目录下的项被成立(因为其的值为NULL)■pci总线初始化★struct bus_type pci_bus_type = {.name = "pci",.match = pci_bus_match,.uevent = pci_uevent,.probe = pci_device_probe,.remove = pci_device_remove,.suspend = pci_device_suspend,.suspend_late = pci_device_suspend_late,.resume_early = pci_device_resume_early, .resume = pci_device_resume,.shutdown = pci_device_shutdown,.dev_attrs = pci_dev_attrs,};★struct device_attribute pci_dev_attrs[] = { __ATTR_RO(resource),__ATTR_RO(vendor),__ATTR_RO(device),__ATTR_RO(subsystem_vendor),__ATTR_RO(subsystem_device),__ATTR_RO(class),__ATTR_RO(irq),__ATTR_RO(local_cpus),__ATTR_RO(modalias),#ifdef CONFIG_NUMA__ATTR_RO(numa_node),#endif__ATTR(enable, 0600, is_enabled_show, is_enabled_store),__ATTR(broken_parity_status,(S_IRUGO|S_IWUSR),broken_parity_status_show,broken_parity_status_store),__ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store),__ATTR_NULL,};★static int __init pci_driver_init(void){return bus_register(&pci_bus_type);}postcore_initcall(pci_driver_init);通过postcore_initcall声明,使pci_driver_init函数被链接到.段。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
PCI设备驱动1.PCI设备驱动一一、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 之上的主要创新在于配置地址空间。
2.PCI设备驱动二前文已讲过,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/ioport中得到验证。
3.PCI设备驱动三为了能看到实际的运行效果,我们选择8139too网卡作为示例,从该网卡的linux驱动程序中裁剪相关代码。
一个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列表。