当前位置:  技术问答>linux和unix

LDD3 分配和释放设备编号的疑惑

    来源: 互联网  发布时间:2016-10-01

    本文导语:  int register_chrdev_region(dev_t first, unsigned int count, char *name); 书上说 first 是你要分配的起始设备编号   我的理解是这里就是指主设备号,为什么要说是“要分配的起始设备编号”        count 是你请求的连续设...

int register_chrdev_region(dev_t first, unsigned int count, char *name);
书上说 first 是你要分配的起始设备编号   我的理解是这里就是指主设备号,为什么要说是“要分配的起始设备编号”
       count 是你请求的连续设备编号的总数   我给一个设备申请一个主设备号,不就一个吗?难道还会申请好几个吗?

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
dev 是一个只输出的参数, 它在函数成功完成时持有你的分配范围的第一个数
    我觉得dev在这里就是主设备号的地址
fisetminor 应当是请求的第一个要用的次编号; 它常常是 0. 
    次设备号我觉得也应该只有一个号

总的疑问可能就在于一个驱动程序应该只有一个确定的主设备号和确定的次设备号,而不应该是一个范围

|
Linux设备驱动程序之字符设备驱

    在学习Linux设备驱动程序设计过程中,见得最多的应该是字符型设备,但是,对于字符型设备驱动程序的注册,注销,在书上看到的大多就是应用register_chrdev_region, unregister_chrdev_region这两个函数,但是,在内核源码中看到得最多的又是register_chrdev, unregister_chrdev这两个函数,在有的资料上面把后面两个函数说明为旧方法,提倡我们应用前两个函数,但是,很多时候,除了做几个简单的实验外,我们更多是应用Linux内核提供给我们的相应的驱动模块来注册或者注销我们的设备驱动程序,Linux内核中已经有很多成功的设备驱动,对于某些设备,我们只需调用Linux内核给我们提供的驱动模块就可以了,比如i2c总线驱动,不过这得在了解Linux内核中i2c总线子系统的前提下才能够很好的应用,所以,这不得不去看内核驱动源码,这要求我们对内核各驱动模块有一定的了解。
    好了,我们回到字符设备驱动上来,在我没有深入了解字型设备驱动之前,也跟大家一样,跟着书上所告诉我的知识,(这里以register_chrdev_region与 unregister_chrdev_region这两个函数注册字符型设备驱动程序来说明,在后面将会说明两种注册方法的不同),首先定义一个结构体,在里面嵌入一个cdev结构体,因为我们在使用cdev相关结构体的时候将会应用到,其实,不嵌入也行,不过,一般使用嵌入方法。一般结构如下:
static struct xx_cdev{
    …..
    struct cdev xxx_cdev;
};
接下来是定义相关的file_operations结构体,进行相应的初始化,再实现相关域的映射,最后,应用 register_chrdev_region进行注册,应用unregister_chrdev_region进行注销,完成模块的初始化与注册后,这样一个字符型设备驱动程序就行了,但是,不知道大家有没有看到内核代码或者一些其它的资料,很多应用 register_chrdev,unregister_chrdev来进行注册与注销的字符型设备驱动程序,更本没有提到过cdev结构体,这是什么呢,我将在下面进行说明。
    首先我们从函数原型来说明,下面为这两种注册与注销方法的函数原型:
第一种:
int register_chrdev(unsigned int major, const char *name,struct file_operations *fops);
int unregister_chrdev(unsigned int major, const char *name);

第二种:
int register_chrdev_region(dev_t from, unsigned count, const char *name);
void unregister_chrdev_region(dev_t from, unsigned count);
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, 
unsigned count,const char *name);
从函数原型上可以看出,两种方法有很大的不同,其实,也没有多大的区别,只是形参不同罢了,最后都是调用
static struct char_device_struct * __register_chrdev_region(unsigned int                                  major, unsigned int baseminor,int minorct, const char *name)
来实现注册,调用
static struct char_device_struct * __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)
来完成相应的注销,不同之处在于第一种方会一次给分配256个同主设备号不同次设备号的设备驱动,应用此种方法,必须得保存此主设备号还有256个次设备号可用,否则将注册出错,而第二种方法则应用count这个参数来指定连续分配的个数,在后面的分配过程中将会看到怎么实现的。
好的,让我们来看看函数的重要点,在register_chrdev中,我们可以看到这样的一行代码:cd = __register_chrdev_region(major, 0, 256, name);这就是为每一个主设备号分配256个设备驱动,关于是怎么分配的,我们将在后面进行分析__register_chrdev_region函数。对于此种方法,我们没有明显的嵌入或者应用cdev结构体,因为在此函数中,内核帮我们完成相关的工作。如下:
    cdev = cdev_alloc();
    if (!cdev)
        goto out2;

    cdev->owner = fops->owner;
    cdev->ops = fops;

err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
这几行代码帮我们完成了,所以,我想很大初学者,跟我一样,当看到过两种方法写过的驱动程序时,会有一点不了解,现在应该了解了吧。关于注销函数在此不进行说明,想知道的朋友请自行去参考内核源代码,其实,本人觉在,搞嵌入Linux,很多时候在看内核源码,不过这是看一件艺术品,在看内核源码中,你会学到很多东西,比如,宏定义的相关技巧,指针应该得有所提高,因为内核源码中差不多每一行都有指针存在。应用预定义控制调试信息,让调试相关代码留在源码中,为将来的维护带来方便。
    我们来分析第二种注册方法,此函数同样也是调用__register_chrdev_region函数来完成相应的注册工作,但是,它可以主次多个主设备号。这是通过一个for循环语言来实现的,源码如下:
