嵌入式第七章_第1页
嵌入式第七章_第2页
嵌入式第七章_第3页
嵌入式第七章_第4页
嵌入式第七章_第5页
已阅读5页,还剩85页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第7章 Linux设备驱动程序,在Linux中有2种工作状态:内核态和用户态,应用程序处于用户态,无法直接访问硬件设备,需要借助驱动程序进入内核态才能访问硬件 7.1 Linux驱动程序概述 7.2 Linux设备管理结构 7.3 Linux驱动程序开发基础 7.4 Linux驱动程序编写,7.1 Linux驱动程序概述,7.1.1 Linux驱动程序分类 7.1.2 驱动程序的作用 7.1.3 嵌入式Linux驱动程序特点 7.1.4 Linux驱动程序开发流程,7.1.1 Linux驱动程序分类,Linux内核必须能够用标准的方式操作设备,每一类设备的驱动程序都提供了通用的接口,供内核在需

2、要请求它们的服务时加以使用。 字符设备 块设备 网络设备,字符设备,字符设备原意是指那些只能按顺序一个字节一个字节读取的设备,但事实上一些高级的字符设备也可以从指定位置一次读取一块数据。其特点为: 按字节访问 顺序访问 一般不使用缓存技术 字符设备是最简单的设备,可以象文件一样访问,应用程序使用系统调用open、read、write、close访问,就像这个设备是个普通文件一样 鼠标,声卡等都为字符设备,初始化字符设备时,驱动程序要在内核注册,在字符设备开关表chrdevs中增加一个device_struct条目,主设备号作为这个表的索引。 每个device_struct结构包含2个元素,驱动

3、程序的名字和文件操作的指针,这些文件操作位于驱动程序中。/proc/devices显示的就是chrdevs表中的内容(参见include/linux/major.h)。 每个字符设备文件对应一个VFS节点(用mknod或devfs函数创建),其中包含主次设备号,当使用文件时就可以通过VFS节点中的主设备号找到相应device_struct条目,并最后把相应的文件系统操作映射到驱动程序函数。,块设备,块设备指那些可以从任意位置读取任意长度数据的设备,它以块为单位进行处理,块的大小通常为0.5KB到32KB。其特点为: 按块访问 随机访问 常常采用缓存技术 硬盘、光盘驱动器等都为块设备。 块设备也

4、支持象文件一样进行访问,注册时使用的条目也是device_struct,但注册使用的表是块设备开关表blkdevs。,与字符设备不同,块设备需要分类,如SCSI类、IDE类等,块设备类的驱动程序提供类相关的接口,参见fs/devices.c。 块设备驱动程序除了提供文件操作的接口,还要提供buffer Cache的接口,为了支持buffer Cache,块设备驱动程序要填充一个结构blk_dev_struct。 与字符设备不同,块设备拥有缓冲机制,这代表,对块设备的操作不一定会引起实际硬件的I/O。 块设备主要是针对磁盘等慢速设备设计的,可以避免耗费过多的CPU时间来等待。,网络设备是linu

5、x中一类比较特殊的设备,它不能通过文件节点访问。 内核启动时,系统通过网络设备驱动程序注册已经存在的网络设备,设备用标准的支持网络的机制把收到的数据转送到相应的网络层。 每个网络接口,都有一个device结构表示,内核中网络设备管理表dev_base是一个指向device的指针,相当于一个链表的表头,所有的网络设备都放在这个链表中。 网络设备驱动程序的详细介绍本课从略。,网络设备,7.1.2 驱动程序的作用,从传统嵌入式开发角度来看,Linux驱动程序是直接操控硬件的软件: 直接读写硬件寄存器,控制硬件 操作设备缓冲区数据 读写存储介质,比如flash或硬盘 操作输出设备和执行机构,例如打印,

