字符设备驱动高级篇2——注册字符设备驱动的函数代码分析

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

一、旧接口register_chrdev()函数 

上文说到,旧接口register_chrdev()函数内部同时完成了设备号的分配驱动的注册,现在我们来分析是否真的如此。

1、函数的调用层次关系

                |..............register_chrdev

                |....................__register_chrdev

                |................................__register_chrdev_region(申请设备号)

                |................................dev_alloc(这下面三个是在注册设备驱动)

                |................................其他代码(效果如同cdev_init)

                |................................cdev_add

2、函数内部代码概览

 (1)register_chrdev()函数

此函数定义在/include/linux/fs.h文件中。函数的内容如下:

static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
{return __register_chrdev(major, 0, 256, name, fops);
}

(2)__register_chrdev()函数

此函数定义在/fs/char_dev.c文件中。此函数的内容如下:

/*** __register_chrdev() - create and register a cdev occupying a range of minors* @major: major device number or 0 for dynamic allocation* @baseminor: first of the requested range of minor numbers* @count: the number of minor numbers required* @name: name of this range of devices* @fops: file operations associated with this devices** If @major == 0 this functions will dynamically allocate a major and return* its number.** If @major > 0 this function will attempt to reserve a device with the given* major number and will return zero on success.** Returns a -ve errno on failure.** The name of this device has nothing to do with the name of the device in* /dev. It only helps to keep track of the different owners of devices. If* your module name has only one type of devices it's ok to use e.g. the name* of the module here.*/
int __register_chrdev(unsigned int major, unsigned int baseminor,unsigned int count, const char *name,const struct file_operations *fops)
{struct char_device_struct *cd;struct cdev *cdev;int err = -ENOMEM;cd = __register_chrdev_region(major, baseminor, count, name);//获取设备号if (IS_ERR(cd))return PTR_ERR(cd);cdev = cdev_alloc(); if (!cdev)goto out2;//对cdev结构体(设备的象征)成员的填充,尤其是ops(驱动的实体函数指针)的填充,//体现了设备与驱动的绑定与关联//后面再用cdev_add函数注册设备时,其实就相当于注册驱动,因为设备已经和驱动关联//这两句代码相当于cdev_init()函数的作用。cdev->owner = fops->owner; cdev->ops = fops;kobject_set_name(&cdev->kobj, "%s", name);err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);if (err)goto out;cd->cdev = cdev;return major ? 0 : cd->major;
out:kobject_put(&cdev->kobj);
out2:kfree(__unregister_chrdev_region(cd->major, baseminor, count));return err;
}

(3)__register_chrdev_region()函数

此函数定义在/fs/char_dev.c文件中。此函数的内容见本文的第三点。


(4)dev_alloc()函数、cdev_add()函数

这些函数的说明,见博客:字符设备驱动高级篇1——注册字符设备驱动的新接口

二、新接口函数

新接口函数包括许多,这里只介绍分配设备号的函数,包括register_chrdev_region()函数和alloc_chrdev_region()函数,前者用于获取指定设备号,后者让内核自动分配设备号。

1、函数的调用关系

|..............register_chrdev_region

|....................__register_chrdev_region

或者

|..............alloc_chrdev_region

|....................__register_chrdev_region

可见内部都调用了__register_chrdev_region这个函数。

2、函数内部代码概览

(1)register_chrdev_region()函数

此函数定义在/fs/char_dev.c文件中。此函数的内容如下:

/*** register_chrdev_region() - register a range of device numbers* @from: the first in the desired range of device numbers; must include*        the major number.* @count: the number of consecutive device numbers required* @name: the name of the device or driver.** Return value is zero on success, a negative error code on failure.*/
int register_chrdev_region(dev_t from, unsigned count, const char *name)
{struct char_device_struct *cd;dev_t to = from + count;dev_t n, next;for (n = from; n < to; n = next) {next = MKDEV(MAJOR(n)+1, 0);if (next > to)next = to;cd = __register_chrdev_region(MAJOR(n), MINOR(n),next - n, name);if (IS_ERR(cd))goto fail;}return 0;
fail:to = n;for (n = from; n < to; n = next) {next = MKDEV(MAJOR(n)+1, 0);kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));}return PTR_ERR(cd);
}