for (n = from; n  to)
            next = to;
        cd = __register_chrdev_region(MAJOR(n), MINOR(n),
                   next - n, name);
        if (IS_ERR(cd))
            goto fail;
    }
我们应用register_chrdev_region传入的是起始设备编号form,连续主册的个数count,设备字称,在此函数的最前面有这么一句 dev_t to = from + count;这是求一个范围,从上面的循环中可以看出,如果注册的设备编号在一个主设备号之间,那么只注册一次,但是如果在多个主设备号之间,需要注册多次,虽然此方法可以注册多主设备号,但是,我们提倡应该把count控制在一个主设备号的范围内,一个字符设备名称,不同主设备号,有点不太合常理。还有动态分配主设备号的方法,与此方法差不多,所以在此不再进行说明。
    是时候分析__register_chrdev_region的时候了,这个函数是真正完成注册的函数,所以,对此函数的了解,也就差不多了解了register_chrdev_region和 register_chrdev这两种方法了。关于struct char_device_struct结构体,请自行查看。
在此函数中,应用下面的语句来完成动态分配主设备号:
if (major == 0) {
        for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
            if (chrdevs == NULL)
                break;
        }

        if (i == 0) {
            ret = -EBUSY;
            goto out;
        }
        major = i;
        ret = major;
    }
此代码段用途在于查找一个空闲的主设备号,从最大开始寻找,所以,我们得大的一般是最大没有使用的主设备号,相信读者也能很容易看懂些段代码。下面的代码为注册的重点。
cd->major = major;
    cd->baseminor = baseminor;
    cd->minorct = minorct;
    cd->name = name;

    i = major_to_index(major);

    for (cp = &chrdevs; *cp; cp = &(*cp)->next)
        if ((*cp)->major > major ||
            ((*cp)->major == major && (*cp)->baseminor >= baseminor))
            break;
    if (*cp && (*cp)->major == major &&
        (*cp)->baseminor next = *cp;
    *cp = cd;
进行注册时有二种情况可以进行注册,第一是主设备号空闲;第二是次设备号有空闲。但是如果次设备号有空闲,连续分配的个数大于本主设备空闲次设备号数,那么不能完成注册。注销函数不进行分析。
    我们接着分析怎样打开相应的设备的设备文件,其实,所有字符型设备驱动程序都打开时都首先运行default file_operations结构体变量的open函数,在此函数在,进行相应的filp f_op域的重定向,才将相应的设备驱动对映相应的设备。所以,打开顺序为:default open  在此函数中完成file f_op 的重定向,再运行相应的open函数。此过程如下:
new = container_of(kobj, struct cdev, kobj),进行cdev的重定向,获取相应设备的cdev结构体,filp->f_op = fops_get(p->ops);进行file_operations重定向,获取相应设备的file_operations结构体变量,ret = filp->f_op->open(inode,filp);重定向打开文件,关于其它的,请自行分析,此函数的重点差不多分析完了。
    另外,关于其它函数和kobject相关的内容在此不进行分析,因为本人不能确保本人的理解分析出来大家是否能理解(本人的理解不一定对),所以,若想对 kobject,stsfs,设备模型相关的内容的读者,请参考《LDD3》第十四章。上述函数与结构体位置。sruct cdev 查看/linux/cdev.h   其它相关函数请参考/linux/fs/char_dev.c 在此给大家推荐一个windows环境下阅读源码的好工具,Source Insight这是一个不错的源码阅读工具,可以把整个Linux内核树加进去,自动进行查询相关变量,函数,结构体,宏定义等。
    好了,写到这里,该停笔了,写了很多,也不知道有没有说明白自己想说的东西,这些也是本人现在水平的能及之处,由于水平与个人对同一知识点不同的理解,所以,若有不对之处,请给予指出,让我也有学习、改正、进步的机会。

|
假如你成功分配到三个设备编号,分别是0,1,2,这三个设备编号的主设备号不是同一个数值0吗?次设备编号不分别是:0,1,2吗?

    
 
 

您可能感兴趣的文章:

  • ldd3中的一点疑惑
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • 寻找学习ldd3的同志
  • 请教ldd3的驱动挂载问题!
  • 请问哪有LDD3中文影印版下载呀
  • 有关LDD3中的faulty.c的问题
  • LDD3网上下的实例源码哪个是第三章的啊?
  • ldd3书中的一个问题
  • 请问LDD3中的 simple_nopage_mmap 函数的一些问题
  • 关于ldd3中的例子scull中的pipe和access
  • ldd3: 禁止了中断,是不是就没有系统滴答了?
  • LDD3中的sbull驱动make不能通过
  • LDD3的scull代码问题
  • LDD3 阅读理解问题
  • 关于LDD3中short代码的分析
  • LDD3中scull编译报错
  • ldd3中helloworld的问题,无法生成hello.ko
  • ldd3中的“重定向控制台消息”,老是错误,为啥????
  • ldd3的hello world编译出错
  • ldd3中的scull驱动程序应该如何测试?
  • LDD3中scull的例子
  • ldd3中的hello world驱动程序的疑问???


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3