6、开关门禁等等,从应用软件编写人员来看,Linux驱动程序提供软件访问硬件的机制: 应用软件通过驱动程序安全高效的访问硬件 驱动程序文件节点可以方便的提供访问权限控制 驱动程序作为一个隔离的中间层软件,将底层细节隐藏起来,提高了软件的可移植性和可重用性 接口鲜明的Linux驱动程序便于将软件分层,并隔离有缺陷的代码,对于项目的管理有积极贡献,7.1.3 嵌入式Linux驱动程序特点,嵌入式Linux驱动程序需求多样: 嵌入式设备硬件各异,芯片花样繁多,总是需要相应的驱动程序 嵌入式系统硬件不停的更新进步,嵌入式芯片厂商如intel,samsung,freescale,TI,ST每年都有新品推出,

7、很多都需要新的驱动 嵌入式处理器往往资源有限,比如处理速度、存储器容量、总线带宽、电池容量等都有限制 个性化和资源有限决定了开发驱动程序需要更专业软硬件知识 嵌入式产品开发还往往面临上市时间的压力,驱动程序对比应用程序,应用程序是一个进程 编程从主函数main()开始 主函数main()返回即是进程结束 驱动程序是一系列内核函数 驱动程序包含了一些函数,是内核的一部分 open() close() read() write() 这些函数由内核在适当的时候来调用 这些函数可以用来完成硬件访问等操作,访问Linux设备驱动的方法,提供dev文件系统节点和proc文件系统节点 应用程序通过dev文件

8、节点访问驱动程序 字符型驱动一般通过标准的文件I/O访问 块设备在上层加载文件系统,比如以FAT32的形式访问 网络设备通过SOCKET来访问 应用程序通过proc文件节点可以查询设备驱动信息 驱动程序位于内核源代码的drivers目录下,按照层次结构分门别类放置,占kernel源代码超过50%。 开发完毕的驱动程序,放置在/lib/modules/kernel-version里,7.1.4 Linux驱动程序开发流程,熟悉设备的特性 确定设备驱动程序是哪一类 编写测试用代码 搜集可重用的代码 编写自己的驱动程序代码 调试、编码、测试,Linux驱动程序加载方式,驱动程序直接编译入内核 驱动程

9、序在内核启动时就已经在内存中 可以保留专用存储器空间 驱动程序以模块形式存储在文件系统里,需要时动态载入内核 驱动程序按需加载,不用时节省内存 驱动程序相对独立于内核,升级灵活 授权方式灵活,Linux驱动程序模块加载,设备驱动程序加载时首先需要调用入口函数init_module(),完成设备驱动的初始化,如寄存器置位、结构体赋值等。 设备驱动程序还要向内核注册该设备,字符设备调用register_chrdev()完成注册,块设备调用的则是register_blkdev(),注册成功后,会获得系统分配的主次设备号,并建立与文件系统的关联。 设备驱动在卸载时需要回收相应资源、令寄存器复位并从系统

10、中注销设备,字符和块设备分别调用unregister_chrdev()和unregister_blkdev()。 操作设备通过系统调用完成,如open、read、write、ioctl等。,驱动程序开发流程,交叉编译开发循环: 1. 移植/编写驱动程序 2. 编译驱动程序 C语言文法级调整 3. 加载驱动程序 4. 软硬件协同调试 5. 卸载驱动程序 6. 重新修改代码,重复2,开发/调试反复消耗大量开发时间 驱动程序可以提供多种灵活方式来减少反复 减少反复次数 减少每次反复消耗的时间 具体来说,驱动程序可以采用以下技巧 用参数来改变配置 用文件系统 采用gdb 建立嵌入式Linux平台,移植

11、和编写驱动程序往往是最具挑战的工作,高效的嵌入式Linux驱动程序开发,驱动程序的开发周期一般较长,对产品的面世时间有着重要影响 驱动程序质量的好坏,直接关系到系统工作效能和稳定性,对项目的成败起着关键作用 完成一个设备驱动程序,需要: 分离硬件相关和硬件无关的代码 划分驱动程序的抽象层次 规定驱动程序行为 驱动程序之间的交互操作 驱动程序给用户提供的接口行为,7.2 Linux设备管理结构,7.2.1 概述 7.2.2 主设备号和次设备号 7.2.3 Linux设备命名习惯,7.2.1 概述,内核常常使用设备类型、主设备号和次设备号来标识一个具体的设备。 用户希望能用同样的应用程序和命令来访