可知此函数最终调用了__register_chrdev_region函数。


(2)alloc_chrdev_region()函数

此函数定义在/fs/char_dev.c文件中。此函数的内容如下:

/*** alloc_chrdev_region() - register a range of char device numbers* @dev: output parameter for first assigned number* @baseminor: first of the requested range of minor numbers* @count: the number of minor numbers required* @name: the name of the associated device or driver** Allocates a range of char device numbers.  The major number will be* chosen dynamically, and returned (along with the first minor number)* in @dev.  Returns zero or a negative error code.*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
{struct char_device_struct *cd;cd = __register_chrdev_region(0, baseminor, count, name);if (IS_ERR(cd))return PTR_ERR(cd);*dev = MKDEV(cd->major, cd->baseminor);return 0;
}

可知此函数最终调用了__register_chrdev_region函数。

三、分析__register_chrdev_region函数

老接口register_chrdev函数调用关系如下:

                |..............register_chrdev

                |....................__register_chrdev

                |................................__register_chrdev_region

                |................................dev_alloc

                |................................cdev_add

新街口register_chrdev_region、alloc_chrdev_region调用关系如下:

                |..............register_chrdev_region

                |....................__register_chrdev_region

                或者

                |..............alloc_chrdev_region

                |....................__register_chrdev_region

从上面的分析得知,新接口和老接口内部都调用了__register_chrdev_region()函数。此函数用于向内核申请设备号。现在我们来重点分析函数__register_chrdev_region。

__register_chrdev_region()函数定义在/fs/char_dev.c文件中,内容如下:

/** Register a single major with a specified minor range.** If major == 0 this functions will dynamically allocate a major and return* its number.** If major > 0 this function will attempt to reserve the passed range of* minors and will return zero on success.** Returns a -ve errno on failure.*/
static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,int minorct, const char *name)
{struct char_device_struct *cd, **cp;int ret = 0;int i;// 分配一个新的 char_device_struct 结构,并用 0 填充。cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);if (cd == NULL)return ERR_PTR(-ENOMEM);mutex_lock(&chrdevs_lock);/* temporary */if (major == 0) {//如果申请的设备编号范围的主设备号为 0for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {//从最后一个倒着来找if (chrdevs[i] == NULL) //如果为空(这整个指针数组什么时候赋值为空的)break;}if (i == 0) {ret = -EBUSY;goto out;}major = i; //主设备号就是这个ret = major;}//根据参数设置struct char_device_struct变量的值cd->major = major;  //如果为0,则分配刚才得到的//如果不为0,则分配自身设置的cd->baseminor = baseminor;cd->minorct = minorct;strlcpy(cd->name, name, sizeof(cd->name));i = major_to_index(major);//major % BLKDEV_MAJOR_HASH_SIZE;即major%255//这里从自身设置编号时的角度考虑,major为0时这步没意义?for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)if ((*cp)->major > major ||((*cp)->major == major &&(((*cp)->baseminor >= baseminor) ||((*cp)->baseminor + (*cp)->minorct > baseminor))))break;/* Check for overlapping minor ranges.  */if (*cp && (*cp)->major == major) {int old_min = (*cp)->baseminor;int old_max = (*cp)->baseminor + (*cp)->minorct - 1;int new_min = baseminor;int new_max = baseminor + minorct - 1;/* New driver overlaps from the left.  */if (new_max >= old_min && new_max <= old_max) {ret = -EBUSY;goto out;}/* New driver overlaps from the right.  */if (new_min <= old_max && new_min >= old_min) {ret = -EBUSY;goto out;}}cd->next = *cp;*cp = cd;mutex_unlock(&chrdevs_lock);return cd;
out:mutex_unlock(&chrdevs_lock);kfree(cd);return ERR_PTR(ret);
}

(1)函数说明

/** Register a single major with a specified minor range.* If major == 0 this functions will dynamically allocate a major and return its number.* If major > 0 this function will attempt to reserve the passed range of* minors and will return zero on success.* Returns a -ve errno on failure.*/

(2)参数说明

1)参数 major

如果major大于0,则major表示想要申请的主设备号,函数返回0表示申请成功。

