Linux设备驱动程序开发基础课件.ppt
Linux设备驱动程序开发基础,主讲人:TonyShen,Linux设备驱动程序开发基础主讲人:TonyShen,课程目标,Linux设备驱动程序开发简介Linux设备驱动程序结构Linux设备驱动程序加载方式实验:编写一个字符设备驱动程序(LED或蜂鸣器)分别用静态编译,模块动态加载方法实现加入内核,课程目标,设备驱动程序,应用程序,驱动程序,设备,write,read,ioctl,ioctl,设备驱动程序应驱设writereadioctlioctl,设备驱动程序特点,核心代码:设备驱动程序是核心的一部分,像核心中其他的代码一样,出错将导致系统的严重损伤。一个编写不当的设备驱动程序甚至能够使系统崩溃导致文件系统的破坏和数据的丢失;标准接口:设备驱动程序必须为Linux核心或者其从属的子系统提供一个标准的接口;核心机制:设备驱动程序可以使用标准的核心服务比如内存分配、中断发送和等待对列等;动态可加载:多数的Linux设备驱动程序可以在核心模块发出加载请求时进行加载,同时在不使用设备时进行卸载,这样核心可以有效地利用系统的资源可配置:Linux设备驱动属于核心的一部分,用户可以根据自己的需要进行配置来选择适合自己的驱动,设备驱动程序特点核心代码:设备驱动程序是核心的一部分,像核,用户态与内核态,Linux运转在两种模式下,一种是用户态,另一种是内核态。内核态有较高的权限,可以控制处理器内存的映射和分配方式,访问外设空间和处理器状态寄存器,控制中断等。用户态只能运行系统上的应用程序。驱动程序作为系统内核的一部分,工作在内核态。通过get_user put_user copy_from_user copy_to_user等函数实现应用程序和驱动程序之间传送数据(指针)。,用户态与内核态Linux运转在两种模式下,一种是用户态,另一,Linux设备的分类,字符设备以字节为单位逐个进行I/O操作字符设备中的缓存是可有可无不支持随机访问如串口设备块设备块设备的存取是通过buffer、cache来进行可以进行随机访问例如IDE硬盘设备可以支持可安装文件系统网络设备通过BSD套接口访问,Linux设备的分类字符设备,设备文件,Linux抽象了对硬件的处理,所有的硬件设备都可以作为普通文件一样来看待可以使用和操作文件相同的、标准的系统调用接口来完成打开、关闭、读写和I/O控制操作对用户来说,设备文件与普通文件并无区别字符设备和块设备是通过文件节点访问的。在Linux的文件系统中,可以找到(或者使用mknod创建)设备对应的文件名,称这种文件为设备文件。,设备文件,设备文件,命令 ls l /dev 可列出系统的设备文件,设备文件命令 ls l /dev 可列出系统的设备文件,主设备号和次设备号,主设备号:标识该设备的种类,也标识了该设备所使用的驱动程序主设备号的范围只能是1-255Linux内核支持动态分配主设备号次设备号:标识使用同一设备驱动程序的不同硬件设备同一个驱动程序可以管理多个设备,它们依靠次设备号来区别。次设备号只在驱动程序内部使用,系统内核直接把次设备号传递给驱动程序,由驱动程序去管理。,主设备号和次设备号主设备号:标识该设备的种类,也标识了该设备,设备文件系统,Linux内核自己管理设备文件,完成设备文件节点的创建、删除。(devfs,device file system)在linux2.4/2.6内核中,设备文件系统可在配置内核时定制。linux2.4内核配置层次 File systems -/dev file system supportlinux2.6内核配置层次 File systems -Pseudo file system -/dev file system support系统驱动程序通过CONFIG_DEVFS_FS宏定义判断系统是否有对设备文件系统的支持。,设备文件系统Linux内核自己管理设备文件,完成设备文件节点,配置设备文件系统,配置设备文件系统,Linux设备驱动程序结构,Linux设备驱动程序的代码结构大致可以分为如下几个部分:驱动程序的注册与注销、设备的打开与释放、设备的读写操作、设备的控制操作、设备的中断和轮询处理。一个最简单字符驱动程序,由下面5个函数和1个结构体就可组成。,static int my_open(struct inode * inode, struct file * filp) 设备打开时的操作 ,static int my_release(struct inode * inode, struct file * filp) 设备关闭时的操作 ,static int my_write(struct file *file, const char * buffer, size_t count, loff_t * ppos) 设备写入时的操作 ,Linux设备驱动程序结构Linux设备驱动程序的代码结构大,驱动程序结构,static struct file_operations my_fops = 对文件操作结构体成员定义初始值,static int _init my_init(void)初始化硬件,注册设备,创建设备节点 ,static void _exit my_exit(void)删除设备节点,注销设备 ,驱动程序结构static struct file_opera,头文件与系统定义,#include #include #include #include #include #include #include #include 9200.h“#ifndef _KERNEL_#define _KERNEL_#endif#ifndef MODULE#define MODULE#endif#define LED AT91C_PIO_PB11static AT91PS_SYS AT91_SYS1 = (AT91PS_SYS) AT91C_VA_BASE_SYS;#define DEVICE_NAME = “my_led”static int Led_Major = 0;#ifdef CONFIG_DEVFS_FSstatic devfs_handle_t Devfs_Led_Dir, Devfs_Led_Raw;#endif,头文件与系统定义#include linux/module,打开和关闭操作,my_open和my_release函数会在设备打开和关闭时被调用,其工作很简单,仅仅执行两个宏:”MOD_INC_USE_COUNT”,”MOD_DEC_USE_COUNT”。这两个宏负责记录(增加或者减少)设备模块被使用的情况,防止当有应用程序使用驱动程序时,此模块被意外地卸载。,static int my_open(struct inode * inode, struct file * filp)MOD_INC_USE_COUNT;return 0; ,static int my_release(struct inode * inode, struct file * filp MOD_DEC_USE_COUNT return 0; ,打开和关闭操作my_open和my_release函数会在设,写入操作,static int my_write(struct file *file, const char * buffer, size_t count, loff_t * ppos) char led_status = 0; copy_from_user(,写入操作static int my_write(struct,文件操作结构体初始化,static struct file_operations my_fops = open: my_open,write: my_write,release:my_release,;,文件操作结构体初始化static struct file_o,struct file_operationsstruct 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*, intdatasync);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);,文件操作结构体,struct file_operations文件操作结,设备初始化,static int _init my_init(void)/硬件初始化 AT91F_PIOB_Enable(LED);AT91F_PIOB_OutputEnable(LED);/字符设备注册Led_Major = register_chrdev(0, DEVICE_NAME, #endif,设备初始化static int _init my_init,设备注销,static void _exit my_exit(void)/删除设备文件#ifdef CONFIG_DEVFS_FSdevfs_unregister(Devfs_Led_Raw);devfs_unregister(Devfs_Led_Dir);#endif/注销设备unregister_chrdev(Led_Major, DEVICE_NAME);,module_init(my_init); /向Linux系统记录设备初始化的函数名称module_exit(my_exit); /向Linux系统记录设备退出的函数名称,设备注销static void _exit my_exit,驱动程序编译,Makefile文件内容,OBJ=io_led.oSOURCE=io_led.cCC=arm-linux-gccCOMP=-Wall -O2 -DMODULE -D_KERNEL_ -I /home/armlinux/linux-2.4.19-rmk7/include c$(OBJ):$(SOURCE)$(CC) $(COMP) $(SOURCE)clean:rm $(OBJ),运行make 命令,编译通过后当前目录下就生成名为io_led.o的驱动程序,驱动程序编译Makefile文件内容OBJ=io_led.o,驱动程序加载,Linux内核有2种加载驱动程序的方法:Linux系统启动时,通过代码自身加载模块.这种方式称为静态编译入内核, 驱动程序开发完毕后一般使用这种方式.Linux系统启动后,通过insmod等命令加载模块.这种方式称为动态加载,驱动程序开发调试过程中一般使用这种方式.,驱动程序加载,模块动态加载,驱动程序模块插入内核查看是否载入,如果载入成功会显示你的设备名称my_led从内核移除设备,#insmod io_led.o,#cat /proc/devices,#rmmod io_led,模块动态加载驱动程序模块插入内核#insmod io_led,静态编译进内核,1.程序放入内核目录linux-2.49-rmk7driverschar2.修改linux-2.49-rmk7driverscharMakefile文件添加 obj-$(CONFIG_9200LED) += io_led.o3.修改linux-2.49-rmk7driverscharConfig.in文件添加 tristate Support 9200LED device CONFIG_9200LED4.配置内核,编译内核.在配置菜单的字符设备中会增加 Support 9200LED device 项目,静态编译进内核1.程序放入内核目录,内核中配置模块,配置内核加载驱动程序,内核中配置模块配置内核加载驱动程序,驱动测试应用程序,int main(void)int fd;char led_on = 0 x01;fd = open(/dev/led/0, O_RDWR); /打开led设备 if(fd=-1) printf(can not open devicen); exit(1);write(fd, ,驱动测试应用程序int main(void),问题回顾,设备驱动程序功能与特点?什么是用户态,内核态.主设备号,次设备号,设备文件系统?设备驱动程序基本结构?设备驱动程序加载方式?,问题回顾设备驱动程序功能与特点?,谢 谢!,谢 谢!,