12、问设备和普通文件,为此Linux中的设备管理应用了设备文件这个概念来统一设备的访问接口。 简单的说,系统试图使它对所有各类设备的输入、输出看起来就好像对普通文件的输入、输出一样。,由于Linux中将设备当作文件来处理,所以对设备进行操作的系统调用和对文件操作的类似,主要包括open()、read()、write()、ioctl()、close()等。 应用程序发出系统调用指令以后,会从用户态转换到内核态,通过内核将open()这样的系统调用转换成对物理设备的操作。,Linux内核体系结构,7.2.2 主设备号和次设备号,设备管理中,除了设备类型(字符设备或块设备)以外,内核还需要一对称做主、次

13、设备号的参数,才能唯一表示设备。 主设备号(major number)相同的设备使用相同的驱动程序,系统依靠主设备号标识不同的驱动程序,可通过/proc/devices看到。内核代码中的相关说明在documentation/devices.txt中。 而次设备号 (minor number) 用来区分同一驱动程序的具体设备实例。 2.4中主次设备号都为8位,2.6扩展为12位和20位 例如,第一IDE接口上的所有磁盘及其分区共用同一主设备号3,而次设备号则为0,1,2,3 。,7.2.3 Linux设备命名习惯,Linux习惯上将设备文件放在目录/dev或其子目录之下。 设备文件命名(通常由两

14、部分组成)规则为: 第一部分通常较短,可能只有2或3个字母组成,用来表示设备大类。例如,普通硬盘如IDE接口的为“hd”,软盘为“fd”,U盘为“sd”。 第二部分通常为数字或字母用来区别设备实例。 例如,/dev/hda、/dev/hdb、/dev/hdc表示第一、二、三块硬盘;而 dev/hda1、/dev/hda2、/dev/hda3则表示第一硬盘的第一、二、三分区。,7.3 Linux驱动程序,7.3.1 驱动程序基本功能 7.3.2 常用接口介绍 7.3.3 驱动程序的工作过程 7.3.4 关键数据结构,7.3.1 驱动程序基本功能,1. 对设备初始化和释放。如对音频设备而言包括向内

15、核注册设备,设置音频初始的输入输出参数 (如采样频率、采样宽度等)、分配音频设备使用的内核内存等工作。 2. 对设备进行管理。包括实时参数设置以及提供对设备的操作接口。 3. 读取应用程序传送给设备文件的数据并回送应用程序请求的数据。这需要在用户空间、内核空间、总线及外设之间传输数据。 4. 检测和处理设备出现的错误。,7.3.2 常用接口介绍,主要有如下接口函数: open(): 打开设备,为其创建文件结构对象,分配文件句柄,将设备对应的文件结构对象与文件句柄关联。 release(): 关闭设备文件,释放文件结构对象和文件句柄,与open相反。 read(): 从设备中读数据,需要提供字符

16、串指针。 write(): 向字符设备写数据,需要提供所写内容指针。 llseek(): 用来修改设备的当前读写位置,并返回新的读写位置。,poll(): 用来检查设备读写操作是否会被阻塞,并返回一个掩码字,每一位代表某种操作是否被阻塞 ioctl(): 指用户进程可以发送特定的命令来获取设备的信息、控制设备的操作方式或操作参数。 flush(): 清除内容。 mmap(): 将设备内存映射到进程地址空间。通常只有块设备驱动程序使用。 select(): 进行选择操作。如果驱动程序没有提供select 入口,select 操作将会认为已经准备好进行任何的I/O 操作。 fasync():用来通