如果major等于0,则表示让内核自动分配主设备号,函数成功则返回所分配的主设备号。

2)参数 baseminor

表示次设备号的起始编号。

3)参数 minorct

表示次设备号的数目。

4)参数 name

表示设备(或者说驱动)的名字。

5)返回值类型:struct char_device_struct

这个结构体定义如下:

static struct char_device_struct {struct char_device_struct *next;unsigned int major;               //主设备的编号unsigned int baseminor;           //次设备的起始编号int minorct;                      //次设备的数目char name[64];                    //设备(或者说驱动)的名字struct cdev *cdev;	              //指向  字符设备驱动程序描述符  的指针
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];  //#define CHRDEV_MAJOR_HASH_SIZE	255

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461284.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LR学习视频

0 性能测试常见用语http://www.boobooke.com/v/bbk15771 lr目录分析http://www.boobooke.com/v/bbk15742.1 lr界面分析http://www.boobooke.com/v/bbk17352.2 lr界面分析http://www.boobooke.com/v/bbk17362.3 lr界面分析http://www.boobooke.com/v/bbk17373 lr常用术语http://…

深入浅出mysql gtid_深入理解MySQL GTID

GTID的概念何为GITDGTID(global transaction identifier)是全局事务标识符&#xff0c;在MySQL5.6版本中作为一个超级特性被推出。事务标识不仅对于Master(起源)的服务器来说是惟一的&#xff0c;而且在整个复制拓扑架构来说&#xff0c;也是全局唯一的。1.GTID的格式GTID sou…

winform 64位系统中使用

WINFOR编译成X86的 转载于:https://blog.51cto.com/agilitygod/1419939

字符设备驱动高级篇3——自动创建设备文件

以下内容源于朱有鹏嵌入式课程学习与整理&#xff0c;如有侵权请告知删除。 问题引入 之前在应用层测试驱动源程序时&#xff0c;需要先安装驱动模块&#xff0c;安装驱动模块后会得到一个主设备号&#xff0c;然后在命令行利用mknod命令“mknod /dev/xxx c 主设备号 次设备号”…

long 转为string_面试必问 Redis数据结构底层原理String、List篇

点击关注上方“Java大厂面试官”&#xff0c;第一时间送达技术干货。阅读文本大概需要 8 分钟。前言今天来整理学习下Redis有哪些常用数据结构&#xff0c;都是怎么使用的呢&#xff1f;首先看下全局存储结构。全局存储结构基础你们肯定都知道&#xff0c;redis支持的基础数据结…

wpf 3D学习

最近在看一些关于wpf 3d的效果&#xff0c;研究了一些代码特效&#xff0c;现在和广大博友共享一下. 首先用到的是MeshGeometry3D&#xff0c;msdn上介绍&#xff1a;用于生成三维形状的三角形基元。主要有4个依赖属性&#xff1a;NormalsProperty&#xff0c;PositionsPropert…

字符设备驱动高级篇4——自动创建设备文件的函数代码分析

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、概述 设备文件的创建&#xff0c;主要涉及class_create()函数、device_create()函数。 class_create()函数用于自动创建 /sys/class/目录下的xxx目录。 device_create()函数用于自动创建 /dev/…

unicode字符、python乱码问题

http://www.cnblogs.com/BeginMan/archive/2013/08/08/3246619.html#a1 Python常见常用知识点http://blog.csdn.net/tingsking18/article/details/4033645 Unicode和Python的中文处理如何让Python的Unicode字符串支持中文&#xff1f;要想利用Python的Unicode机制处理字符串&…

win10下如何安装vb6.0sp6_Mac如何安装win10系统?Parallels Desktop 15 Mac安装win10系统教程...

Parallels Desktop 15 mac版是mac上非常强大也非常好用的虚拟机软件&#xff0c;最新版本的parallels desktop mac 15针对最新的Windows 10更新和macOS Catalina&#xff08;10.15&#xff09;进行了优化。今天分享的内容就是Parallels Desktop 15 mac版如何安装win10系统。PD虚…

android面试题精选

