字符设备驱动框架
Linux中设备分类:按照对设备的访问方式可分为以下三类:1.字符设备(char device)(1)例如:键盘、鼠标、串口、帧缓存等;(2)通过/dev/下的设备节点访问;以字节为单位访问;(3)一般只支持顺序访问;(特例:帧缓存framebuffer)(4)无缓冲。
2.块设备(block device)(1)例如:磁盘、光驱、flash等;(2)以固定大小为单位访问:磁盘以扇区(512B)为单位;flash以页为单位。
(3)支持随机访问;(4)有缓冲(减少磁盘IO,提高效率)。
3.网络设备(network device)(1)无设备文件(节点);(2)应用层通过socket接口访问网络设备(报文发送和接收的媒介)。
设备驱动在内核中的结构:1.VFS虚拟文件系统作用:向应用层提供一致的文件访问接口,正是由于VFS的存在,才可以将设备以文件的方式访问。
2.虚拟文件系统,存在于内存中,不在磁盘上,掉电丢失。
例如:/proc、/sys、/tmp。
设备号:1.作用:唯一地标识一个设备;2.类型:dev_t devno;即32位无符号整型;3.组成:(1)主设备号:用于区分不同类型(按功能划分)的设备;(2)此设备号:用于区分相同类型的不同设备。
注意:相同类型的设备(主设备号相同)可以使用同一个驱动。
4.构建设备号:int major = 250; int minor = 0;(1)dev_t devno = (major << 20) | minor;不建议使用;(2)利用宏来构建:dev_t devno = MKDEV (major, minor);注意:我们可以通过文件$(srctree)/documentation/device.txt来查看内核对设备号的分配情况。
(1)该文本中的有对应设备文件的设备号是已经被申请过的,我们不可以重复使用(申请);(2)从中可以看出,我们在编写驱动程序时可以使用的主设备号范围为240~254,为了方便记忆,通常使用250作为主设备号。
字符设备驱动框架:驱动:作用,为应用层提供访问设备的接口(对设备发的各种操作)。
一、申请设备号1.构建设备号:dev_t devno = MKDEV (major, minor);2.申请设备号:(1)动态申请:alloc_chrdev_region;(2)静态申请: register_chrdev_region。
(3)静态申请设备号的优缺点:优点:可以提前创建设备文件;缺点:有可能会发成冲突,导致申请失败。
(4)register_chrdev_region函数详解:注意:①最后一个参数是设备名称。
且在/proc/devices下会有关于当前系统已经注册成功的设备信息;②申请设备号应在加载函数中实现;同时,在卸载函数中我们也要调用unregister_chrdev_region来释放设备号。
二、实现操作集合struct file_operations {……}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 *);unsigned int (*poll) (struct fiel *, poll_table *);int (*fasync) (int, struct file *, int);long (*unlock_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 (*flush) (struct file *, fl_owner_t id);int (*lock) (struct file *, int, struct file_lock *);……};除owner成员外,其他成员均是对文件进行相关操作的函数指针。
1.对设备的大多数操作函数都有参数struct file *filp;2.在起始阶段我们可以先定义一个空的操作集合:struct file_operations { .owner = THIS_MODULE };注意:(1)可以看出宏THIS_MODULE代表结构体struct module的起始地址;(2)我们可以通过“.<struct_member_name> = <……>,”语法来给结构体指定成员赋值。
三、注册字符设备:1.通过结构体struct cdev将设备号devno和操作集合file_operations关联起来;2.并将cdev结构体加入到内核维护的cdev链表中。
(计算机系统支持很多字符设备,内核会维护一个cdev链表,这样我们就可以通过设备号找到对应的操作集合了)3.cdev结构体的定义如下:4.我们通过调用内核函数cdev_init和cdev_add来完成字符设备的注册。
注意:注册字符设备应在加载函数中实现;同时,在卸载函数中,我们要调用cdev_del函数来注销字符设备。
且注销字符设备应在释放设备号之前进行。
应用层如何访问设备:1.在Linux中一切皆文件;2.因为VFS(向应用层提供一致的文件访问接口),应用层也可以将设备当做文件来访问;3.应用层要想访问设备,首先要创建设备节点:命令:mknod <device_name> c/b <major> <minor>注意:(1)mknod /dev/hello c 250 0(2)mknod /dev/hello c 250 1 错误;(3)mknod /dev/hello1 c 250 0 正确,即可以有多个设备节点指向同一个设备。
操作集合打开open:1.统计计数,检查错误;(一个设备可以被多个进程打开)2.申请资源:(1)在xxx_open()函数中也可以申请资源;(2)若在open中申请资源,则对应要在release中释放资源;(3)若在加载函数中申请资源,则对应要在卸载函数中释放资源。
3.识别次设备号。
(在一个驱动识别多个设备中有应用)4.在应用层调用open函数打开设备的实现过程:(1)int fd = open (“/dev/hello”, O_RDWR);(2)sys_open();系统调用(3)vfs_open();虚拟文件系统提供的操作接口注意:在vfs_open()函数执行时,会在内存中创建两个结构体:①struct inode { … dev_t i_rdev; … struct cdev *i_cdev;…}记录所打开文件的静态信息;是将磁盘上的inode节点信息读过来的。
②struct file {……struct file *f_op;……}记录所打开文件的动态信息:包括打开方式、当前读写位置、用户信息等。
(4)xxx_open(struct inode *, struct file *);注意:①应用层的文件描述符fd与内核中的struct file结构体是一一对应的,即每打开一个设备文件,就会在内存中创建一个struct file类型的结构体变量。
每个进程都维护有一个文件描述符表:fd与structfile的对应关系。
②与xxx_open()函数相对应的是xxx_release()函数。
(1)file结构体:(文件的静态属性)struct file{……const struct file_operations *f_op;//操作集合结构体指针unsigned int f_flags;//文件打开方式:如O_NONBLOCKloff_t f_ops;//当前读写位置void *private_data;//文件私有数据,通常用来存放设备结构体的地址……};应用:①在多个同类型的设备共用一个驱动程序时:为了在驱动中识别不同的设备,可以将设备结构体的地址保存到file结构体的私有成员中;②利用file结构体的f_flags成员,判断进程是以阻塞还是非阻塞方式访问文件。
(2)inode结构体:(文件的动态信息)struct inode{umode_t i_mode;//inode的权限uid_t i_uid;//inode拥有者的idgid_t i_gid;//inode所属的组iddev_t i_rdev;//设备号struct timespec i_atime;//inode最近一次的存取时间struct timespec i_mtime;//inode最近一次的修改时间struct timespec i_ctime;//inode的产生时间union {//若是块设备,为其对应的block_device结构体指针struct block_device *i_bdev;struct cdev *i_cdev;//若是字符设备,为其对应的cdev结构体指针};……};读read:相应地,read()--->sys_read()--->vfs_read()-->xxx_read()-->copy_to_user。
APP:ssize_t read (int fd, void *buf, size_t count);driver:ssize_t xxx_read (struct file *,char __user *,size_t,loff_t *);读(写)操作在内核空间和用户空间会发生数据交互。
在xxx_read(xxx_write)函数中我们通过调用内核函数:copy_to_user(cpy_from_user)将数据返回给用户buffer(将用户数据读到本地buffer)。
1.read()函数:(1)成功:返回实际读到的字节数;(2)失败:返回-1并设置errno(非负)。
注意:errno就是通过将xxx_read()函数的失败返回值取绝对值得到的。
2.xxx_read()函数:(1)成功:返回实际读到的字节数;(2)失败:返回负的错误码-EFAULT。
platform模型驱动和字符设备模型驱动
platform模型驱动和字符设备模型驱动字符设备驱动模型:1、申请设备号:动态申请(alloc_chrdev_region()),动态申请(register_chrdev_region())struct cdev btn_cdev;//申请设备号if(major){//静态申请设备号dev_id = MKDEV(major, 0);register_chrdev_region(dev_id, 1, "button");}else{//动态申请设备号alloc_chardev_region(&dev_id, 0, 1, "button");major = MAJOR(dev_id);}在Linux中以主设备号用来标识与设备文件相连的驱动程序。
次编号被驱动程序用来辨别操作的是哪个设备。
cdev 结构体的 dev_t 成员定义了设备号,为 32 位,其中高 12 位为主设备号,低20 位为次设备号。
设备号的获得与生成:获得:主设备号:MAJOR(dev_t dev);次设备号:MINOR(dev_t dev);生成:MKDEV(int major,int minor);2、初始化设备:void cdev_init(struct cdev *, struct file_operations *);cdev_init()函数用于初始化cdev 的成员,并建立cdev 和file_operations 之间的连接。
3、注册设备int cdev_add(struct cdev *, dev_t, unsigned);cdev_add()函数向系统添加一个 cdev,完成字符设备的注册。
4、创建设备节点手动创建设备节点:mknod 的标准形式为:mknod DEVNAME {b | c} MAJOR MINOR1,DEVNAME是要创建的设备文件名,如果想将设备文件放在一个特定的文件夹下,就需要先用mkdir在dev目录下新建一个目录;2, b和c 分别表示块设备和字符设备:b表示系统从块设备中读取数据的时候,直接从内存的buffer中读取数据,而不经过磁盘;c表示字符设备文件与设备传送数据的时候是以字符的形式传送,一次传送一个字符,比如打印机、终端都是以字符的形式传送数据;3,MAJOR和MINOR分别表示主设备号和次设备号:为了管理设备,系统为每个设备分配一个编号,一个设备号由主设备号和次设备号组成。
linux字符设备驱动框架流程
linux字符设备驱动框架流程Linux字符设备驱动框架流程一、引言字符设备驱动是Linux系统中的一种设备驱动类型,用于对字符设备的操作和管理。
本文将介绍Linux字符设备驱动框架的流程,包括驱动的注册、设备的初始化、文件操作接口的实现以及驱动的注销。
二、驱动的注册1. 驱动的初始化:驱动的初始化是在模块加载时进行的,通过定义init函数来进行初始化操作。
在初始化函数中,需要进行一些准备工作,如分配主设备号、创建设备类等。
2. 分配主设备号:主设备号是用来标识设备驱动的唯一标识符,通过调用函数alloc_chrdev_region来分配主设备号。
分配成功后,可以通过主设备号和次设备号来唯一标识一个设备。
3. 创建设备类:设备类用于将具有相同属性和行为的设备分为一组,通过调用函数class_create来创建设备类。
设备类的创建需要指定设备类的名字和设备的回调函数。
4. 注册字符设备驱动:注册字符设备驱动是通过调用函数cdev_init和cdev_add来实现的。
cdev_init用于初始化字符设备结构,cdev_add用于将字符设备添加到系统中。
三、设备的初始化1. 设备的创建:设备的创建是通过调用函数device_create来实现的。
设备的创建需要指定设备类、父设备、设备号和设备名。
2. 设备的初始化:设备的初始化是在设备创建后进行的,通过定义probe函数来进行初始化操作。
在probe函数中,需要进行一些设备的特定初始化工作,如申请资源、初始化设备寄存器等。
四、文件操作接口的实现1. 文件操作接口的定义:文件操作接口是用于对设备进行读写操作的接口,包括打开设备、关闭设备、读取设备和写入设备等操作。
文件操作接口需要定义在字符设备结构的file_operations成员中。
2. 文件操作接口的实现:文件操作接口的实现是通过定义对应的函数来实现的。
在函数中,需要进行一些设备操作的具体实现,如读取设备数据、写入设备数据等。
实验二:字符设备驱动实验
实验二:字符设备驱动实验一、实验目的通过本实验的学习,了解Linux操作系统中的字符设备驱动程序结构,并能编写简单的字符设备的驱动程序以及对所编写的设备驱动程序进行测试,最终了解Linux操作系统如何管理字符设备。
二、准备知识字符设备驱动程序主要包括初始化字符设备、字符设备的I/O调用和中断服务程序。
在字符设备驱动程序的file_operations结构中,需要定义字符设备的基本入口点。
open()函数;release()函数read()函数write()函数ioctl()函数select()函数。
另外,注册字符设备驱动程序的函数为register_chrdev()。
register_chrdev() 原型如下:int register_chrdev(unsigned int major, //主设备号const char *name, //设备名称struct file_operations *ops); //指向设备操作函数指针其中major是设备驱动程序向系统申请的主设备号。
如果major为0,则系统为该驱动程序动态分配一个空闲的主设备号。
name是设备名称,ops是指向设备操作函数的指针。
注销字符设备驱动程序的函数是unregister_chrdev(),原型如下:int unregister_chrdev(unsigned int major,const char *name);字符设备注册后,必须在文件系统中为其创建一个设备文件。
该设备文件可以在/dev目录中创建,每个设备文件代表一个具体的设备。
使用mknod命令来创建设备文件。
创建设备文件时需要使用设备的主设备号和从设备号作为参数。
阅读教材相关章节知识,了解字符设备的驱动程序结构。
三、实验内容根据教材提供的实例。
编写一个简单的字符设备驱动程序。
要求该字符设备包括open()、write()、read()、ioctl()和release()五个基本操作,并编写一个测试程序来测试所编写的字符设备驱动程序。
Linux系统字符设备驱动框架笔记
Linux系统字符设备驱动框架笔记字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标、键盘、显示器、串口等等,当我们执行 ls -l /dev 的时候,就能看到大量的设备文件, c 就是字符设备, b 就是块设备,网络设备没有对应的设备文件。
编写一个外部模块的字符设备驱动,除了要实现编写一个模块所需要的代码之外,还需要编写作为一个字符设备的代码。
驱动模型Linux一切皆文件,那么作为一个设备文件,它的操作方法接口封装在struct file_operations ,当我们写一个驱动的时候,一定要实现相应的接口,这样才能使这个驱动可用,Linux的内核中大量使用"注册+回调"机制进行驱动程序的编写,所谓注册回调,简单的理解,就是当我们open一个设备文件的时候,其实是通过VFS找到相应的inode,并执行此前创建这个设备文件时注册在inode 中的'open函数,其他函数也是如此,所以,为了让我们写的驱动能够正常的被应用程序操作,首先要做的就是实现相应的方法,然后再创建相应的设备文件。
#include //for struct cdev#include //for struct file#include //for copy_to_user#include //for error number/* 准备操作方法集 *//*struct file_operations {struct module *owner; //THIS_MODULE//读设备ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);//写设备ssize_t (*write) (struct file *, const char __user *, size_t,loff_t *);//映射内核空间到用户空间int (*mmap) (struct file *, struct vm_area_struct *);//读写设备参数、读设备状态、控制设备long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);//打开设备int (*open) (struct inode *, struct file *);//关闭设备int (*release) (struct inode *, struct file *);//刷新设备int (*flush) (struct file *, fl_owner_t id);//文件定位loff_t (*llseek) (struct file *, loff_t, int);//异步通知int (*fasync) (int, struct file *, int);//POLL机制unsigned int (*poll) (struct file *, struct poll_table_struct *);。
块设备和字符设备详解
linux设备驱动分3类:字符设备驱动、块设备驱动、网络设备驱动。
废话少说:直接贴图展示:1、字符设备结构体描述:cdevstruct cdev{struct kobject kobj;/*内嵌的kobject对象*/strcut module *owner;/*所属模块*/struct file_operations *ops;/*文件操作结构体*/struct list_head list;dev_t dev;/*设备号,dev_t实质是一个32位整,12位为主设备号,20位为次设备号,提取主次设备号的方法:MAJOR(dev_t dev),MINOR(dev_t dev),生成dev_t的方法:MKDEV(int major,int minor)*/unsigned int count;};2、linux2.6内核提供了一组函数来操作cdev结构体void cdev_init(struct cdev *,struct file_operations *);/*初始化cdev的成员,并且建立cdev 与file_operation的连接*/struct cdev *cdev_alloc(void);/*动态申请一个cdev的内存空间*/void cdev_put(struct cdev *p);int cdev_add(struct cdev *,dev_t,unsigned);/*添加一个cdev,完成字符的注册*/void cdev_del(struct cdev *);/*删除一个cdev,完成字符的注销*/在调用cdev_add函数向系统注册字符设备之前,应该先调用register_chrdev_region()函数或是alloc_chrdev_region()函数向系统申请设备号;模型为:int register_chrdev_region(dev_t from,unsigned count,const char *name);int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char*name)在系统调用cdev_del函数从系统注销字符设备后,unregister_chrdev_region()应该释放之前申请的设备号,该函数原型为:unregister_chrdev_region(dev_t from,unsigned count)3、关于file_operations的内容请参考下篇分析下面代码是基于虚拟的globalmem设备进行字符设备的分析,具体在linux中设备驱动远要比这个复杂。
linux字符驱动框架(用户态的read,write,poll是怎么操作驱动的)
linux字符驱动框架(⽤户态的read,write,poll是怎么操作驱动的)前⾔这篇⽂章是通过对⼀个简单字符设备驱动的操作来解释,⽤户态的读写操作是怎么映射到具体设备的。
因为针对不同版本的linux内核,驱动的接⼝函数⼀直有变化,这贴出我测试的系统信息:root@ubuntu:~/share/dev/cdev-2# cat /etc/os-release |grep -i verVERSION="16.04.5 LTS (Xenial Xerus)"VERSION_ID="16.04"VERSION_CODENAME=xenialroot@ubuntu:~/share/dev/cdev-2#root@ubuntu:~/share/dev/cdev-2# uname -r4.15.0-33-generic字符驱动这⾥给出了⼀个不怎么标准的驱动,定义了⼀个结构体 struct dev,其中buffer成员模拟驱动的寄存器。
由wr,rd作为读写指针,len作为缓存buffer的长度。
具体步骤如下:1. 定义 init 函数,exit函数,这是在 insmod,rmmod时候调⽤的。
2. 定义驱动打开函数open,这是在⽤户态打开设备时候调⽤的。
3. 定义release函数,这是在⽤户态关闭设备时候⽤到的。
4. 定义read,write,poll函数,并挂接到 file_operations结构体中,所有⽤户态的read,write,poll都会最终调到这些函数。
chardev.c/*参考:深⼊浅出linux设备驱动开发*/#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/uaccess.h>#include <linux/wait.h>#include <linux/semaphore.h>#include <linux/sched.h>#include <linux/cdev.h>#include <linux/types.h>#include <linux/kdev_t.h>#include <linux/device.h>#include <linux/poll.h>#define MAXNUM 100#define MAJOR_NUM 400 //主设备号 ,没有被使⽤struct dev{struct cdev devm; //字符设备struct semaphore sem;int flag;poll_table* table;wait_queue_head_t outq;//等待队列,实现阻塞操作char buffer[MAXNUM+1]; //字符缓冲区char *rd,*wr,*end; //读,写,尾指针}globalvar;static struct class *my_class;int major=MAJOR_NUM;static ssize_t globalvar_read(struct file *,char *,size_t ,loff_t *);static ssize_t globalvar_write(struct file *,const char *,size_t ,loff_t *);static int globalvar_open(struct inode *inode,struct file *filp);static int globalvar_release(struct inode *inode,struct file *filp);static unsigned int globalvar_poll(struct file* filp, poll_table* wait);/*结构体file_operations在头⽂件 linux/fs.h中定义,⽤来存储驱动内核模块提供的对设备进⾏各种操作的函数的指针。
嵌入式Linux驱动开发(一)——字符设备驱动框架入门
嵌入式Linux驱动开发(一)——字符设备驱动框架入门提到了关于Linux的设备驱动,那么在Linux中I/O设备可以分为两类:块设备和字符设备。
这两种设备并没有什么硬件上的区别,主要是基于不同的功能进行了分类,而他们之间的区别也主要是在是否能够随机访问并操作硬件上的数据。
1.字符设备:提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取。
相反,此类设备支持按字节/字符来读写数据。
举例来说,调制解调器是典型的字符设备。
2.块设备:应用程序可以随机访问设备数据,程序可自行确定读取数据的位置。
硬盘是典型的块设备,应用程序可以寻址磁盘上的任何位置,并由此读取数据。
此外,数据的读写只能以块(通常是512Byte)的倍数进行。
与字符设备不同,块设备并不支持基于字符的寻址。
两种设备本身并没用严格的区分,主要是字符设备和块设备驱动程序提供的访问接口(file I/O API)是不一样的。
本文主要就数据接口、访问接口和设备注册方法对两种设备进行比较。
那么,首先,认识一下字符设备的驱动框架。
对于上层的应用开发人员来说,没有必要了解具体的硬件是如何组织在一起并工作的。
比如,在Linux中,一切设备皆文件,那么应用程序开发者,如果需要在屏幕上打印一串文字,虽然表面看起来只是使用printf函数就实现了,其实,他也是使用了int fprintf(FILE *fp, const char* format[, argument,...])封装后的结果,而实际上,fprintf函数操作的还是一个FILE,这个FILE对应的就是标准输出文件,也就是我们的屏幕了。
那么最简单的字符设备驱动程序的框架是如何呢?应用程序和底层调用的结构正如上图所显示的那样,用户空间的应用开发者,只需要通过C库来和内核空间打交道;而内核空间通过系统调用和VFS(virtual file system),来调用各类硬件设备的驱动。
如果,有过单片机的经验,那么一定知道,操作硬件简单来说就是操作对应地址的寄存器中的内容。
字符驱动框架
字符驱动框架1.1.1 字符驱动框架接下来将前面所讲述的编写驱动的知识融合起来,给出一个完整的字符驱动程序的框架,一个典型的字符驱动框架略缩图如图0.1所示。
相关头文件相关变量和宏定义#include <linux/init.h> #include <linux/module.h>...static int char_cdev_open(struct inode *inode, struct file *file ){}open 方法static int char_cdev_release(struct inode *inode, struct file *file ){}release 方法static ssize_t char_cdev_read(struct file *file, char *buf,size_t count, loff_t *f_pos){}read 方法static ssize_t char_cdev_write(struct file *file, const char *buf, size_t count, loff_t *f_pos){}write 方法static int char_cdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){}ioctl 方法struct file_operations char_cdev_fops = { .owner = THIS_MODULE, .read = char_cdev_read, .write = char_cdev_write,.open = char_cdev_open, .release = char_cdev_release, .ioctl = char_cdev_ioctl };fops 定义static int __init char_cdev_init(void){}module_init(char_cdev_init);static void __exit char_cdev_exit(void){}module_exit(char_cdev_exit);MODULE_LICENSE("GPL");模块初始化代码模块退出代码协议声明模块描述图0.1 char_cdev 字符驱动框架略缩图从略缩图来看,字符驱动框架很简单,与前面一节程序代码相比,只增加了fops的定义以及char_cdev_xxx各方法的实现(尽管差不多是空函数)。
13 字符设备驱动程序框架
return ret;
return fd;
open系统调用分析
而do_filp_open()函数会调用nameidata_to_filp()函 数,nameidata_to_filp()调用__dentry_open();
__dentry_open()函数
//获取文件的inode信息 inode = dentry->d_inode;
每一个文件对应 一个inode结构
inode .i_rdev .i_fop 磁盘上的文件 /dev/xxx
底层操作函 数,操作硬件
字符设备驱动框架
每一个设备都有自己的底层操作函数;也就是说每 一个设备都对应着自己的file_operations结构体; 那么打开的文件file->f_op指针应该指向那一个 file_operations结构体呢?或者说 file->f_op怎样找 到对应的file_operations呢?
操作硬件
内核里面,通过一个宏来建立实地址到虚地址的映 射,这个宏是:
ioremap( paddr,size); 宏的第一个参数是要映射的实地址; 第二个参数是要映射的大小; 宏返回映射后的虚拟地址;
例如,GPF控制寄存的地址映射如下:
unsigned long viraddr; viraddr = ioremap(0x56000050 ,12 ); 通过 *(volatile unsigned long *)viraddr 就可以在 内核中访问GPF寄存器。
def_chr_fops
.open = chrdev_open
(2)而inode->i_fop指向 def_chr_fops,其成员open执行 函数chrdev_open()
字符设备驱动框架
Linux中设备分类:按照对设备的访问方式可分为以下三类:1.字符设备(char device)(1)例如:键盘、鼠标、串口、帧缓存等;(2)通过/dev/下的设备节点访问;以字节为单位访问;(3)一般只支持顺序访问;(特例:帧缓存framebuffer)(4)无缓冲。
2.块设备(block device)(1)例如:磁盘、光驱、flash等;(2)以固定大小为单位访问:磁盘以扇区(512B)为单位;flash以页为单位。
(3)支持随机访问;(4)有缓冲(减少磁盘IO,提高效率)。
3.网络设备(network device)(1)无设备文件(节点);(2)应用层通过socket接口访问网络设备(报文发送和接收的媒介)。
设备驱动在内核中的结构:1.VFS虚拟文件系统作用:向应用层提供一致的文件访问接口,正是由于VFS的存在,才可以将设备以文件的方式访问。
2.虚拟文件系统,存在于内存中,不在磁盘上,掉电丢失。
例如:/proc、/sys、/tmp。
设备号:1.作用:唯一地标识一个设备;2.类型:dev_t devno;即32位无符号整型;3.组成:(1)主设备号:用于区分不同类型(按功能划分)的设备;(2)此设备号:用于区分相同类型的不同设备。
注意:相同类型的设备(主设备号相同)可以使用同一个驱动。
4.构建设备号:int major = 250; int minor = 0;(1)dev_t devno = (major << 20) | minor;不建议使用;(2)利用宏来构建:dev_t devno = MKDEV (major, minor);注意:我们可以通过文件$(srctree)/documentation/device.txt来查看内核对设备号的分配情况。
(1)该文本中的有对应设备文件的设备号是已经被申请过的,我们不可以重复使用(申请);(2)从中可以看出,我们在编写驱动程序时可以使用的主设备号范围为240~254,为了方便记忆,通常使用250作为主设备号。
