Linux将所有外部设备看成是一类特殊文件,称之为“设备文件”,如果说系统调用是Linux内核和应用程序之间的接口,那么设备驱动程序则可以看成是 Linux内核与外部设备之间的接口。设备驱动程序向应用程序屏蔽了硬件在实现上的细节,使得应用程序可以像操作普通文件一样来操作外部设备。

1. 字符设备和块设备

Linux抽象了对硬件的处理,所有的硬件设备都可以像普通文件一样来看待:它们可以使用和操作文件相同的、标准的系统调用接口来完成打开、关闭、读写和 I/O控制操作,而驱动程序的主要任务也就是要实现这些系统调用函数。Linux系统中的所有硬件设备都使用一个特殊的设备文件来表示,例如,系统中的第 一个IDE硬盘使用/dev/hda表示。每个设备文件对应有两个设备号:一个是主设备号,标识该设备的种类,也标识了该设备所使用的驱动程序;另一个是 次设备号,标识使用同一设备驱动程序的不同硬件设备。设备文件的主设备号必须与设备驱动程序在登录该设备时申请的主设备号一致,否则用户进程将无法访问到 设备驱动程序。

在Linux操作系统下有两类主要的设备文件:一类是字符设备,另一类则是块设备。字符设备是以字节为单位逐个进行I/O操作的设备,在对字符设备发出读 写请求时,实际的硬件I/O紧接着就发生了,一般来说字符设备中的缓存是可有可无的,而且也不支持随机访问。块设备则是利用一块系统内存作为缓冲区,当用 户进程对设备进行读写请求时,驱动程序先查看缓冲区中的内容,如果缓冲区中的数据能满足用户的要求就返回相应的数据,否则就调用相应的请求函数来进行实际 的I/O操作。块设备主要是针对磁盘等慢速设备设计的,其目的是避免耗费过多的CPU时间来等待操作的完成。一般说来,PCI卡通常都属于字符设备。

 
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 __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);int (*readdir) (struct file *, void *, filldir_t);unsigned int (*poll) (struct file *, struct poll_table_struct *);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long (*compat_ioctl) (struct file *, unsigned int, unsigned long);int (*mmap) (struct file *, struct vm_area_struct *);int (*open) (struct inode *, struct file *);int (*flush) (struct file *, fl_owner_t id);int (*release) (struct inode *, struct file *);int (*fsync) (struct file *, loff_t, loff_t, int datasync);int (*aio_fsync) (struct kiocb *, int datasync);int (*fasync) (int, struct file *, int);int (*lock) (struct file *, int, struct file_lock *);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);int (*check_flags)(int);int (*flock) (struct file *, int, struct file_lock *);ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);int (*setlease)(struct file *, long, struct file_lock **);long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);
};

当应用程序对设备文件进行诸如open、close、read、write等操作时,Linux内核将通过file_operations结构访问驱动程 序提供的函数。例如,当应用程序对设备文件执行读操作时,内核将调用file_operations结构中的read函数。
 
3. 设备驱动程序模块

Linux下的设备驱动程序可以按照两种方式进行编译,一种是直接静态编译成内核的一部分,另一种则是编译成可以动态加载的模块。如果编译进内核的话,会增加内核的大小,还要改动内核的源文件,而且不能动态地卸载,不利于调试,所有推荐使用模块方式。

从本质上来讲,模块也是内核的一部分,它不同于普通的应用程序,不能调用位于用户态下的C或者C++库函数,而只能调用Linux内核提供的函数,在/proc/ksyms中可以查看到内核提供的所有函数。

在以模块方式编写驱动程序时,要实现两个必不可少的函数init_module( )和cleanup_module( ),而且至少要包含和两 个头文件。一般使用LDD3 例程中使用的makefile 作为基本的版本,稍作改变之后用来编译驱动,编译生成的模块(一般为.ko文件)可以使用命令insmod载入Linux内核,从而成为内核的一个组成部分,此时内核会调用 模块中的函数init_module( )。当不需要该模块时,可以使用rmmod命令进行卸载,此进内核会调用模块中的函数cleanup_module( )。任何时候都可以使用命令来lsmod查看目前已经加载的模块以及正在使用该模块的用户数。

 

4. 设备驱动程序结构

了解设备驱动程序的基本结构(或者称为框架),对开发人员而言是非常重要的,Linux的设备驱动程序大致可以分为如下几个部分:驱动程序的注册与注销、设备的打开与释放、设备的读写操作、设备的控制操作、设备的中断和轮询处理。

驱动程序的注册与注销

向系统增加一个驱动程序意味着要赋予它一个主设备号,这可以通过在驱动程序的初始化过程中调用register_chrdev( )或者register_blkdev( )来完成。而在关闭字符设备或者块设备时,则需要通过调用unregister_chrdev( )或unregister_blkdev( )从内核中注销设备,同时释放占用的主设备号。但是现在

