您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 管理学资料 > 第11章 嵌入式Linux设备驱动开发
章、嵌入式Linux设备驱动开发Linux设备驱动的基本概念Linux设备驱动程序的基本功能Linux设备驱动的运作过程常见设备驱动接口函数掌握字符设备驱动程序的编写掌握键盘设备驱动程序的编写了解块设备的编写流程本章的主要内容11.1设备驱动概述11.2字符设备驱动编程11.3GPIO驱动程序实例11.4块设备驱动编程11.5中断编程11.6按键驱动程序实例11.7实验内容——test驱动设备驱动简介及驱动模块操作系统是通过各种驱动程序来驾驭硬件设备的,它为用户屏蔽了各种各样的设备,驱动硬件是操作系统最基本的功能,并且提供统一的操作方式。设备驱动程序是内核的一部分,硬件驱动程序是操作系统最基本的组成部分,在Linux内核源程序中也占有60%以上。因此,熟悉驱动的编写是很重要的。Linux内核中采用可加载的模块化设计(LKMs,LoadableKernelModules),一般情况下编译的Linux内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中,其他的代码可以编译到内核中,或者编译为内核的模块文件(在需要时动态加载)。内核模块的主要相关命令常见的驱动程序是作为内核模块动态加载的,比如声卡驱动和网卡驱动等,而Linux最基础的驱动,如CPU、PCI总线、TCP/IP协议、APM(高级电源管理)、VFS等驱动程序则直接编译在内核文件中。有时也把内核模块叫做驱动程序,只不过驱动的内容不一定是硬件罢了,比如ext3文件系统的驱动。因此,加载驱动就是加载内核模块。lsmod列出当前系统中加载的模块,其中左边第一列是模块名,第二列是该模块大小,第三列则是使用该模块的对象数目。rmmod是用于将当前模块卸载。insmod和modprobe是用于加载当前模块,但insmod不会自动解决依存关系,即如果要加载的模块引用了当前内核符号表中不存在的符号,则无法加载,也不会去查在其他尚未加载的模块中是否定义了该符号;modprobe可以根据模块间依存关系以及/etc/modules.conf文件中的内容自动加载其他有依赖关系的模块。设备分类(1)Linux系统的设备分为三类:字符设备、块设备和网络设备。字符设备通常指像普通文件或字节流一样,以字节为单位顺序读写的设备,如并口设备、虚拟控制台等。字符设备可以通过设备文件节点访问,它与普通文件之间的区别在于普通文件可以被随机访问(可以前后移动访问指针),而大多数字符设备只能提供顺序访问,因为对它们的访问不会被系统所缓存。但也有例外,例如帧缓存(framebuffer)是一个可以被随机访问的字符设备。块设备通常指一些需要以块为单位随机读写的设备,如IDE硬盘、SCSI硬盘、光驱等。块设备也是通过文件节点来访问,它不仅可以提供随机访问,而且可以容纳文件系统(例如硬盘、闪存等)。Linux可以使用户态程序像访问字符设备一样每次进行任意字节的操作,只是在内核态内部中的管理方式和内核提供的驱动接口上不同。设备分类(2)网络设备通常是指通过网络能够与其他主机进行数据通信的设备,如网卡等。内核和网络设备驱动程序之间的通信调用一套数据包处理函数,它们完全不同于内核和字符以及块设备驱动程序之间的通信(read(),write()等函数)。Linux网络设备不是面向流的设备,因此不会将网络设备的名字(例如eth0)映射到文件系统中去。$ls–l/devcrw-rw----1rootuucp4,6408-3022:58ttyS0/*串口设备,c表示字符设备*/brw-r-----1rootfloppy2,008-3022:58fd0/*软盘设备,b表示块设备*/设备号设备号是一个数字,它是设备的标志。就如前面所述,一个设备文件(也就是设备节点)可以通过mknod命令来创建,其中指定了主设备号和次设备号。主设备号表明设备的类型(例如串口设备、SCSI硬盘),与一个确定的驱动程序对应;次设备号通常是用于标明不同的属性,例如不同的使用方法,不同的位置,不同的操作等,它标志着某个具体的物理设备。高字节为主设备号,底字节为次设备号。例如,在系统中的块设备IDE硬盘的主设备号是3,而多个IDE硬盘及其各个分区分别赋予次设备号1、2、3……$ls–l/devcrw-rw----1rootuucp4,6408-3022:58ttyS0/*主设备号4,此设备号64*/驱动层次结构用户程序的进程(设备)文件系统设备服务子程序中断处理程序设备驱动程序物理设备控制器物理设备输入/输出请求输入/输出响应入入设备驱动程序的特点(1)(1)内核代码:设备驱动程序是内核的一部分,如果驱动程序出错,则可能导致系统崩溃。(2)内核接口:设备驱动程序必须为内核或者其子系统提供一个标准接口。比如,一个终端驱动程序必须为内核提供一个文件I/O接口;一个SCSI设备驱动程序应该为SCSI子系统提供一个SCSI设备接口,同时SCSI子系统也必须为内核提供文件的I/O接口及缓冲区。(3)内核机制和服务:设备驱动程序使用一些标准的内核服务,如内存分配等。设备驱动程序的特点(2)(4)可装载:大多数的Linux操作系统设备驱动程序都可以在需要时装载进内核,在不需要时从内核中卸载。(5)可设置:Linux操作系统设备驱动程序可以集成为内核的一部分,并可以根据需要把其中的某一部分集成到内核中,这只需要在系统编译时进行相应的设置即可。(6)动态性:在系统启动且各个设备驱动程序初始化后,驱动程序将维护其控制的设备。如果该设备驱动程序控制的设备不存在也不影响系统的运行,那么此时的设备驱动程序只是多占用了一点系统内存罢了。()cleanup_module()内核注册设备卸载设备设备功能insmodrmmod模块在调用insmod命令时被加载,此时的入口点是init_module()函数,通常在该函数中完成设备的注册。同样,模块在调用rmmod命令时被卸载,此时的入口点是cleanup_module()函数,在该函数中完成设备的卸载。在设备完成注册加载之后,用户的应用程序就可以对该设备进行一定的操作,如open()、read()、write()等,而驱动程序就是用于实现这些操作,在用户应用程序调用相应入口函数时执行相关的操作。{loff_t(*llseek)(structfile*,loff_t,int);ssize_t(*read)(structfile*filp,char*buff,size_tcount,loff_t*offp);ssize_t(*write)(structfile*filp,constchar*buff,size_tcount,loff_t*offp);int(*readdir)(structfile*,void*,filldir_t);unsignedint(*poll)(structfile*,structpoll_table_struct*);int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);int(*mmap)(structfile*,structvm_area_struct*);int(*open)(structinode*,structfile*);int(*flush)(structfile*);int(*release)(structinode*,structfile*);int(*fsync)(structfile*,structdentry*);int(*fasync)(int,structfile*,int);int(*check_media_change)(kdev_tdev);int(*revalidate)(kdev_tdev);int(*lock)(structfile*,int,structfile_lock*);};{mode_tf_mode;/*标识文件是否可读或可写,FMODE_READ或FMODE_WRITE*/dev_tf_rdev;/*用于/dev/tty*/off_tf_pos;/*当前文件位移*/unsignedshortf_flags;/*文件标志,如O_RDONLY、O_NONBLOCK和O_SYNC*/unsignedshortf_count;/*打开的文件数目*/unsignedshortf_reada;structinode*f_inode;/*指向inode的结构指针*/structfile_operations*f_op;/*文件索引指针*/};早期版本的字符设备注册(1)早期版本的设备注册使用函数register_chrdev(),调用该函数后就可以向系统申请主设备号,如果register_chrdev()操作成功,设备名就会出现在/proc/devices文件里。在关闭设备时,通常需要解除原先的设备注册,此时可使用函数unregister_chrdev(),此后该设备就会从/proc/devices里消失。其中主设备号和次设备号不能大于255。早期版本的字符设备注册(2)设备号相关函数(1)在linux2.6的版本中,用dev_t类型来描述设备号(dev_t是32位数值类型,其中高12位表示主设备号,低20位表示次设备号)。用两个宏MAJOR和MINOR分别获得dev_t设备号的主设备号和次设备号,而且用MKDEV宏来实现逆过程,即组合主设备号和次设备号而获得dev_t类型设备号。分配设备号有静态和动态的两种方法。静态分配(register_chrdev_region()函数)是指在事先知道设备主设备号的情况下,通过参数函数指定第一个设备号(它的次设备号通常为0)而向系统申请分配一定数目的设备号。动态分配(alloc_chrdev_region())是指通过参数仅设置第一个次设备号(通常为0,事先不会知道主设备号)和要分配的设备数目而系统动态分配所需的设备号。通过unregister_chrdev_region()函数释放已分配的(无论是静态的还是动态的)设备号。设备号相关函数(2)字符设备注册(1)在Linux内核中使用structcdev结构来描述字符设备,我们在驱动程序中必须将已分配到的设备号以及设备操作接口(即为structfile_operations结构)赋予structcdev结构变量。首先使用cdev_alloc()函数向系统申请分配structcdev结构,再用cdev_init()函数初始化已分配到的结构并与file_operations结构关联起来。最后调用cdev_add()函数将设备号与structcdev结构进行关联并向内核正式报告新设备的注册,这样新设备可以被用起来了!。如果要从系统中删除一个设备,则要调用cdev_del()函数。w
本文标题:第11章 嵌入式Linux设备驱动开发
链接地址:https://www.777doc.com/doc-1314147 .html