Linux驱动程序设计.ppt_第1页
Linux驱动程序设计.ppt_第2页
Linux驱动程序设计.ppt_第3页
Linux驱动程序设计.ppt_第4页
Linux驱动程序设计.ppt_第5页
已阅读5页,还剩43页未读 继续免费阅读

下载本文档

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

文档简介

2019年7月21日 1,教学内容,第一章 嵌入式系统概述 第二章 学习板硬件及开发环境的建立 第三章 构建嵌入式Linux系统 第四章 嵌入式Linux设备驱动 第五章 嵌入式Linux串口和网络编程 第六章 嵌入式Linux图形编程,2019年7月21日 2,第四章 嵌入式Linux驱动程序,4.1 嵌入式Linux设备驱动简介 4.2 设备的分类及特点 4.3 构造和运行驱动程序模块,2019年7月21日 3,课程目标,掌握嵌入式Linux设备驱动程序的基本原理、架构和设计方法 字符设备驱动 块设备驱动 网络设备驱动 掌握Linux设备驱动开发中常用的机制和内核资源 中断顶/底半部处理 内核定时器和延时操作 并发控制在内核中的应用 内存管理和分配 阻塞型I/O和非阻塞型I/O,2019年7月21日 4,本章目标,了解Linux设备驱动程序的基础知识 掌握Linux驱动模块的构造和装载方法,2019年7月21日 5,本章结构,设备驱动程序简介,Linux驱动程序概述,设备驱动的Hello World模块,内核驱动模块和应用程序对比,编译和装载驱动模块,构造和运行模块,设备的分类和特点,字符设备,网络设备,块设备,驱动程序的作用,应用程序,驱动程序,操作系统,硬件设备,write,ioctl,read,ioctl,2019年7月21日 7,为什么要学嵌入式Linux驱动程序开发?,高需求 内核代码的大部分 新芯片、新设备 高门槛 需要具有硬件知识 需要了解内核基础知识 需要了解内核中的并发控制和同步 复杂的软件结构框架 高回报,2019年7月21日 8,4-1设备驱动程序简介,驱动程序的特点 操控硬件,是应用程序和硬件设备之间的一个接口 隐藏硬件细节,提高应用软件的可移植性 提供安全性 开发模式 内核态驱动 用户态驱动 提供机制,而不是提供策略 机制:驱动程序能实现什么功能 策略:用户如何使用这些功能,内核态和用户态,大多数OS(包括Linux)把内核和运行在其上的应用程序分为两个层次管理,即用户态和内核态 内核态有较高的权限,可以控制处理器内存的映射和分配方式等等对应于ARM的svc模式 用户态:只能运行系统上的应用程序对应于ARM的usr模式 内核态与用户态切换:可通过软件中断实现,内核态和用户态,驱动程序作为系统内核的一部分,其工作在内核态,而应用程序工作在用户态,即不能直接通过指针,把用户空间的数据地址传递给内核(MMU映射地址不一样)。 需要经过转换,把用户态“看到的空间”转换成内核态可访问的地址。Linux系统提供了一系列方便的函数实现这种转换,如:_get_user、_put_user、 _copy_from_user、_copy_to_user,2019年7月21日 11,4-2设备的分类和特点,设备分类 字符设备(char device) 块设备(block device) 网络设备(network device),2019年7月21日 12,4-2设备的分类和特点,字符设备特点 像字节流一样来存取的设备( 如同文件 ) 通过/dev下的文件系统结点来访问。 通常至少需要实现 open, close, read, 和 write 等系统调用 只能顺序访问数据通道,不能前后移动访问指针。 特例:比如framebuffer设备就是这样的设备,应用程序可以使用mmap或lseek访问图像的各个区域,Linux内核结构,Linux内核结构,应用程序、库、内核、驱动程序的关系,open read write ioctl 调用其他库函数,称为系统调用,执行swi指令进入内核,系统调用的异常处理,其他功能,物理设备控制器,物理设备,应用程序,库,其他库函数的实现,内核,open read write ioctl ,驱动程序,4层软件关系说明,(1)应用程序使用库函数提供的open函数打开设备文件 (2)库根据open函数传入的参数执行“swi”指令,引起CPU异常,进入内核 (3)内核的异常处理函数根据这些参数找到相应的驱动程序,返回一个文件描述符给库,进而返回给应用程序 (4)应用程序得到文件描述符后,使用库提供的write或ioclt函数发出控制命令 (5)库根据write或ioclt函数传入的参数执行“swi”指令,这条指令会引起CPU异常,再次进入内核 (6)内核的异常处理函数根据参数调用驱动程序的相关函数,控制硬件,2019年7月21日 17,4-2设备的分类和特点,块设备特点 块设备通过位于 /dev 目录的文件系统结点来存取 块设备和字符设备的区别仅仅在于内核内部管理数据的方式 块设备有专门的接口,块设备的接口必须支持挂装(mount)文件系统。 应用程序一般通过文件系统来访问块设备上的内容,2019年7月21日 18,4-2设备的分类和特点,网络设备特点 通过单独的网络接口来访问 任何一个网络事务都通过一个网络接口访问,即一个能够和其他主机交换数据的设备。 网卡 软件设备:环回接口(loopback) 内核调用一套和数据包传输相关的函数,2019年7月21日 19,4-3 构造和运行模块,驱动程序加入内核的方法 把所有需要的功能都编译到内核中 生成的内核镜像(Image)文件会很大 如果我们要在现有的内核中新增或删除功能,将不得不重新编译和装载内核。,?,2019年7月21日 20,4-3 驱动程序加入内核的方法,Linux提供了机制被称为模块(Module)的机制 提供了对许多模块支持, 包括但不限于, 设备驱动 每个模块由目标代码组成( 没有连接成一个完整可执行程序 ) insmod 将模块动态加载到正在运行内核 rmmod 程序移除模块,2019年7月21日 21,4.3.1 设备驱动的Hello World模块,#include #include MODULE_LICENSE(“Dual BSD/GPL“); static int _init hello_init(void) printk(KERN_ALERT “Hello worldn“); return 0; static void _exit hello_exit(void) printk(KERN_ALERT “ Hello world exitn“); module_init(hello_init); module_exit(hello_exit);,自由许可证,模块卸载宏,用法类似于printf,但它有优先级(比如KERN_ALERT),模块初始化宏,宏,告诉内核这两个函数只会在加载和卸载模块时使用,打印调试printk,指示日志级别的宏 KERN_EMERG 用于紧急事件消息,一般是系统崩溃前提示 KERN_ALERT 用于需要立即采取动作 KERN_CRIT 临界状态,通常涉及到严重的硬件或软件操作失败 KERN_ERR 用于报告错误状态,设备驱动程序会经常使用其来报告来自硬件的问题 KERN_WARNING 用于对可能出现的问题进行警告 KERN_NOTICE 用于有必要进行提示的正常情况 KERN_INFO 提示性信息 KERN_DEBUG 用于调试信息,打印调试printk,每个宏表示一个中的整数(07),数值越小,优先级越高 未指定优先级在内核2.6版本中就是KENR_WARNING,简单的内核模块编译(内核2.4),编译内核模块 #gcc O -DMODULE -D_KERNEL_ -c hello.c -I /usr/src/linux/include (x86 or ARM ? ) 生成hello.o 加载内核模块:insmod #insmod hello.o #dmesg Hello,world 查看内核中使用的模块 #lsmod | grep hello 卸载内核模块:rmmod #rmmod hello.o #dmesg Goodbye world,简单的内核模块编译(内核2.6),obj-m := led_drv.o KDIR:=/home/at9200/kernel/linux-2.6.38/ PWD :=$(shell pwd) all: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean,Makefile文件内容,简单的内核模块编译(内核2.6),obj-m := led_drv.o表示编译后生成led_drv.ko模块 $(KDIR) 指定了target内核源码的路径,“M=”表示这是个外部模块,M=$(PWD) 指定了该模块文件所在的路径。,简单的内核模块编译(内核2.6),如果是多个源文件编译出一个模块,假设模块名是test.ko,那么源文件名不能有test.c obj-m := test.o test-objs := file1.o file2.o file3.o KDIR := /home/at9200/kernel/linux-2.6.38/ PWD := $(shell pwd) all: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean,2019年7月21日 28,4.3.1 设备驱动的Hello World模块,Linux内核模块的程序结构 module_init()-模块加载函数(必须) 通过insmod或modprobe命令加载内核模块时,模块的加载函数会自动被内核执行,完成模块的相关初始化工作 module_exit()-模块卸载函数(必须) 当通过rmmod命令卸载某模块时,模块的卸载函数会自动被内核执行,完成与模块装载函数相反的功能,2019年7月21日 29,4-3-1 设备驱动的Hello World模块,MODULE_LICENSE()-模块许可证声明(必须) 模块许可证(LICENSE)声明描述内核模块的许可权限 如果不声明LICENSE,模块被加载时,将收到内核被污染(kernel tainted)的警告 包括:“GPL”、 “GPL v2”、 “GPL and additional rights”、 “Dual BSD/GPL”、“Dual MPI/GPL”、 “Proprietary”,2019年7月21日 30,4-3-1 设备驱动的Hello World模块,模块加载函数 static int _init initialization_function(void) /* 初始化代码 */ module_init(initialization_function);,应当声明成静态的(static), 因为它们不会在特定文件之外可见,表明该函数只是在初始化时使用。模块加载器在模块加载后会丢掉这个初始化函数, 这样可将该函数占用的内存释放出来,以作他用。 原型:#define _init _attribute_ (_section_(“.init.text”),定义会在模块目标代码中增加一个特殊的段, 用于说明内核模块初始化函数所在的位置。没有这个定义, 初始化函数不会被调用。,2019年7月21日 31,4-3-1 设备驱动的Hello World模块,模块卸载函数 static void _exit cleanup_function(void) /* 释放资源 */ module_exit(cleanup_function);,在模块被移除前注销接口并释放所有所占用的系统资源,标识这个代码是只用于模块卸载( 通过使编译器把它放在特殊的 ELF 段) 原型:#define _exit _attribute_ (_section_(“.exit.text”),2019年7月21日 32,4.3.1 设备驱动的Hello World模块,#include #include MODULE_LICENSE(“Dual BSD/GPL“); static int _init hello_init(void) printk(KERN_ALERT “Hello worldn“); return 0; static void _exit hello_exit(void) printk(KERN_ALERT “ Hello world exitn“); module_init(hello_init); module_exit(hello_exit);,2019年7月21日 33,4-3-1 设备驱动的Hello World模块,module_param()-模块参数(可选) 模块参数是模块被加载的时候可以被传递给它的值,它本身对应模块内部的全局变量。,2019年7月21日 34,4-3-1 设备驱动的Hello World模块,EXPORT_SYMBOL()-模块导出符号(可选) 内核模块可以导出符号(symbol,对应于函数或变量)到内核 其他模块可以使用本模块中的变量或函数 其他一些声明MODULE_XXXXX()-模块声明(可选),2019年7月21日 35,4.3.1 设备驱动的Hello World模块,#include #include MODULE_LICENSE(“Dual BSD/GPL“); static char *whom = “world“; static int howmany = 1; static int hello_init(void) int i; for(i=0;ihowmany;i+) printk(KERN_ALERT “Hello %sn“,whom); return 0; static void hello_exit(void) printk(KERN_ALERT “ Hello world exitn“); module_init(hello_init); module_exit(hello_exit); module_param(howmany, int, S_IRUGO); module_param(whom, charp, S_IRUGO);,module_param(参数名, 参数类型,参数读/写权限),2019年7月21日 36,4-3-1 设备驱动的Hello World模块,module_param(参数名,参数类型,参数读/写权限) 内核支持的模块参数类型包括: byte、short、ushort、int、uint、long、ulong、charp(字符指针)、bool以u开头的为无符号值。,static char *whom = “world“; static int howmany = 1; module_param(howmany, int, S_IRUGO); module_param(whom, charp, S_IRUGO);,2019年7月21日 37,4-3-1 设备驱动的Hello World模块,模块也可以拥有参数数组 形式为“module_param_array(数组名,数组类型,数组长,参数读/写权限)”。 运行insmod或modprobe命令时,应使用逗号分隔输入的数组元素 装载模块时改变参数: 可通过insmod或modprobe insmod hello_ext.ko howmany=5 whom=“Students“ modprobe也可以从它的配置文件(/etc/modprobe.conf)读取参数的值,2019年7月21日 38,4.3.1 设备驱动的Hello World模块,模块导出符号 EXPORT_SYMBOL(name); EXPORT_SYMBOL_GPL(name);,_GPL 版本的宏定义的导出符号只能对 GPL 许可的模块可用,注: 符号必须在模块文件的全局部分导出, 不能在函数中导出,2019年7月21日 39,4.3.1 设备驱动的Hello World模块,/hello.c #include #include MODULE_LICENSE(“GPL“); MODULE_AUTHOR(“David Xie“); MODULE_DESCRIPTION(“Hello World Module“); MODULE_ALIAS(“a simplest module“); extern int add_integar(int a,int b); extern int sub_integar(int a,int b); static int _init hello_init() int res_add = add_integar(1,2); printk(“res_add=%dn”,res_add); return 0; static void _exit hello_exit() int res_sub = sub_integar(2,1); printk(“res_sub=%dn”,res_sub); module_init(hello_init); module_exit(hello_exit);,/ calculate.c #include #include MODULE_LICENSE(“GPL“); int add_integar(int a,int b) return a+b; int sub_integar(int a,int b) return a-b; static int _init sym_init() return 0; static void _exit sym_exit() ; module_init(sym_init); module_exit(sym_exit); EXPORT_SYMBOL(add_integar); EXPORT_SYMBOL(sub_integar);,2019年7月21日 40,4-3-1 设备驱动的Hello World模块,模块声明与描述 MODULE_AUTHOR(author); -声明模块的作者 MODULE_DESCRIPTION(description); -声明模块的描述 MODULE_VERSION(version_string); -声明模块的版本 MODULE_DEVICE_TABLE(table_info); -声明模块的设备表 MODULE_ALIAS(alternate_name); -声明模块的别名,2019年7月21日 41,4-3-1 设备驱动的Hello World模块,模块的使用计数 Linux2.4内核 MOD_INC_USE_COUNT(加一计数) MOD_DEC_USE_COUNT(减一计数) Linux2.6内核中 int try_module_get(struct module *module); void module_put(struct module *module);,在Linux2.6内核下,对于设备驱动工程师而言,很少需要 亲自调用try_module_get()和module_put(),因为模块的 计数管理由内核里更底层的代码(如总线驱动或是此类设 备共用的核心模块)来实现,从而简化了设备驱动的开发,2019年7月21日 42,4-3-2 内核驱动模块与应用程序对比,应用程序是一个进程 编程从主函数main()开始 主函数main返回即是进程结束 驱动程序是一系列内核函数 驱动程序向内核添加了一些函数,是内核的一部分 Open() Release() Read() Write() 这些函数由内核在适当的时候来调用 这些函数可以用来完成硬件访问等操作,2019年7月21日 43,4-3-3 编译和装载驱动模块,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 *); 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 (*release) (struct inode *, struct file *); int (*fsync) (struct file *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struc

温馨提示

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

评论

0/150

提交评论