程序员都倾向于动态创建设备号和设备结点,动态创建设备号和设备结点需要几个指定的函数,具体
可以参见“Linux字符驱动中动态分配设备号与动态生成设备节点”。

设备的打开与释放

打开设备是通过调用file_operations结构中的函数open( )来完成的,它是驱动程序用来为今后的操作完成初始化准备工作的。在大部分驱动程序中,open( )通常需要完成下列工作:

1.检查设备相关错误,如设备尚未准备好等。

2.如果是第一次打开,则初始化硬件设备。

3.识别次设备号,如果有必要则更新读写操作的当前位置指针f_ops。

4.分配和填写要放在file->private_data里的数据结构。

5.使用计数增1。

释放设备是通过调用file_operations结构中的函数release( )来完成的,这个设备方法有时也被称为close( ),它的作用正好与open( )相反,通常要完成下列工作:

1.使用计数减1。

2.释放在file->private_data中分配的内存。

3.如果使用计算为0,则关闭设备。

设备的读写操作

字符设备的读写操作相对比较简单,直接使用函数read( )和write( )就可以了。但如果是块设备的话,则需要调用函数block_read( )和block_write( )来进行数据读写,这两个函数将向设备请求表中增加读写请求,以便Linux内核可以对请求顺序进行优化。由于是对内存缓冲区而不是直接对设备进行操作 的,因此能很大程度上加快读写速度。如果内存缓冲区中没有所要读入的数据,或者需要执行写操作将数据写入设备,那么就要执行真正的数据传输,这是通过调用 数据结构blk_dev_struct中的函数request_fn( )来完成的。

设备的控制操作

除了读写操作外,应用程序有时还需要对设备进行控制,这可以通过设备驱动程序中的函数ioctl( )来完成,ioctl 系统调用有下面的原型: int ioctl(int fd, unsigned long cmd, ...),第一个参数是文件描述符,第二个参数是具体的命令,一般使用宏定义来确定,第三个参数一般是传递给驱动中处理设备控制操作函数的参数。ioctl( )的用法与具体设备密切关联,因此需要根据设备的实际情况进行具体分析。


设备的中断和轮询处理

对于不支持中断的硬件设备,读写时需要轮流查询设备状态,以便决定是否继续进行数据传输。如果设备支持中断,则可以按中断方式进行操作。  
 
 
基本框架

在用模块方式实现PCI设备驱动程序时,通常至少要实现以下几个部分:初始化设备模块、设备打开模块、数据读写和控制模块、中断处理模块、设备释放模块、设备卸载模块。下面给出一个典型的PCI设备驱动程序的基本框架,从中不难体会到这几个关键模块是如何组织起来的。

/* 指明该驱动程序适用于哪一些PCI设备 */
static struct pci_device_id my_pci_tbl [] __initdata = {
{PCI_VENDOR_ID, PCI_DEVICE_ID,PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0,}
};/* 对特定PCI设备进行描述的数据结构 */
struct device_private {
...
}/* 中断处理模块 */
static irqreturn_t device_interrupt(int irq, void *dev_id)
{
/* ... */
}/* 设备文件操作接口 */
static struct file_operations device_fops = {
owner: THIS_MODULE, /* demo_fops所属的设备模块 */
read: device_read, /* 读设备操作*/
write: device_write, /* 写设备操作*/
ioctl: device_ioctl, /* 控制设备操作*/
mmap: device_mmap, /* 内存重映射操作*/
open: device_open, /* 打开设备操作*/
release: device_release /* 释放设备操作*/
/* ... */
};/* 设备模块信息 */
static struct pci_driver my_pci_driver = {
name: DEVICE_MODULE_NAME, /* 设备模块名称 */
id_table: device_pci_tbl, /* 能够驱动的设备列表 */
probe: device_probe, /* 查找并初始化设备 */
remove: device_remove /* 卸载设备模块 */
/* ... */
};static int __init init_module (void)
{
/* ... */
}static void __exit cleanup_module (void)
{pci_unregister_driver(&my_pci_driver);
}/* 加载驱动程序模块入口 */
module_init(init_module);/* 卸载驱动程序模块入口 */
module_exit(cleanup_module);

上面这段代码给出了一个典型的PCI设备驱动程序的框架,是一种相对固定的模式。需要注意的是,同加载和卸载模块相关的函数或数据结构都要在前面加上 __init、__exit等标志符,以使同普通函数区分开来。构造出这样一个框架之后,接下去的工作就是如何完成框架内的各个功能模块了。

 

转载于:https://www.cnblogs.com/zhuyp1015/archive/2012/06/30/2571400.html

