/*

*本文版权归于凌阳教育。如转载请注明

*原作者和原文链接 http://blog.csdn.net/edudriver/article/details/18354313*

*特此说明并保留对其追究法律责任的权利*

*/

现实社会中存在着大量设备,各种设备有自己的工作方式以及硬件特性。但是如果每类设备(比如串口这类设备)都有自己不同的驱动框架的话,这无疑对驱动程序员来说是一个非常大的挑战。所以为了简化驱动程序员的工作,linux系统从千千万万设备中提取它们的共性,将这些设备分成3大类:字符设备,块设备,网络设备。具体字符设备与块设备有什么区别,大家可以看一下这篇文章(http://www.cnblogs.com/qlee/archive/2011/07/27/2118406.html),今天我们主要介绍字符设备的驱动框架。

字符设备是前面提到的这三类设备中最常见的设备,比如生活中大家常见的键盘、鼠标、触摸屏等都属于字符设备。所以掌握字符设备驱动框架是每个驱动程序员所必须的。下面我们通过先通过一个图把字符驱动的整体概念建立起来:

1、首选上层应用程序调用open函数打开/dev目录下的一个文件(这个文件就是“设备文件”)

2、我们知道open是库里面的函数,所以库里面的一部分代码会被调用

3、这时候库里面会执行一些异常指令,执行这些异常指令最后导致的结果就是产生“系统调用”进入内核,这个时候已经从用户空间进入了内核空间

4、进入内核后,内核会接受到这个异常并开始处理,处理的结果是找到对应的驱动程序

5、找到对应的驱动后,驱动程序中的open()函数就会被调用。因为驱动程序是在内核里面执行的,它的运行权限就比较大,所以在驱动的open()函数就可以对硬件设备进行操作了。

这就是上层open函数到驱动中的open函数的整个过程。当然,如果你open一个文件后,下面紧跟着就会调用read、write和close函数,他们的调用过程跟open函数的调用过程是如出一辙的,这里面我们就不再具体介绍了。

在整个的流程中,大家可能会感觉到看似整个过程非常完整,但是还是有几点过不去。比如当在库中执行一段异常指令就会通过系统调用进入内核,内核是怎么找到对应的驱动程序的呢?如果考虑到这个问题,我们就不得不介绍与字符设备驱动密切相关的两个概念了:设备文件(就是open打开的/dev目录下的文件)和主从设备号。

设备文件:从文字上看就是设备的文件,这样解释也是可以的,这类文件就是专门给硬件设备服务的文件,而且这类文件都存在/dev目录下,每当我们向内核中加一个设备驱动程序,这时候就会在相应的/dev目录下生成相应的设备文件。总结起来:设备文件就是跟驱动程序对应起来的,是上层应用访问驱动的接口。下面我们就来看一下这个设备文件的属性:

从这个图中我们可以看到这个设备文件中非常重要的3个属性:

—设备类型:c和b,c代表这个设备文件对应的设备驱动程序是一个字符设备驱动程序,b代表这个设备文件对应的设备驱动程序是一个块设备驱动程序。

—主设备号(范围:1~254):标识一类驱动程序。通过前面这个图我们可以看出主设备号204是标识串口这一类驱动程序;主设备号31标识mtd这一类驱动程序。那这里就有一个问题了,我们知道一台电脑上不止一个串口,这多个串口的驱动程序都是用主设备号204来标识,那我们怎么从中找到某一个具体的串口驱动程序呢,这个时候就需要我们的从设备号了。

—从设备号(范围:0~255):对应一类驱动程序中某一个具体的驱动程序。当我们通过204这个主设备号找到串口这一类驱动程序后,再通过从设备号找到具体的某一个串口的驱动程序。

所以通过上面的分析我们就知道了我们上层的open函数一调用是怎么找到具体的驱动程序并且调用该驱动程序中的open函数的,最后要归功于设备文件中的主从设备号。所以我们从这里也得出了一个非常重要的结论:主从设备在跟设备文件绑定的同时也跟具体的驱动程序绑定在一起。

好了以上就是我们对写具体的字符设备驱动程序所做的热身,下面我们就来看一下怎么写具体的字符设备驱动程序。

首先,我们先建立一个.c文件把模块搭建起来。

然后,我们就要在模块初始化函数里面注册字符设备并且生成相应的设备文件。我们只需要3个函数就可以完成我们这里的任务。

int register_chrdev(unsigned int major,  const char *name,

const struct file_operations *fops)

这个函数完成了对字符设备的注册,在这个函数中有两个非常值得我们注意的信息:主设备号和struct  file_operartions结构体指针变量。

register_chrdev()的第一个参数就是主设备号,我们如果给第一个参数赋值为0的话,这时候内核会动态的给我们分配一个主设备号,分配的主设备号会通过这个函数的返回值返回。这里大家可能会思考从设备号我们需要在那里指定呢?在这里从设备号是由内核给我们指定的,指定的从设备号就是0~255。也就说这个主设备号对应的所有从设备号我们都占用了,我们可以通过主设备号和0~255中任何一个从设备号都可以找到我们注册的这个字符设备驱动程序。 这样我们通过第一个参数我们的主从设备号就可以跟我们的字符设备驱动程序绑定在一起了,下面我们就来看一下另一个非常重要的参数struct file_operatiosn *变量。

我们在调用register_chrdev()这个函数之前要定义这样的一个结构体变量,在这个结构体里面有什么呢?

在这里只是标出我们这个结构体里面我们看起来比较熟悉的成员:open、read、write、ioctl和release(就是我们驱动里面的close函数),这些成员都是函数指针,这些函数指针都指向我们驱动里面的一些具体函数。只要是被我们open这个函数指针指向的函数就是我们驱动里面的open函数,还有read函数、write函数也是如此。所以总起来说我们驱动程序中的所有open、read、write、ioctl和release函数都被封装到这个结构体里面。我们通过把这个结构体变量的地址传给register_chrdev()函数,就可以把它跟字符驱动绑定在一起。这是我们register_chrdev的第三个参数,第二个参数就是我们给这个字符驱动起的名字,大家不要以为这个名字是设备文件的名字,它只是我们字符驱动的名字,我们可以通过cat /proc/devices命令查看内核里面主设备号使用情况的时候可以看到这个名字。通过上面的函数我们已经把字符设备驱动程序注册进了内核,下面我们的任务就是给这个字符设备驱动生成设备文件。

生成设备文件我们有两种方法:一个是手动生成,一个是自动生成。这里我们只介绍自动生成设备节点的方法。

自动生成设备节点的方法无非就是在register_chrdev()函数后面再调用两个函数:class_create()和device_create()函数,下面我们就主要看一下我们怎么调用这两个函数。

struct class * class_key = class_create(THIS_MODULE, “key_class”);

这个函数主要是内核用来给设备文件进行分类用的,具体怎么实现这里我们就不深入说了,如果大家想了解可以学习有关《设备模型》的知识。

struct device *device_create(struct class *cls,  struct device *parent,

dev_t devt, void *drvdata,

const char *fmt, ...)

这个函数就完成了创建设备节点的任务,第一个参数就是我们前面class_create()函数的返回值,第二个参数我们填上NULL就可以了,第三个参数就是主从设备号(MKDEV(主设备号,从设备号)),第四个参数为NULL,最后一个参数为设备文件的名字。通过调用这个函数我们的设备文件就已经生成成功了。

最后,当我们不想用这个驱动程序的时候,我们必须从内核中把这个驱动程序删除掉。我们需要注销字符设备及删除设备节点。我们同样涉及到了3个函数。

void unregister_chrdev(unsigned int major, const char *name)

注销字符设备驱动的函数。第一个参数主设备号,第二个参数设备的名字。

device_destroy(struct class *class, dev_t devt)

删除设备节点函数。第一个参数class_create()函数的返回值,第二个参数创建设备文件时用到的主从设备号。

void class_destroy(struct class *cls)

删除前面class_create()函数创建的变量。

以上,就是我们写字符设备驱动程序涉及到的主要函数,下面我们就看一下led灯驱动程序。

一步步理解linux字符设备驱动框架(转)的更多相关文章

  1. 深入理解Linux字符设备驱动

    文章从上层应用访问字符设备驱动开始,一步步地深入分析Linux字符设备的软件层次.组成框架和交互.如何编写驱动.设备文件的创建和mdev原理,对Linux字符设备驱动有全面的讲解.本文整合之前发表的& ...

  2. Linux字符设备驱动框架

    字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l ...

  3. (57)Linux驱动开发之三Linux字符设备驱动

    1.一般情况下,对每一种设备驱动都会定义一个软件模块,这个工程模块包含.h和.c文件,前者定义该设备驱动的数据结构并声明外部函数,后者进行设备驱动的具体实现. 2.典型的无操作系统下的逻辑开发程序是: ...

  4. 谈谈Linux字符设备驱动的实现

    @ 目录 字符设备驱动基础 申请设备号 创建设备节点 在驱动中实现操作方法 文件IO调用驱动中的操作 应用程序与驱动的数据交互 内核驱动如何控制外设 控制LED的简单驱动实例 驱动程序的改进 框架复盘 ...

  5. Linux字符设备驱动结构(一)--cdev结构体、设备号相关知识机械【转】

    本文转载自:http://blog.csdn.net/zqixiao_09/article/details/50839042 一.字符设备基础知识 1.设备驱动分类 linux系统将设备分为3类:字符 ...

  6. Smart210学习记录----beep linux字符设备驱动

    今天搞定了beep linux字符设备驱动,心里还是很开心的,哈哈...但在完成的过程中却遇到了一个非常棘手的问题,花费了我大量的时间,,,, 还是把问题描述一下吧,好像这个问题很普遍的,网上许多解决 ...

  7. Linux字符设备驱动实现

    Linux字符设备驱动实现 要求 编写一个字符设备驱动,并利用对字符设备的同步操作,设计实现一个聊天程序.可以有一个读,一个写进程共享该字符设备,进行聊天:也可以由多个读和多个写进程共享该字符设备,进 ...

  8. Linux字符设备驱动基本结构

    1.Linux字符设备驱动的基本结构 Linux系统下具有三种设备,分别是字符设备.块设备和网络设备,Linux下的字符设备是指只能一个字节一个字节读写的设备,不能随机读取设备内存中某一数据,读取数据 ...

  9. Linux 字符设备驱动模型

    一.使用字符设备驱动程序 1. 编译/安装驱动 在Linux系统中,驱动程序通常采用内核模块的程序结构来进行编码.因此,编译/安装一个驱动程序,其实质就是编译/安装一个内核模块 2. 创建设备文件 通 ...

随机推荐

  1. leetcode || 50、Pow(x, n)

    problem: Implement pow(x, n). Hide Tags Math Binary Search 题意:求x的n次幂 thinking: (1)最简单想到的是直观上的数学幂函数求法 ...

  2. java 线程 错失的信号、notify() 与notifyAll的使用

    package org.rui.thread.block; import java.util.Timer; import java.util.TimerTask; import java.util.c ...

  3. 【CSS】隐藏多行文本框Textarea在IE中的垂直滚动栏

    在<[CSS]禁止Google浏览器同意定义调整多行文本框>(点击打开链接)中已经提及过怎样使多行文本框Textarea在一些DOM2的浏览器中固定下来. 这不,多行文本框Textarea ...

  4. 转:APP测试总结

  5. Android动态部署五:怎样从插件apk中启动Service

    转载请注明出处:http://blog.csdn.net/ximsfei/article/details/51072332 github地址:https://github.com/ximsfei/Dy ...

  6. [think in java]第12章 通过异常处理错误

    异常处理是java中唯一正式的错误报告机制. 而且通过编译器强行运行. 异常參数 抛出异常与方法正常返回值的差别:异常返回的"地点"与普通方法调用返回的"地点" ...

  7. 扩展函数之 IsWhat 简单好用

    代码实现: /***扩展函数名细***/ //[IsInRange] ; //以前写法 & num < ) { } //现在写法 , )) { } //datetime类型也支持 //[ ...

  8. [BZOJ 1579] Revamping Trails

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1579 [算法] dist[u][k]表示当前在点u,升级了k条道路,最短路径的长度 ...

  9. C++中值传递(pass-by-value)和引用传递(pass-by-reference)

    1.pass-by-value的情况: 缺省情况C++以pass-by-value(继承C的方式)传递对象至(或来自)函数.函数参数都是以实际参数的复件为初值,调用端所获得的也是函数返回值的一个复件, ...

  10. 利用道格拉斯·普客法(DP法)压缩矢量多边形(C++)

    1.算法描述 经典的Douglas-Peucker算法(简称DP法)描述如下: (1)在曲线首尾两点A,B之间连接一条直线AB,该直线为曲线的弦: (2)得到曲线上离该直线段距离最大的点C,计算其与A ...