您好,欢迎访问三七文档
Linux字符设备驱动程序Linux驱动程序的分类字符设备驱动:用于驱动能够像字节流(文件)一样被访问的设备。应用程序通常可以利用open、close、read、write等系统调用访问字符设备驱动。块设备驱动:块设备和字符设备只在系统内核内部的管理上有所区别。应用程序对于字符设备的每一个I/O操作都会被内核直接传递给对应的驱动程序;而应用程序对于块设备的操作要经过虚拟文件系统(VFS)和缓冲区管理系统间接地传递给驱动程序处理。网络设备驱动:应用程序必须利用套接字(socket)接口访问网络设备。网络设备驱动程序字符设备驱动程序基本结构字符设备开发的基本步骤确定主设备号和次设备号实现字符驱动程序实现file_operations结构体构造字符设备结构体cdev在模块加载函数中注册字符设备在模块卸载函数中注销字符设备创建设备文件节点设备文件与设备号为了体现“一切都是文件”的设计思想,linux将每个已安装的设备都表示为一个设备文件。设备文件通常位于/dev子目录。对于字符设备,应用程序可以利用open、close、read、write等系统调用访问其设备文件,这些I/O操作都被直接传递给该设备文件所对应的设备。每个设备文件中都存储了该设备的“主设备号”和“次设备号”。一般由同一个内核模块管理的多个设备占用同一个主设备号,具体设备用次设备号标识。用mknodfilenamecmajorminor命令创建设备文件用rmfilename命令删除设备文件。注意删除设备文件并不会影响驱动模块。设备号的内部表达设备编号的内部表达dev_t类型(32位):用来保存设备编号(包括主设备号(12位)和次设备号(20位))从dev_t获得主设备号和次设备号:MAJOR(dev_t);MINOR(dev_t);将主设备号和次设备号转换成dev_t类型:MKDEV(intmajor,intminor);主设备号与次设备号分配主设备号手工分配主设备号:找一个内核没有使用的主设备号来使用。#includelinux/fs.hintregister_chrdev_region(dev_tfirst,unsignedintcount,char*name);要分配的设备编号范围的起始值,次设备号经常为0所请求的连续设备编号的个数和该编号范围关联的设备名称主设备号与次设备号动态分配主设备号:#includelinux/fs.hintalloc_chrdev_resion(dev_t*dev,unsignedintfirstminor,unsignedintcount,char*name);输出的设备号要使用的被请求的第一个次设备号主设备号与次设备号释放设备号voidunregister_chrdev_region(dev_tfirst,unsignedintcount);通常在模块的清除函数中调用。记录字符设备的结构体cdev实现字符驱动程序cdev结构体structcdev{structkobjectkobj;/*内嵌的kobject对象*/structmodule*owner;/*所属模块*/structfile_operations*ops;/*文件操作结构体*/structlist_headlist;dev_tdev;/*设备号*/unsignedintcount;};cdev的kobj、list、count字段不用我们关系和维护(内核代劳),我们只需将其ops字段指向为我们自己的fileoperations结构。对cdev结构体的操作操作cdev的函数voidcdev_init(structcdev*,structfile_operations*);structcdev*cdev_alloc(void);intcdev_add(structcdev*,dev_t,unsigned);voidcdev_del(structcdev*);用于初始化cdev的成员,并建立cdev和file_operations之间的连接分别向系统删除一个cdev,完成字符设备的注销,通常在模块的卸载函数中调用分别向系统添加一个cdev,完成字符设备的注册,通常在模块加载函数中调用函数用于动态申请一个cdev内存file_operations结构体file_operations结构体字符驱动和内核的接口:在include/linux/fs.h定义字符驱动只要实现一个file_operations结构体并注册到内核中,内核就有了操作此设备的能力。structfile_operations{structmodule*owner;loff_t(*llseek)(structfile*,loff_t,int);ssize_t(*read)(structfile*,char__user*,size_t,loff_t*);ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*);unsignedint(*poll)(structfile*,structpoll_table_struct*);int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);int(*open)(structinode*,structfile*);int(*flush)(structfile*,fl_owner_tid);int(*release)(structinode*,structfile*);//………………………};file_operations结构体file_operations的主要成员:structmodule*owner:指向模块自身open:打开设备release:关闭设备read:从设备上读数据write:向设备上写数据ioctl:I/O控制函数llseek:定位读写指针mmap:映射设备空间到进程的地址空间fileoperations的初始化structfile_operationsmy_fops={.owner=THIS_MODULE,.llseek=my_llseek,.read=my_read,.write=my_write,.ioctl=my_ioctl,.open=my_open,.release=my_release,};file结构体file结构代表一个打开的文件,它由内核在应用程序open时创建,并将该文件所对应的fileoperations记录在file结构中。在应用程序调用close函数,内核会释放该数据结构。structfile{structfile_operations*f_op;unsignedintf_flags;fmode_tf_mode;loff_tf_pos;void*private_data;};file结构体file结构体file结构:file_operations结构相关的一个结构体。描述一个正在打开的设备文件。成员:loff_tf_pos:当前读/写位置unsignedintf_flags标识文件打开时,是否可读或可写O_RDONLYO_NONBLOCKO_SYNCstructfile_operations*f_op文件相关的操作,指向所实现的structfile_operationsvoid*private_data:私有数据指针。驱动程序可以将这个字段用于任何目的或者忽略这个字段。inode结构体内核用inode结构在内部表示文件,用于存储文件访问权限、属主、组、大小、生产时间等VFS关心的信息。其字段中我们只关心i_rdev(设备号),和i_cdev(和该文件所对应的cdev结构)我们在创建设备文件时,内核会自动创建一个对应的inode结构体,并将其i_cdev字段指向对应的字符设备结构体cdev(事先已经在内核中注册过)。Inode与file的区别:file表示打开的文件描述符,多个file结构,可以指向单个inode结构。structinode{dev_ti_rdev;structcdev*i_cdev;//………………………};Inode结构体Inode结构中的两个主要字段:dev_ti_rdev;对表示设备文件的inode结构,该字段包含了真正的设备编号。structcdev*i_cdev;structcdev是表示字符设备的内核的内部结构。当inode指向一个字符设备文件时,该字段包含了指向structcdev结构的指针structlist_headidevices;从一个inode中获得主设备号和次设备号:unsignedintiminor(structinode*inode);unsignedintimajor(structinode*inode);字符设备驱动程序基本结构注册设备,在模块或驱动初始化时调用Linux-2.4及之前Linux-2.6intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops)如何操作字符设备的接口voidcdev_init(structcdev*,structfile_operations*);intcdev_add(structcdev*,dev_t,unsigned);字符设备驱动程序基本结构注销设备:在模块卸载时调用Linux-2.4及之前Linux-2.6intunregister_chrdev(unsignedintmajor,constchar*name);voidcdev_del(structcdev*);字符设备驱动程序基本结构//设备驱动模块加载函数staticint__initxxx_init(void){...cdev_init(&xxx_dev.cdev,&xxx_fops);//初始化cdevxxx_dev.cdev.owner=THIS_MODULE;//获取字符设备号if(xxx_major){register_chrdev_region(xxx_dev_no,1,DEV_NAME);}else{alloc_chrdev_region(&xxx_dev_no,0,1,DEV_NAME);}ret=cdev_add(&xxx_dev.cdev,xxx_dev_no,1);//注册设备...}字符设备驱动程序基本结构/*设备驱动模块卸载函数*/staticvoid__exitxxx_exit(void){unregister_chrdev_region(xxx_dev_no,1);//释放占用的设备号cdev_del(&xxx_dev.cdev);//注销设备...}字符设备驱动程序基本结构打开模块使用计数加1识别次设备号硬件操作:检查设备相关错误(诸如设备未就绪或类似的硬件问题);如果设备是首次打开,则对其初始化;如果有中断操作,申请中断处理程序;intopen(structinode*inode,structfile*filp);字符设备驱动程序基本结构关闭模块使用计数减1释放由open分配的,保存在filpprivate_data里的所有内容。硬件操作:如果申请了中断,则释放中断处理程序。在最后一次关闭操作时关闭设备。intrelease(structinode*inode,structfile*filp);字符设备驱动程序基本结构read/writessize_tread(structfile*filp,char__user*buff,size_tcount,loff_t*offp);ssize_twrite(structfile*filp,constchar__user*buff,size_tcount,loff_t*offp);指向用户空间的缓冲区,这个缓冲区或者保存将写入的数据,或者是一个存放新读入数据的空缓冲区。用户在文件中存取操作的位置字符设备驱动程序基本结构用户空间和内核空
本文标题:字符设备驱动程序
链接地址:https://www.777doc.com/doc-3146673 .html