Linux PCI 设备驱动基本框架(一)相关推荐

  1. 浅谈Linux PCI设备驱动(一)

    要弄清楚Linux PCI设备驱动,首先要明白,所谓的Linux PCI设备驱动实际包括Linux PCI设备驱动和设备本身驱动两部分.不知道读者理不理解这句话,本人觉得这句话很重要,对于PCI.US ...

  2. 浅谈Linux PCI设备驱动(二)

    我们在浅谈Linux PCI设备驱动(一)中(以下简称浅谈(一) )介绍了PCI的配置寄存器组,而Linux PCI初始化就是使用了这些寄存器来进行的.后面我们会举个例子来说明Linux PCI设备驱 ...

  3. linux PCI设备驱动

    1.PCI 简介 1.1 PCI 引脚 为处理数据.寻址.接口控制.仲裁以及系统功能,PC接口作为目标设备的设备至少有47条引脚.作为总线主设备的设备至少有49条引脚.必要的引脚在左边,任选的引脚在右 ...

  4. linux驱动 pcie 框架_Linux PCI 设备驱动基本框架(二)

    针对相应设备定义描述该PCI设备的数据结构: structdevice_private {/*注册字符驱动和发现PCI设备的时候使用*/ struct pci_dev *my_pdev;// stru ...

  5. Linux总线设备驱动框架的理解(非常棒的文章!)

    以下内容源于微信公众号:嵌入式企鹅圈.有格式内容上的修改,如有侵权,请告知删除. Linux的设备驱动框架,即某类设备对应的驱动的框架. 这里是"Linux总线设备驱动框架",应该 ...

  6. linux设备模型 字符设备,Linux 字符设备驱动模型之框架解说

    一.软件操作硬件设备模型 在进行嵌入式开发的过程中,在常做的事情就是驱动配置硬件设 备,然后根据功能需求使用硬件设备,实现功能的逻辑.如下图为其 相互之间的关系. 如上图所示: 驱动程序:主要作为操作 ...

  7. windows linux 融合,Windows和Linux的设备驱动框架的对比融合研究

    摘要:把驱动框架分为三层,针对各层在Windows和Linux中的实现方法的不同,对Windows和Linux的设备驱动框架进行对比研究.从接口函数,应用程序访问驱动程序的路径,驱动程序具体实现及安装 ...

  8. Linux usb设备驱动

    原文地址:http://blog.csdn.net/chenjin_zhong/article/details/6329316 1.Linux usb设备驱动框架 USB是通用串行总线的总称,Linu ...

  9. Linux usb设备驱动详解

    1.Linux usb设备驱动框架 USB是通用串行总线的总称,Linux内核几乎支持所有的usb设备,包括键盘,鼠标,打印机,modem,扫描仪.Linux的usb驱动分为主机驱动与gadget驱动 ...

最新文章

  1. springboot(十六):使用Jenkins部署Spring Boot
  2. TaskExecutor设计与实现
  3. oracle怎样开启服务,Oracle 11g必须开启的服务及服务详细介绍
  4. 人工智能、机器学习和深度学习的区别?
  5. 在基于nuxt的移动端页面中引用mint UI的popup组件之父子组件传值
  6. 2021“MINIEYE杯”中国大学生算法设计超级联赛(4)Display Substring(后缀数组+二分)
  7. arch linux界面优化,Archlinux 启动优化
  8. springmvc中的单例问题
  9. jdbc连接mysql数据库过程_jdbc连接数据库的步骤
  10. Windows 文件一直被占用,无法删除(对应解决方法)
  11. ecshop添加商品选择品牌时如何按拼音排序
  12. 【To Understand!回文串8 哈希表】LeetCode 336. Palindrome Pairs
  13. mysql模式匹配详解_老生常谈MYSQL模式匹配 REGEXP和like的用法
  14. 金字塔 2020-12-29
  15. MSP430的JTAG接口和BSW接口
  16. matlab中grid相关知识
  17. 争取下周内初步实现AHB的BFM
  18. 单片机第三讲 ——中断及定时器基本知识
  19. 常见的云服务器运营商及相关的优惠活动
  20. element-ui之el-image-viewer(图片查看器)

热门文章

  1. 硬件基础:理解串口通信以及232,485,422常见问题
  2. 盘点程序猿经常说的那些行话,你了解吗?
  3. 盘点十个超级实用的 JS 特性
  4. java弱引用怎么手动释放,十分钟理解Java中的弱引用,十分钟java引用
  5. linux安装mq报5724,linux下MQ简单配置手册.doc
  6. java druid jdbc例子_JDBC【使用Druid连接数据库,DBUtils工具类的使用】
  7. Docker 常用命令(二)
  8. .net core高性能通讯开源组件BeetleX
  9. ThinkPHP3.2 实现阿里云OSS上传文件
  10. ORACLE关于段的HEADER_BLOCK的一点浅析