17、知设备文件标志字中的FASYNC标志发生了变化。,7.3.3 驱动程序的工作过程,下面的论述针对的是字符设备驱动程序的一般情况,目的是帮助我们理解和开发自己的字符设备驱动程序。 对于linux内核封装的复杂tty和控制台设备驱动程序,虽然也属于字符设备,但一般都属于内核开发人员的工作,本处不做进一步介绍。 下面从创建字符设备文件、打开字符设备文件和读写字符设备三个方面进行论述。,创建字符设备文件,创建了字符设备文件,就可以在/dev目录下看到它和使用它了。 Linux下可以用mknod命令(前面有例子)或者devfs_register函数(后面有例子)创建。 创建文件在linux内部使用了mk

18、nod()系统调用,内核实现函数是sys_mknod。 sys_mknod调用vfs_mknod,然后调用当前文件系统相关函数(如ext2_mknod)创建文件,打开字符设备文件,用户进程使用open()系统调用打开字符设备文件 open()系统调用使用的内核函数为sys_open,它首先申请一个未使用的文件句柄,然后调用flip_open()函数。 flip_open创建一个与文件句柄号对应的struct file结构体,并对其进行初始化及设置,然后调用chrdev_open()函数。 chrdev_open()函数根据字符设备开关表找到并调用对应的*_open()函数(如果函数存在的话)。

19、,读写字符设备,用户进程分别用read()和write()系统调用读写字符设备中的数据。 read()和write()系统调用分别使用内核函数sys_read()和sys_write()函数,sys_read()和sys_write()函数再分别调用vfs_read()和vfs_write()函数。 最后根据字符设备开关表找到并调用对应的*_read()和*_write函数(如果函数存在的话)。,7.3.4 关键数据结构,file_operations数据结构 inode数据结构 file数据结构,file_operations数据结构,在Linux系统内部,I/O设备的存/取通过一组固定的入

20、口点来进行,这组入口点是由每个设备的设备驱动程序提供的。 具体来说,设备驱动程序所提供的这组入口点由一个文件操作结构来向系统进行说明。 file_operations结构定义于linux/fs.h 文件中,随着内核的不断升级,file_operations 结构也越来越大,不同版本的内核会稍有不同。 下面是2.4.18版本的file_operations 结构,struct file_operations struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct fi

21、le *, 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 (*mma

22、p) (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 *, in

23、t, 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

24、)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); ;,下面是2.6.24版本的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 *

25、, 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 *, stru

26、ct poll_table_struct *);,int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); 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

27、) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct f

28、ile *, 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 (*dir_notify)(struct file *filp, unsigned long

29、arg); 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

30、file_lock *); ;,file_operations 结构中的成员名字都对应一个系统调用,在用户利用系统调用对设备进行操作(如write和read) ,就要利用这个结构来找到实际执行的函数。 file_operations 结构中的成员全部是函数指针,所以实质上就是函数跳转表。每个进程对设备的操作,都会根据major、minor 设备号,转换成对file_operations 结构的访问。 从某种意义上讲,写驱动程序的主要任务就是完成file_operations 结构中的函数。 在用户自己的驱动程序中,首先要根据驱动程序的功能,完成file_operations 结构的函数实现。,

31、不需要的函数接口可以直接在file_operations 结构中初始化为NULL。 file_operations变量会在驱动程序初始化时,注册到系统内部。 当操作系统对设备进行操作时,会调用驱动程序注册的file_operations结构中的函数指针。 上面看到,2.6和2.4中这个结构不同,2个版本之间的驱动可能需要移植。,inode数据结构,文件系统处理的文件所需要的信息在inode(索引节点)数据结构中。 在inode中保存了页结构,用于进行设备缓冲,当进行读写时,首先检查是否有inode存在,然后检查是否有它的缓冲,若没有就要发出请求,若页缓冲后在别处被改写,则标记为脏页。 inod

