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