Linux下PCI驱动开发
Linux下PCI驱动程序的开发
用 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 总线 的 时 钟 频 率 一 般 在
Linux下PCI设备驱动开发方法及应用实例
种 平台 和 体 系结 构 中 采用 不 同 的方 法 设 计出 各 种 PCI 接 口 卡 , ule 函数从内核中将该驱动程序卸载, 否则返回出错信息。
编 写出 各 种 平台 下 的 接口 卡 驱 动程 序 实 现控 制 系 统高 速 、高 精
1.3 设备驱动程序接口
度、低功耗下通过 PCI 总线对外围 I/O 设备的操作。
1.4 设备驱动程序结构 Linux 的设备驱动程序大致可以分为如下几个 部 分:驱动 程 序 的注 册 与 注销 、设 备 的打 开 与 释放 、设 备 的读 写 操 作、设 备 的
的 内容 , 如 果 缓冲 区 中 的数 据 能 满足 用 户 的要 求 就 返回 相 应 的 控制 操 作 、设备 的 中 断和 轮 询 处理 。驱 动 程序 的 注 册是 通 过 内
struct file_operations dispatch_table; /* 系 统 参 数 dis- patch_table 是 设 备 驱动 程 序 对 应 用 程 序 提 供 诸 如 打 开 , 读 写 , I/ 0 控制等等操作的入口 */
}dev_object ; 3.1 初始化 PCI 设备 在初 始 化 PCI 设 备 时 , 首 先 要 检 查 PCI 总 线 是 否 被 Linux 内 核 支持,主 要 是通 过 内 核函 数 pci_ present()来 实 现 , 该 函 数 用 来 确 定 PCI 总 线 是 否 己 经 被 Linux 内 核 支 持 。 然 后 调 用 pci_ find_device()函数 用 来 查找 系 统 中存 在 的 第一 个 PCI 设 备, 函 数 如下所示: pci_ find_device( PCI_ANY_ID, PCI_ANY_ID,NULL); 该函数检查设备是否插在总线插槽上, 如果在的话则保存
一、如何编写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在中国发展以来,得到了许多公司的青睐。
在国内的玩家也越来越多了,但目前还是停留在玩的水平上,很少有玩家对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设备的IO和内存
linux设备驱动之pci设备的I/O和内存------------------------------------------Pci设备的I/O和内存是一个比较复杂的问题.如下的总线结构:在上图的总线结构中,ethernet设备和pci-pci bridge的同类型资源空间必须要是pci bus0的一个子集例如,pci bus 0的I/O端口资源是0x00CC~0x01CC. Ethernet设备的I/O范围的是0x00CC~0x0xE0.那么pci-pci bridge的I/O端口范围就必须要在0x0xE0~0x01CC之间. 同样,SCSI和VIDEO同类型资源必须要是pci_bus1的子集.pci bus1上有一个pci桥,对应的资源也就是它所连桥上的资源.即pci_bus->self.也就是说,下层总线的资源是它上层总线资源的子集。
上层总线资源是下层总线资源的父集。
其实,每个PCI设备的资源地始地址都是由操作系统设置的.在x86上,都由bios设置好了.假若没有bios的时候,我们应该怎么去设置设备的资源起始范围呢?可能在pci枚举完成之后:1:从根总线开始,设置根总线的资源范围是从0开始,到0xFFFF或者0xFFFFFFFF的最大范围. 2:对其它的设备,可往其资源寄存器全部写入1,就可以求得该资源项的类型和长度.3:设备从根总线的资源那里分得对应长度的资源.4:如果设备是pci-pci bridge,则递归配置它.可能有人会有这样迷惑,对应于上图,如果pci-pci bridge的资源大小是N.而SCSI和video资源范围超过了N怎么办呢?我们必须要注意一点,总线的区间是可以自已设定的,而设备资源的区间是在设计的时候就已经确定好了.也就是说,我们可以更改pci device区间的起始地址,但我们不能改变它的大小.因此,出现了上面所说的这种情况.可能是由bios在处理PCI的时候出现了BUG.我们需要调整总线的资源区间.其实对于pci_bus的资源范围就是它的过滤窗口.对于过滤窗口的作用,我们在枚举的时候分析的很清楚了.CPU访问PC过程是这样的(只有一个根总线和pci-pci bridge过滤窗口功能打开的情况): 1:cpu向pci发出一个I/O请求.首先经过根总线.它会判断是否在它的资源范围内.如果在它的范围,它就会丢向总线所在的每一个设备.包括pci bridge. 如果没有在根总线的资源范围,则不会处理这个请求.2:如果pci设备判断该地址属于它的资源范围,则处理后发出应答4:pci bridge接收到这个请求,它会判断I/O地址是否在它的资源范围内.如果在它的范围,它会把它丢到它的下层子线.5:下层总线经过经过相同的处理后,就会找到这个PCI设备了一个PCI设备访问其它PCI设备或者其它外设的过程:1:首先这个PCI发出一个请求,这个请求会在总线上广播2:如果要请求的设备是在同级总线,就会产生应答3:请求的设备不是在同层总线,就会进行pci bridge.pci桥判断该请求不在它的范围内(目的地不是它下层的设备),就会将它丢向上层.4:这样辗转之后,就能找到对应的设备了经过这样的分析过来,相信对pci bridge的过滤窗口有更深的理解了.Linux中使用struct resource的结构来表示I/O端口或者是设备内存。
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设 备本 身 的驱 动 。
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系统中,设备启动是由设备的驱动程序来完成的。
Linux环境下PCI设备驱动程序
引 言物理隔离网闸是带有多种控制功能的、处于内外网之间的信息安全设备。
近年来在信息安全领域得到了越来越广泛的应用。
网闸是一个系统设备,由外网主机、硬件通道、内网主机三部分组成,其整体结构如图1所示(虚线内的部分为网闸)。
网闸可以阻断内外网络之间基于通用网络协议(例如TCP/IP协议)的数据通信,在网闸一侧的主机上使用专用协议对数据重新打包并通过PCI卡将其传递到另一侧的主机上进行检测,“安全”的数据将被恢复为通用网络协议的格式并传送到目标主机,有效地遏制了基于通用网络协议的攻击行为。
作为内外网服务器之间的数据通道,网闸PCI卡承担了重要的角色,我们称该PCI卡为安全板,两块安全板分别插在内外网主机的PCI槽上,安全板之间通过IDE总线进行通信。
鉴于Linux操作系统具有较高的稳定性与安全性,网闸内外网的主机分别在Linux平台上对数据进行处理。
本文在参照Linux-2.4.20内核源代码中的有关数据结构和函数原型基础上设计并实现了网闸安全板的驱动程序,并给出了网闸的性能测试结果及结果分析。
网闸安全板的硬件设计1. 安全板主控芯片的架构安全板使用FPGA(选用Xilinx公司的XC2VP2-6FG256)作为主控芯片。
FPGA外围接口有PCI总线控制接口、EEPROM控制接口与ATA控制接口,芯片内部由PCI Master、FIFO与ATA Master构成DMA数据通道。
其芯片的体系结构如图2所示。
2. 网闸安全板寄存器的设计编写驱动程序离不开对设备寄存器进行各种操作,在FPGA内部实现网闸安全板的寄存器:PCI配置空间寄存器和本地总线寄存器。
PCI配置寄存器是每一个PCI设备必须具备的,它们在初始化中要用到;而设备本地总线寄存器则在整个驱动程序中都要被使用。
下面对PCI配置空间寄存器和安全板的本地总线寄存器加以简要介绍。
(1) PCI配置空间寄存器。
PCI配置空间独立于内存空间和I/O空间。
它的大小为256B,其中低64B称为头标区,这部分区域的格式是固定的,内容包括:PCI设备号、厂商识别号、命令寄存器、状态寄存器、基址寄存器等重要信息;其余的192B称为设备有关区,不同的设备可以对这部分寄存器进行不同的定义。
基于Linux系统的PCI设备DMA驱动开发
基于Linux系统的PCI设备DMA驱动开发范哲超,吴永亮(内蒙古机电职业技术学院,内蒙古呼和浩特010070)摘要:目前PCI总线已经成为计算机系统中应用最为广泛最为通用的总线标准,Linux的内核能较好地支持PCI总线。
文章探讨了在Linux系统下开发PCI主设备DMA驱动程序的基本过程。
关键词:Linux;DMA;驱动开发中图分类号:TP311文献标识码:A文章编号:1673-1131(2014)04-0117-02Linux驱动程序实现DMA操作,涉及内存I/O端口操作、中断操作、内存映射以及DMA映射过程等内容。
DMA传输过程步骤如下:(1)申请I/O资源、中断资源、内存资源,初始化。
(2)分配DMA缓冲区,设置DMA寄存器。
(3)硬件将数据写入到DMA缓冲区,写入完毕产生PCI 中断。
(4)应答中断、并且唤醒进程读取数据。
I/O端口是驱动程序与设备之间的通信方式,驱动程序需要通过I/O端口对DMA寄存器设置,因此内核允许驱动程序声明自己需要操作的I/O端口。
在申请I/O端口之前调用pci_ resource_start获得I/O空间基地址,调用request_region(unsigned long firstaddress,unsigned long length,const char*name)在Linux系统模块中使用中断前要先申请中断通道(或者中断请求IRQ),使用完毕后释放中断,DevEx.irq是设备中断号,my_interrupt是中断服务例程,SA_SHIRQ中断管理选项表示可以在设备间共享中断,DRV_NAM获得中断设备名,PCI 设备结构体。
request_irq(DevEx.irq,my_interrupt,SA_SHIRQ,DRV_ NAME,&DevEx)free_irq(DevEx.irq,&DevEx)如果request_irq登记成功,在/proc/interrupts文件中可以看到所请求的中断。
linux pci枚举流程
linux pci枚举流程摘要:1.Linux PCI枚举流程简介2.内核空间与用户空间的PCI配置空间3.PCI设备初始化流程4.驱动程序加载与卸载5.实战应用:Linux内核模块开发正文:Linux作为一种开源的操作系统,具有强大的硬件兼容性和丰富的设备驱动支持。
在Linux系统中,PCI(Peripheral Component Interconnect,外围组件互连)设备的枚举和驱动开发是至关重要的。
本文将详细介绍Linux PCI枚举流程,以及与之相关的内核空间与用户空间的PCI配置空间、设备初始化流程、驱动程序加载与卸载等知识点,最后通过实战应用案例,帮助读者更好地理解和掌握Linux内核模块开发。
1.Linux PCI枚举流程简介在Linux系统中,PCI设备的枚举过程主要分为以下几个阶段:(1)PCI配置空间的读取(2)设备驱动程序的加载与初始化(3)设备驱动程序与硬件设备的交互(4)设备驱动程序的卸载2.内核空间与用户空间的PCI配置空间Linux系统中,PCI配置空间分为内核空间和用户空间两部分。
内核空间主要用于操作系统内核对PCI设备的访问和管理,用户空间则用于设备驱动程序对PCI设备的配置和控制。
内核空间的PCI配置空间包括:(1)PCI设备的基本配置寄存器(2)PCI设备的扩展配置寄存器用户空间的PCI配置空间包括:(1)PCI设备的寄存器映射(2)PCI设备的I/O空间映射3.PCI设备初始化流程PCI设备初始化流程主要包括以下几个步骤:(1)设备枚举:操作系统内核通过读取PCI配置空间,获取设备信息。
(2)设备驱动程序加载:根据设备类型和接口,加载相应的设备驱动程序。
(3)设备初始化:设备驱动程序对硬件设备进行初始化,如配置寄存器、中断号等。
(4)设备启动:设备驱动程序发送命令,启动硬件设备。
4.驱动程序加载与卸载在Linux系统中,设备驱动程序的加载与卸载是通过动态链接库实现的。
PCI驱动开发手册
Linux2.6内核PCI驱动程序开发一,PCI相关数据结构说明1.1struct pci_driver这个数据结构在文件/linux/pci.h里,这是Linux内核版本2.4之后为新型的PCI设备驱动程序所添加的,其中最主要的是用于识别设备的id_table结构,以及用于检测设备的函数probe( )和卸载设备的函数remove( )。
struct pci_driver {struct list_head node;char *name;const struct pci_device_id *id_table;int (*probe) (struct pci_dev *dev, const struct pci_device_id *id);void (*remove) (struct pci_dev *dev);int (*save_state) (struct pci_dev *dev, u32 state);int (*suspend)(struct pci_dev *dev, u32 state);int (*resume) (struct pci_dev *dev);int (*enable_wake) (struct pci_dev *dev, u32 state, int enable);};为创建一个正确的struct pci_driver 结构, 只有4个字段需要被初始化:name,id_table,probe和remove。
其中id_table初始化可以用到宏PCI_DEVICE(VENDOR_ID,DEVICE_ID),VENDOR_ID 和DEVICE_ID分别为设备和厂商编号,由板卡生产厂家指定。
Static const struct pci_device_id mypci[] ={{PCI_DEVICE(VENDOR_ID,DEVICE_ID)},{}};1.2pci_dev这个数据结构也在文件include/linux/pci.h里,它详细描述了一个PCI设备几乎所有的硬件信息,包括厂商ID、设备ID、各种资源等。
Linux设备驱动之pci设备的枚举
一:前言Pci,是Peripheral Component Interconnect的缩写,翻译成中文即为外部设备互联.与传统的总线相比.它的传输速率较高.能为用户提供动态查询pci deivce.和局部总线信息的方法,此外,它还能自动为总线提供仲裁.在近几年的发展过程中,被广泛应用于多种平台.pci协议比较复杂,关于它的详细说明,请查阅有关pci规范的资料,本文不会重复这些部份.对于驱动工程师来说,Pci设备的枚举是pci设备驱动编写最复杂的操作。
分析和理解这部份,是进行深入分析pci设备驱动架构的基础。
我们也顺便来研究一下,linux是怎么对这个庞然大物进行封装的。
二:pci架构概貌上图展现了pci驱动架构中,pci_bus、pci_dev之间的关系。
如上图所示,所有的根总线都链接在pci_root_buses 链表中。
Pci_bus ->device链表链接着该总线下的所有设备。
而pci_bus->children链表链接着它的下层总线。
对于pci_dev来说。
pci_dev->bus指向它所属的pci_bus。
Pci_dev->bus_list链接在它所属bus的device链表上。
此外,所有pci设备都链接在pci_device链表中。
三:pci设备的配置空间每个pci设备都有最多256个连续的配置空间。
配置空间中包含了设备的厂商ID,设备ID,I RQ,设备存储区信息等.摘下LDD3中的一副说明图,如下:要注意了,上图是以字节为单位的,而不是以位为单位.那怎么去读取每个设备的配置空间呢?我们在开篇的时候提到过,pci总线为用户提供了动态查询pci设备信息的方法。
在x86上,保留了0xCF8~0xCFF的8个寄存器.实际上就是对应地址为0xCF8的32位寄存器和地址为0xCFC的32位寄存器。
在0xCF8寄存中写入要访问设备对应的总线号, 设备号、功能号和寄存器号组成的一个32位数写入0xCF8.然后从0xCFC上就可以取出对应pci设备的信息.写入到0xCF8寄存器的格式如下:低八位(0~7): (寄存器地址)&0xFC.低二位为零8~10:功能位. 有时候,一个pci设备对应多个功能.将每个功能单元分离出来,对应一个独立的pci device 11~15位:设备号对应该pci总线上的设备序号16~23位:总线号根总线的总线号为0.每遍历到下层总线,总线号+131:有效位如果该位为1.则说明写入的数据有效,否则无效例如:要读取n总线号m设备号f功能号对应设备的vendor id和Device id.过程如下:要写入到0xCF8中的数为:l = 0x80<<23 | n<<16 | m<<11 | f<<8 | 0x00 即:outl(l,0xCF8)从0xCFC中读相关信息:L = Inw(0xCFC) (从上图中看到,vendor id和device id总共占四个字节.因此用in w)所以device id = L&0xFF,V endor id = (L>>8)&0xFF。
Linux系统下PCI转串口卡驱动安装方法
Linux系统下PCI转串口卡驱动安装方法由于公司产品要做行业市场,而产品与行业用户间PC的通讯为RS232串口方式。
而行业用户那里的PC都没有串行口,而且行业用户PC操作系统为Turbo Linux。
怎么办?办公室内有台机器是RedHat Linux 9.0 一个是Fedora Core 5 。
就先在这两个系统上试验吧。
这两台电脑上各自本身就有2个RS232串口。
一、PCI转串口卡安装型号NetMos Nm9835CV1、插入PCI卡到主机2、启动Linux,打开终端3、输入命令:#setserial /dev/ttyS0 -a (COM-1)显示内容:/dev/ttyS0, Line 0, UART: 16550A, Port: 0x3f8, irq: 4Baud_base: 115200, clos_delay: 50, divisor: 0closing_wait: 3000, closing_wait2: infiniteFlags: spd_normal skip_test4、输入命令:#setserial /dev/ttyS2 -a (COM-3)显示内容:/dev/ttyS2, Line 2, UART: unknown, Port: 0x3e8, irq: 4Baud_base: 115200, clos_delay: 50, divisor: 0closing_wait: 3000, closing_wait2: infiniteFlags: spd_normal skip_test第3、4步操作的目的主要是对主机自带串口及PCI扩展串口的区别。
区别在于4显示的内容中UART:未unknow。
不过若您检测这一步的时候UART为16550A而不是unknow,证明你的系统已经认识了扩展的串口,不需要进一步设置,直接跳入第8步测试就可以了。
5、需要输入命令查看一下您当前PCI检测的状态,以便对扩展串口进行设置#more /proc/pci会显示出一堆的信息,不要因为看不懂而吓坏了。
linux下pci的数据读写流程
linux下pci的数据读写流程Linux下PCI的数据读写流程一、引言PCI(Peripheral Component Interconnect,外设互联)是一种计算机总线标准,用于连接计算机的主板与外部硬件设备。
在Linux系统下,PCI设备的数据读写是通过访问设备的寄存器来实现的。
本文将介绍Linux下PCI的数据读写流程。
二、PCI设备的识别和配置在Linux系统启动时,会进行PCI总线的枚举和设备的识别与配置。
Linux内核会扫描PCI总线上的设备,并为每个设备分配唯一的设备标识符,称为PCI设备号。
系统将会为每个PCI设备分配资源,并将设备驱动程序与设备进行匹配。
三、设备驱动程序的加载与初始化在设备识别和配置完成后,系统会加载与该设备匹配的驱动程序。
设备驱动程序是对设备进行管理和控制的软件模块。
当驱动程序被加载时,会进行初始化操作,包括分配内存空间、注册中断处理程序等。
四、设备寄存器的映射设备驱动程序在初始化过程中需要访问设备的寄存器来进行数据的读写。
为了实现对设备寄存器的访问,驱动程序需要将设备寄存器映射到内核空间。
在Linux中,可以通过ioremap函数将设备寄存器的物理地址映射到内核虚拟地址空间。
五、数据读写操作一旦设备寄存器映射完成,驱动程序就可以通过读写内核虚拟地址来实现对设备寄存器的读写操作。
通常,设备寄存器是以字节为单位进行读写的。
驱动程序可以使用readb、readw、readl函数来读取设备寄存器的值,使用writeb、writew、writel函数来向设备寄存器写入数据。
六、同步与互斥在进行数据读写操作时,为了保证数据的正确性和一致性,需要进行同步与互斥操作。
同步操作可以通过在读写操作前后使用memory barrier指令来实现,确保读写操作的顺序性。
互斥操作可以通过自旋锁或信号量机制来实现,防止多个进程同时对设备进行读写操作。
七、数据传输与中断处理在进行数据读写操作的同时,设备可能会触发中断。
linunx pcie发送数据调用的函数
linunx pcie发送数据调用的函数【Linux PCIE发送数据调用的函数】当在Linux系统中使用PCIe总线发送数据时,可以使用一些特定的函数来实现这一功能。
PCIE发送数据的关键是通过驱动程序对PCIE设备进行访问和控制。
在编写驱动程序时,我们可以使用以下函数来进行数据发送操作:1. pci_request_regions:这个函数用于向操作系统请求PCI设备的资源,包括IO端口和内存地址。
将该函数与之后的pci_iomap函数结合使用,可以分配并映射PCI 设备的内存资源,为数据传输做准备。
2. pci_iomap:该函数用于将分配的内存资源映射到用户空间的内存地址空间,以便用户可以直接访问PCI设备的内存区域。
用户程序可以通过访问这些内存地址来发送数据到PCI设备。
3. memcpy_toio:这个函数用于将数据从内核空间复制到IO内存空间。
调用该函数可以将用户空间的数据复制到PCI设备的内存地址,实现数据发送的功能。
4. writel:该函数用于向PCI设备的寄存器中写入数据。
通过调用该函数,可以向PCI设备的控制寄存器中写入特定的命令和参数,控制设备的操作。
综合上述函数,我们可以编写一个函数,将数据从用户空间发送到PCIe 设备。
以下是一个示例:#include <linux/pci.h>#include <asm/io.h>void send_data_to_pcie(struct pci_dev *pdev, void *data, size_t size) {请求PCI设备的资源if (pci_request_regions(pdev, "driver_name") != 0) {printk("Fail to request regions\n");return;}映射设备的内存资源void __iomem *mem_base = pci_iomap(pdev, 0,pci_resource_len(pdev, 0));if (!mem_base) {printk("Fail to iomap\n");pci_release_regions(pdev);return;}将数据从内核空间复制到IO内存空间memcpy_toio(mem_base, data, size);向寄存器中写入命令和参数,实现数据发送writel(reg_value, mem_base + reg_offset);解除内存映射iounmap(mem_base);释放PCI设备的资源pci_release_regions(pdev);}上述代码示例展示了一个基本的数据发送函数。
Linux下基于PCI接口的DSP驱动程序设计
引言 三 、L nx驱动 程序 开 发 iu 数 字信 号处 理器 (S ) DP 在通 讯 、 音 图像处 理 、 据加 密等 各 语 数 L nx下对 TS 2C 46加密 卡进行 驱动 程序 开发 ,要完 成 iu M 306 1 方 面得 到越 来越 广泛 的 应用 。T 60 系列 DP芯 片 内嵌 P I I 00 C S C 接 的工作 主 要包 括 :对 P I C 设备 的查 找 、初始 化 、卸载 ,对字 符 设 口,支 持主/ 从模 式 的读 写, 以 3 Mz 的工作 频率 和 3 地址 / 备 的数 据读 写和控 制 , 中断处 理等 。 3t t 2位 ( )P I - C 设备 初始 化 数据 总 线进 行数 据传 输 , 论上 最 高可 以支持 12B t/ 的数 据 理 3 My e s 传输 , 速度 上远 远超 过 DP常用 的 H I M BP总线 , 全 可 以 在 S P 、 cS 完 在 Ln x . iu2 6内核 中,使 用 p id ie c—r vr结 构体来 定义 P I C 驱 动 。 设 备 查 找 时 通 过 i _ a l 结 构 体 中 的 的 V N E _ D、 dt be ED R I 满足 传 输实 时性 的要 求 。
规 方法 比 ,显著 地提 高 了传 输速度 ,具有较 高的应 用价 值 。
二 、T S 2 C 4 6的 P பைடு நூலகம் 口 M 30 6 1 CI
I 来 区别 不 同的设 备 , D 对于 T S 2 C 4 6 片 , E D R I M 30 6 1 芯 V NE _D 和 DV C. D分别 为 O l4 、0 a0 。 E IE I x 0 c x l6 pc d ie i rv r结构 体 中的 p o e rb 0函数 向内核提 供 了对硬 件进 行 探测 并 初始 化 的接 口 。p oe r b 0函数 完成 的功 能有 : 1 读取 D P 加 密 卡 B s O 2 空 间 基 地 址 和 中 断 号 ,通 过 . S ae 一
Linux系统PCI设备驱动程序的实现方法
增 加 内 核 的 大 小 . 动 内 核 的 源 文 件 。如 果 新 添 加 一 个 硬 件 ,就 要 重 改 新 编 译 内 核 。 如 果 去 掉 一 个 硬 件 ,那 么 这 个 硬 件 的 驱 动 程 序 已 经 编 译 到 内核就 会 造 成资 源 的 浪费 。 另 一 种 方 法 是 将 驱 动 程 序 编 译 成 可 安 装 模 块 ( d l) 通 过 使 用 moue , is o nm d命 令 动 态 加 载 到 内 核 中 . 之 成 为 系 统 内核 的 一 部 分 。 不 使 使 当
较为 容 易 。
oeain p rt s( o
s mc du e o e ; t t mo l wn r
s ie sz
—
t(ra )(t tfe , hr ,s e t oft ) ed s l mc i c a i —,l _ ; z f t(w t)(t tfe ,e ntca ,s e t oft ) r e smc l i i os h r i —,1f ; z _
P I提 供 了 ~ 组 完 整 的 总 线 接 口规 范 . 不 依 附 于 具 体 处 理 器 C 且 的 局 部 总 线 。 P I在 C U 和 原 来 的 系 统 总 线 之 间 插 入 的 一 级 总 线 。 C P
由一 个 桥 接 电 路 实 现 对 这一 层 的管 理 , 实 现 上 下 之 间 的 数 据 传 送 。 并
Src l t t Fi u e
_
P I Pr h rlC m o e tIt cn et ( 围 设 备 互 联 ) 的 简 作 。 Icu el u /sh 中 的 Fl_ prtn C 是 ei ea o p nn ne o nc p r 外 nld / nx f. i i o eai s数 据 结 构 定 义 如 下 : e o 称 .C P I总 线 作 为 一 个 具 有 兼 容 性 强 、 台 无 关 性 等 特 点 的 计 算 机 总 平 线 标 准 . 目 前 计 算 机 系 统 中 应 用 最 为 广 泛 的 总 线 标 准 。 Ln x内 核 是 iu 较 好 地 支 持 了 P I总 线 ,使 得 在 Ln x环 境 下 驱 动 程 序 的 编 写 变 得 C iu
linux下pcie驱动编写流程
linux下pcie驱动编写流程英文回答:PCIe Driver Development Process on Linux.Step 1: Create a skeleton driver.Use the `pci_driver.c` template to create a skeleton driver. This template provides the basic framework for a PCIe driver, including the necessary function hooks.Step 2: Define the PCI device ID.In the header file of the driver, define the PCI device ID that the driver will support. This ID identifies the specific type of PCIe device that the driver can handle.Step 3: Initialize the driver.Implement the `probe()` function, which is called whena new PCIe device is found that matches the driver's device ID. In this function, initialize the driver and allocate any necessary resources.Step 4: Register the driver.Register the driver with the kernel using the`pci_register_driver()` function. This function tells the kernel that the driver is available to handle PCIe devices with the specified device ID.Step 5: Release resources.Implement the `remove()` function, which is called when a PCIe device is removed. In this function, release any resources that were allocated by the driver.Step 6: Test the driver.Use the `lspci` command to verify that the driver is recognized by the kernel. Install the driver on a system with the PCIe device connected and test the functionalityof the driver.中文回答:Linux下PCIe驱动编写流程。
Linux下PCI设备驱动开发方法及应用实例
Linux下PCI设备驱动开发方法及应用实例
高翊宇;马林华
【期刊名称】《微计算机信息》
【年(卷),期】2007(023)010
【摘要】Linux是一种日趋成熟完善的操作系统.越来越多的软硬件厂商开始使用Linux平台来开发自己的产品,因而对基于该平台的设备驱动程序的需求也愈来愈多.同时PCI总线作为一个具有兼容性强、平台无关性等特点从的计算机总线标准.日前得到了广泛的应用.本文通过实例讨论了如何在Linux下实现PCI设备驱动程序的开发.
【总页数】3页(P310-312)
【作者】高翊宇;马林华
【作者单位】710038,陕西西安,空军工程大学工程学院;710038,陕西西安,空军工程大学工程学院
【正文语种】中文
【中图分类】TP316
【相关文献】
1.Linux下无配置信息PCI设备的驱动开发 [J], 陈宁;虢莉敏;王新梅
2.基于Linux系统的PCI设备DMA驱动开发 [J], 范哲超;吴永亮
3.Linux下PCI设备流式DMA驱动开发 [J], 朱红星;苗克坚
4.Linux设备驱动开发方法及应用实例 [J], 王小龙;何克忠;房小翠;丁冬花
5.Linux字符设备驱动开发方法与应用实例 [J], 简杰鸿;刘朝辉
因版权原因,仅展示原文概要,查看原文内容请购买。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
当应用程序对设备文件进行诸如 open、close、read、write 等操作时,Linux 内核将通过 file_operations 结构访问驱动程序提供的函数。 例如, 当应用程序对设备文件执行读操作时, 内核将调用 file_operations 结构中的 read 函数。
PDF created with pdfFactory Pro trial version
[root@gary root]# mknod /dev/lp0 c 6 0
将建立一个主设备号为 6,次设备号为 0 的字符设备文件/dev/lp0。当应用程序对某个设备 文件进行系统调用时,Linux 内核会根据该设备文件的设备类型和主设备号调用相应的驱动 程序,并从用户态进入到核心态,再由驱动程序判断该设备的次设备号,最终完成对相应硬 件的操作。
2. 设备驱动程序模块
Linux 下的设备驱动程序可以按照两种方式进行编译, 一种是直接静态编译成内核的一部分, 另一种则是编译成可以动态加载的模块。如果编译进内核的话,会增加内核的大小,还要改 动内核的源文件,而且不能动态地卸载,不利于调试,所有推荐使用模块方式。 从本质上来讲,模块也是内核的一部分,它不同于普通的应用程序,不能调用位于用户态下 的 C 或者 C++库函数,而只能调用 Linux 内核提供的函数,在/proc/ksyms 中可以查看到内 核提供的所有函数。 在以模块方式编写驱动程序时,要实现两个必不可少的函数 init_module( )和 cleanup_module( ),而且至少要包含<linux/krernel.h>和<linux/module.h>两个头文件。在 用 gcc 编译内核模块时,需要加上-DMODULE -D__KERNEL__ -DLINUX 这几个参数,编 译生成的模块(一般为.o 文件)可以使用命令 insmod 载入 Linux 内核,从而成为内核的一 个组成部分,此时内核会调用模块中的函数 init_module( )。当不需要该模块时,可以使用 rmmod 命令进行卸载,此进内核会调用模块中的函数 cleanup_module( )。任何时候都可以 使用命令来 lsmod 查看目前已经加载的模块以及正在使用该模块的用户数。
2. 设备驱动程序接口
Linux 中的 I/O 子系统向内核中的其他部分提供了一个统一的标准设备接口,这是通过 include/linux/fs.h 中的数据结构 file_operations 来完 成的:
struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); };
Linux 将所有外部设备看成是一类特殊文件,称之为 “设备文件”,如果说系统调用是 Linux 内核和应用程序之间的接口, 那么设备驱动程序则可以看成是 Linux 内核与外部设备之间的 接口。 设备驱动程序向应用程序屏蔽了硬件在实现上的细节, 使得应用程序可以像操作普通 文件一样来操作外 pdfFactory Pro trial version
紧接着就发生了,一 般来说字符设备中的缓存是可有可无的,而且也不支持随机访问。块设 备则是利用一块系统内存作为缓冲区, 当用户进程对设备进行读写请求时, 驱动程序先查看 缓冲区中的内容, 如果缓冲区中的数据能满足用户的要求就返回相应的数据, 否则就调用相 应的请求函数来进行实际的 I/O 操作。块设备主要是针对磁盘等慢速设备设计的,其目的是 避免耗费过多的 CPU 时间来等待操作的完成。一般说来,PCI 卡通常都属于字符设备。 所有已经注册(即已经加载了驱动程序)的硬件设备的主设备号可以从 /proc/devices 文件 中得到。使用 mknod 命令可以创建指定类型的设备文件,同时为其分配相应的主设备号和 次设备号。例如,下面的命令:
一、PCI 总线系统体系结构
PCI 是外围设备互连(Peripheral Component Interconnect)的简称,作为一种通用的总线 接口标准,它在目前的计算机系统中得到了非常广泛的应用。PCI 提供了一组完整的总线接 口规范, 其目的是描述如何将计算机系统中的外围设备以一种结构化和可控化的方式连接在 一起, 同时它还刻画了外围设备在连接时的电气特性和行为规约, 并且详细定义了计算机系 统中的各个不同部件之间应该如何正确地进行交互。 无论是在基于 Intel 芯片的 PC 机中,或是在基于 Alpha 芯片的工作站上,PCI 毫无疑问都 是目前使用最广泛的一种总线接口标准。 同旧式的 ISA 总线不同,PCI 将计算机系统中的总 线子系统与存储子系统完全地分开,CPU 通过一块称为 PCI 桥(PCI-Bridge)的设备来完 成同总线子系统的交互,如图 1 所示。 图 1 PCI 子系统的体系结构
3. 设备驱动程序结构
了解设备驱动程序的基本结构(或者称为框架),对开发人员而言是非常重要的,Linux 的 设备驱动程序大致可以分为如下几个部分:驱动程序的注册与注销、设备的打开与释放、设 备的读写操作、设备的控制操作、设备的中断和轮询处理。 • 驱动程序的注册与注销 向系统增加一个驱动程序意味着要赋予它一个主设备号,这可以通过在驱动程序的 初始化过程中调用 register_chrdev( )或者 register_blkdev( )来完成。而在关闭字符 设备或者块设备时,则需要通过调用 unregister_chrdev( )或 unregister_blkdev( )从 内核中注销设备,同时释放占用的主设备号。 • 设备的打开与释放 打开设备是通过调用 file_operations 结构中的函数 open( )来完成的, 它是驱动程序 用来为今后的操作完成初始化准备工作的。在大部分驱动程序中,open( )通常需要 完成下列工作: 1. 检查设备相关错误,如设备尚未准备好等。 2. 如果是第一次打开,则初始化硬件设备。 3. 识别次设备号,如果有必要则更新读写操作的当前位置指针 f_ops。 4. 分配和填写要放在 file->private_data 里的数据结构。 5. 使用计数增 1。 释放设备是通过调用 file_operations 结构中的函数 release( )来完成的, 这个设备方 法有时也被称为 close( ),它的作用正好与 open( )相反,通常要完成下列工作: 6. 使用计数减 1。 7. 释放在 file->private_data 中分配的内存。 8. 如果使用计算为 0,则关闭设备。
•
PDF created with pdfFactory Pro trial version
桥是一个特殊的 PCI 设备,它负责将 PCI 总线 0 和 PCI 总线 1(即从 PCI 主线)连接在一 起, 通常 PCI 总线 1 称为 PCI-PCI 桥的下游 (downstream ) , 而 PCI 总线 0 则称为 PCI-PCI 桥的上游(upstream )。图中连接到从 PCI 总线上的是 SCSI 卡和以太网卡。为了兼容旧 的 ISA 总线标准, PCI 总线还可以通过 PCI-ISA 桥来连接 ISA 总线,从而能够支持以前的 ISA 设备。图中 ISA 总线上连接着一个多功能 I/O 控制器,用于控制键盘、鼠标和软驱。 图 2 PCI 系统示意图
1. 字符设备和块设备
Linux 抽象了对硬件的处理,所有的硬件设备都可以像普通文件一样来看待:它们可以使用 和操作文件相同的、标准的系统调用接口来完成打开、关闭、读写和 I/O 控制操作,而驱动 程序的主要任务也就是要实现这些系统调用函数。Linux 系统中的所有硬件设备都使用一个 特殊的设备文件来表示,例如,系统中的第一个 IDE 硬盘使用/dev/hda 表示。每个设备文 件对应有两个设备号:一个是主设备号,标识该设备的种类,也标识了该设备所使用的驱动 程序;另一个是次设备号,标识使用同一设备驱动程序的不同硬件设备。设备文件的主设备 号必须与设备驱动程序在登录该设备时申请的主设备号一致, 否则用户进程将无法访问到设 备驱动程序。 在 Linux 操作系统下有两类主要的设备文件:一类是字符设备,另一类则是块设备。字符设 备是以字节为单位逐个进行 I/O 操作的设备, 在对字符设备发出读写请求时, 实际的硬件 I/O