32、e中有指向file_operations的指针,指向设备文件时,会有一些特别的属性,如i_bdev和i_cdev分别指向块设备和字符设备。 它的定义如下也在linux/fs.h中,定义比较长,这里从略。,file数据结构,file数据结构主要是与文件系统对应的驱动程序使用。它在linux/fs.h中定义(2.6.24)如下: struct file union struct list_headfu_list; struct rcu_head fu_rcuhead; f_u; struct pathf_path; #define f_dentryf_path.dentry #define f_v

33、fsmntf_path.mnt const struct file_operations*f_op; atomic_tf_count; unsigned int f_flags; mode_tf_mode;,loff_tf_pos; struct fown_structf_owner; unsigned intf_uid, f_gid; struct file_ra_statef_ra; u64f_version; #ifdef CONFIG_SECURITY void*f_security; #endif void*private_data; #ifdef CONFIG_EPOLL stru

34、ct list_headf_ep_links; spinlock_tf_ep_lock; #endif /* #ifdef CONFIG_EPOLL */ struct address_space*f_mapping; ;,7.4 Linux驱动程序编写,以一个虚拟设备驱动程序为例 7.4.1 驱动程序功能 7.4.2 具体实现 7.4.3 驱动程序中使用的函数 7.4.4 在驱动程序中使用中断 7.4.5 对硬件的访问,7.4.1 驱动程序功能,实现虚拟设备的写入、读出等操作。这个驱动程序并不基于特定硬件设备的,实际上仅仅是对内存进行读、写操作。 当执行写入操作时,将会对特定的存储空间进行写

35、入;当执行读出操作时,将会对该存储空间进行数据的读取;同时还可以利用ioctl进行清除该存储空间的操作。 这个mydrv设备的实现文件是mydrv.c,其中的文件接口flle_operations提供了mydrv_read、mydrv_write、mydrv_ioctl函数。,1. 函数mydrv_read()的功能是从mybuf100中读取字符串,并传递给调用的进程。 2. 函数mydrv_write()的功能是将调用的进程传入的字符串赋值给mybuf,如果字符串的长度超过100,则只取前100个字符。 3. 函数mydrv_ioctl()中仅仅实现了一个控制功能:清除mybuf存储区。,7

36、.4.2 具体实现,首先,要根据设备功能的需要,编写file_operations结构中的操作函数。 其次,要向系统注册该设备,包括字符设备的注册,devfs节点的注册与中断响应函数的注册。然后就可以利用对应的文件进行设备操控了。具体如下:,1驱动源程序 # include # include # include # include # include # include # include # include #include /仅为2.6内核使用 # include /仅为2.6内核使用 # include # define MYDRV_CLS 1 /定义清存储区命令字 char mybu

37、f100; /存储区域 int mydrv_major = 99; /主设备号 devfs_handle_t dev_handle; /保存devfs的注册句柄 struct cdev *mydrv_cdev;/仅为2.6内核使用 dev_t mydrv_dev; /仅为2.6内核使用,/第一步:编写file_operations函数 ssize_t mydrv_read(struct file * filp, char * buf, size_t count,loff_t * f_pos); static ssize_t mydrv_write(struct file * filp,cons

38、t char * buf,size_t count,loff_t * ppos); static int mydrv_ioctl( struct inode * inode,struct file * file, unsigned int cmd, unsigned long arg); struct file_operations mydrv_ops= owner: THIS_MODULE, read: mydrv_read, write: mydrv_write, ioctl: mydrv_ioctl, ;,/ mydrv_read()将内核空间的mybuf中的字符串赋给用户空间的buf区

39、 ssize_t mydrv_read(struct file * filp, char * buf, size_t count,loff_t * f_pos) /filp:指向设备文件的指针;f_pos:偏移量 if(count 100) count = 100; /忽略大于100部分 if(copy_to_user(buf, mybuf, count) ) /从内核区复制到用户区 printk(error reading, copy_to_usern”); retum -EFAULT; retum count; ,/ mydtv_write()将用户空间的buf字符串赋给内核空间的mybu