1.android dvm 的进程和Linux的进程&#xff0c;应用程序的进程是否为同一个概念&#xff1a; 答&#xff1a;dvm是dalivk虚拟机。每一个android应用程序都在自己的进程中运行&#xff0c;都拥有一个dalivk虚拟机实例。而每一个dvm都是在linux的一个进程。所以说可以认为是同一…

字符设备驱动高级篇5——静态映射表的建立过程,动态映射结构体方式操作寄存器

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 补充内容&#xff1a;字符设备驱动基础5——驱动如何操控硬件_天糊土的博客-CSDN博客 一、静态映射表的建立过程 关于“静态映射表的建立”这部分内容&#xff0c;有以下三个关键&#xff1a; &…

python 分布图_python数据分布型图表柱形分布图系列带误差线的柱形图

柱形分布图系列柱形分布图系列使用柱形图的方式展示数据的分布规律&#xff1b;可以借助误差线或散点图&#xff1b;带误差线的柱形图就是使用每个类别的均值作为柱形的高度&#xff1b;再根据每个类别的标准差绘制误差线&#xff1b;缺点&#xff1a;无法显示数据的分布情况&a…

[汇编] 002基础知识-CPU和寄存器

CPU是什么 当然这里的内存不仅仅指电脑上的内存&#xff0c;例如&#xff1a;我的金士顿8G内存&#xff0c;七彩虹1G独显&#xff0c;在这里来说&#xff0c;显卡也是有内存的(寄存器) CPU如何控制其它部件的&#xff1f; 问题&#xff1a;CPU是如何和电脑主机中其它芯片有条不…

Asp.net中页面传值几种方式

页面传值是学习asp.net初期都会面临的一个问题&#xff0c;总的来说有页面传值、存储对象传值、ajax、类、model、表单等。但是一般来说&#xff0c;常用的较简单有QueryString&#xff0c;Session&#xff0c;Cookies&#xff0c;Application&#xff0c;Server.Transfer。  …

字符设备驱动高级篇6——内核提供的读写寄存器接口

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 1、访问寄存器的方式 之前对寄存器的操作&#xff0c;都是先定义指向寄存器的指针&#xff0c;然后再解引用来对寄存器进行操作。这是因为ARM体系中&#xff0c;内存和IO是统一编址的。但是其他体系…

java台球游戏设计原理_Java实现简单台球游戏

Java实现简单台球桌问题&#xff0c;供大家参考&#xff0c;具体内容如下需求&#xff1a;使小球可以在桌面上移动&#xff0c;移动到桌面边缘将被弹回&#xff0c;显示小区的移动素材&#xff1a;小球照片桌球照片程序源代码&#xff1a;package 桌球游戏;import java.awt.*;i…

wordpress教程:默认http头信息X-Pingback的隐藏与修改

利用站长工具的http状态查询工具查询可以看到类似如下的一段http HEAD信息 X-Pingback: http://www.kristain.com/wordpress真实路径/xmlrpc.php 其实这样就已经暴露了wordpress网站的真实路径了&#xff0c;那么如何来隐藏wordpress默认http HEAD信息中的X-Pingback信息呢&…

关于java assertion

大部分转载自参考资料&#xff1a;http://www.ibm.com/developerworks/cn/java/l-javaassertion/index.html assertion(断言)在软件开发中是一种常用的调试方式&#xff0c;assertion就是在程序中的一条语句&#xff0c;它对一个boolean表达式进行检查&#xff0c;一个正确程序…

mupdf不支持x64_Delphi xe2使用x64编译器编译ASM代码时出错 . 不支持的语言功能:'ASM'...

代码无法直接正确移植到x64&#xff0c;因为它将执行64位指针截断 - 有关详细信息&#xff0c;请参见下文 .64位应用程序不支持将汇编语句与Pascal代码混合使用 . 使用Pascal代码或完全用汇编编写的函数替换汇编语句 .这里使用装配是不必要的 . 我不确定为什么原作者会选择去解…

IOC是什么?

2019独角兽企业重金招聘Python工程师标准>>> Inversion of Control&#xff0c;即反转控制&#xff0c;或许说为依赖注入更为合适。IoC就是一种设计模式。 Interface Driven Design接口驱动&#xff0c;接口驱动有很多好处&#xff0c;可以提供不同灵活的子类实现&a…