40、f 数组中 static ssize_t mydrv_write(struct file * filp,const char * buf, size_t count,loff_t * ppos) int num; num=count100? count: 100; if(copy_from_user(mybuf, buf, num) /mybufbuf return -EFAULT; printk(mydrv_write succeed! n”); return num; ,static int mydrv_ioctl( struct inode * inode,struct file * f

41、ile, unsigned int cmd, unsigned long arg) /MYDRV-CLS则清除mybuf数组内容 switch ( cmd ) case MYDRV_CLS: mybuf0 = 0 x0; return 0; default: return -EINVAL; / 指定模块init和exit函数 module_init(mydrv_init); module_exit(mydrv_exit);,/第二步:向系统注册该设备(2.4版本) int mydrv_init(void) int result; printk(“initing. n”); result = r

42、egister_chrdev(mydrv_major, “mydrv”, ,/第二步:向系统注册该设备(2.6版本) int mydrv_init(void) int result; printk(“initing. n”); mydrv_dev=MKDEV(mydrv_major,0); result=register_chrdev_region(mydrv_dev,1,”mydrv”); mydrv_cdev=cdev_alloc(); if (mydrv_cdev = NULL) printk(KERN_WARNING “Register cdev error n”); return -

43、1; else cdev_init(mydrv_cdev, ,/注销设备(2.4) void mydrv_exit(void) unregister_chrdev(mydrv_major, “mydrv”); devfs_unregister( dev_handle ); printk(exiting. n); /注销设备(2.6) void mydrv_exit(void) cdev_del(mydrv_cdev); unregister_chrdev_region(mydrv_dev,1); devfs_remove(“mydrv”); printk(exiting. n); /GPL许可

44、声明 MODULE_LICENSE(GPL);,2设备驱动程序编译和安装 2.4可采用下面的命令对mydrv.c进行编译: # gcc -c mydrv.c D_KERNEL_ -DMODULE -O2 -g -Wall o mydrv.o 2.6版本所需编译选项可能比较多,一般建有makefile文件,使用make编译 如果没有出错的话,将会在本目录下生成一个mydrv.o(2.6为mydrv.ko)文件。 模块操作必须是以root身份进行的(用命令su转换成root身份) 模块的加载操作, #insmod mydrv.o/(2.6为mydrv.ko) 如果模块已经过调试,想直接加入内核的话

45、,可以通过修改内核使用的makefile文件来完成。,如果使用了devfs,此时在设备文件系统挂接的目录(通常是/dev)下,就可以找到mydrv文件节点了。否则则需要手工为设备添加文件节点: #mknod mydrv c 99 0 此时就可以对设备进行读、写、ioctl等操作了。 当不再需要对设备进行操作时,可以采用下面的命令卸载模块: #rmmod mydrv 要列出已加载模块,可以使用: #lsmod,3设备的使用: #include int main() int fp; char bufl00; if(fp = open(“/dev/mydrv”, 0) 0 ) printf(Coul

46、d not opened! n); return -1; else printf(File open ok! n); read(fp , buf , sizeof(buf); printf(the buffer content is: %s n,buf);,printf(Please input( 100): ); scanf(%s, buf); write(fp , buf , sizeof(buf); read(fp , buf , sizeof(buf); printf(the buffer content is: %s n,buf); ioctl(fp,1,0); read(fp ,

47、buf , sizeof(buf); printf(the buffer content is: %s n,buf); close(fp); return 0; 编译生成可执行文件之后,就可以用该程序对mydrv设备的文件节点进行操作。,7.4.3 驱动程序中使用的函数,2.4使用的相关函数 2.6使用的相关函数 其它: printk copy_to_user copy_from_user module_init module_exit kmalloc kfree,2.4使用的相关函数,register_chrdev unregister_chrdev devfs_register和devfs

48、_unregister,在设备驱动程序的入口点,设备驱动程序初始化时向系统进行登记,以便系统在适当的时候调用。Linux系统里,通过调用register_chrdev 向系统注册字符型设备驱动程序。 register_chrdev 定义为: #include #include int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);,register_chrdev,其中,major 是为设备驱动程序向系统申请的主设备号,如果为0,则系统为此驱动程序动态分配一个主设备号。nam

49、e 是设备名。fops 即上述对各个调用的入口点说明。 此函数返回0 时表示成功。返回-EINVAL 表示申请的主设备号非法,一般来说是主设备号大于系统所允许的最大设备号。返回-EBUSY 表示所申请的主设备号正在被其他设备程序使用。如果动态分配主设备号成功,此函数将返回所分配的主设备号。 如果register_chrdev 操作成功,设备名就会出现在/proc/devices 文件中。,当设备驱动模块从Linux 内核中卸载,对应的主设备号必须被释放。 下面的函数卸载设备驱动: int unregister_chrdev(unsigned int major, const char *nam

50、e); 此函数的参数为主设备号major 和设备名name。Linux内核把name 和major 在内核注册的名称对比,如果不相等,卸载失败,并返回-EINVAL;如果major大于最大的设备号,也返回-EINVAL。,unregister_chrdev,devfs_register和devfs_unregister,函数原型: devfs_register(devfs_handle_t dir, const char *name, unsigned int flags,unsigned int major, unsigned int minor, umode_t mode, void *o

51、ps, void *info) void devfs_unregister(devfs_handle_t de);,2.6使用的相关函数,cdev_alloc cdev_init cdev_add和cdev_del devfs_mk_cdev和devfs_remove MKDEV register_chrdev_region和unregister_chrdev_region alloc_chrdev_region,cdev_alloc,cdev_alloc()函数分配一个字符设备结构体: struct cdev *cdev_alloc(void); cdev结构在linux/cdev.h中定义

52、: struct cdev struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; ;,cdev_init,初始化已经分配好的cdev结构,函数原型为: void cdev_init(struct cdev *, const struct file_operations *); cdev代表要初始化的字符设备驱动程序指针 fops代表字符设备的设备文件操作函数组结构指针,cdev_add和

53、cdev_del,cdev_add将cdev代表的驱动程序添加到内核: int cdev_add(struct cdev *, dev_t, unsigned); cdev_del从内核中删除已注册字符设备cdev: void cdev_del(struct cdev *); cdev代表要初始化的字符设备驱动程序指针 dev_t代表要被注册的字符设备驱动程序的起始设备号 count代表要被注册的字符设备驱动程序的设备号数量 cdev_add函数成功时返回0,不成功返回非0,devfs_mk_cdev和devfs_remove,功能为在dev下建立和删除文件节点 函数原型为: int devf

54、s_mk_cdev(dev_t dev, umode_t mode, const char *fmt, .) _attribute_ (format(printf, 3, 4); void devfs_remove(const char *fmt, .) _attribute_ (format(printf, 1, 2);,MKDEV,MKDEV(int major, int minor): 根据主设备号major和次设备号minor构建设备号 MAJOR(dev_t dev): 根据设备号dev获得主设备号 MINOR(dev_t dev): 根据设备号dev获得次设备号,register_

55、chrdev_region和unregister_chrdev_region,手动分配和释放设备号(linux/fs.h): int register_chrdev_region(dev_t first, unsigned int count, const char *name); void unregister_chrdev_region(dev_t first, unsigned int count); first表示要分配设备号的起始值 count是请求的连续设备号的数量 name是设备名称,alloc_chrdev_region,自动分配设备号: int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, const char *name); 该函数需要传递给它指定的第一个次设备号firstminor(一般为0)和要分配的设备数count,以及设备名name,调用该函数后自动分配得到的设备号保存在dev中。,7.4.4 在驱动程序中使用中断,中断服务程序,又称为驱动程序的下半部。在Linux 系统中并不是直接从中断向量表调用设备驱动程序的中断服务子程

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论