一、剧情回顾
在上一篇链接器的秘密里面我们讲到我们用一些特殊的宏让链接器帮我们把一些初始化好的结构体列好队并安排在程序的某一个段里面,这里我例举出了三个和我们主题相关段的分布情况,它们大概如下图所示:(我们可以通过搜索宏ll_entry_declare来找到它们)

那么问题来了,那它们三个是什么关系呢?它们在程序里面是怎么被用到的呢?

二、剧透
剧透环节希望你们喜欢,uclass对驱动进行了归类处理,它描述了具有相似方法的驱动,而不管它们的具体形式。比如对于GPIO它们会具有读取管脚和设置管脚输出的方法,对于serial它们会具有输出串行数据、读取串行数据和设置波特率等方法,这就是uclass要描述的东西,而它不会在乎GPIO或者serial是片内soc上的外设还是外部专用芯片扩展的。你还可以把它理解为一个具体驱动的基类。

让我们回到上图,让我们来理理它们三个的关系,uclass_driver即是uclass的描述,driver即是一个具体的device的驱动,driver_info其实是对应于一个具体的device,所以一个driver_info会有一个与之对应的driver它们通过相同的名字来找到对方。

在u-boot中有个重要的数据结构struct
global_data,通常我们用于全局访问的数据多会放在这个数据结构下,在这里我们要关注其中的struct udevice
*dm_root;成员和struct list_head uclass_root;成员,它们会将所有这里介绍的东西串起来。

三、剧情分析
在开始之前我们先来看下uclass它的数据结构是怎样的,如下:

struct uclass {
    void *priv;
    struct uclass_driver *uc_drv;
    struct list_head dev_head;
    struct list_head sibling_node;
};

通过注释我们了解到dev_head这里将链接所有从属于这个驱动类的器件,sibling_node为了把所有的uclass链接起来并后面会添加到uclass_root链表,uc_drv则指向了描述这个驱动类具体方法的声明。

这里让我们回到code里面来吧,我们继续回到我们在链接器的秘密里面的一段code,这次我将整个过程关键的位置多展开来了,如下:

initf_dm()
{
    dm_init_and_scan()
    {
        dm_init()
        {
            device_bind_by_name(&root_info, gd->dm_root)
            {
                struct driver *drv;

drv = lists_driver_lookup_name("root_driver")
                {
                    struct driver *drv = ll_entry_start(struct driver, driver);

const int n_ents = ll_entry_count(struct driver, driver);

for (entry = drv; entry != drv + n_ents; entry++) 
                    {
                        if (!strcmp(name, entry->name))   // ------------------------------- N.1
                            return entry;
                    }
                };

device_bind_common(drv, "root_driver", gd->dm_root)
                {
                    struct udevice *dev;
                    struct uclass *uc;

uclass_get(drv->id, &uc)  // ------------------------------- N.2
                    {
                        struct uclass *uc;

uc = uclass_find(drv->id)
                        {
                            list_for_each_entry(uc, &gd->uclass_root, sibling_node) { // -- N.3
                                if (uc->uc_drv->id == key)
                                    return uc;
                            }
                        };

if (!uc)
                            return uclass_add(drv->id, &uc)
                            {
                                struct uclass_driver *uc_drv;
                                struct uclass *uc;

uc_drv = lists_uclass_lookup(drv->id);  // --------- N.4

uc = calloc(1, sizeof(*uc));

uc->uc_drv = uc_drv;

list_add(&uc->sibling_node, &gd->uclass_root);  // --- N.5

&uc = uc;
                            };
                    };

dev = calloc(1, sizeof(struct udevice)); // --- N.6

dev->name = name;
                    dev->driver = drv;
                    dev->uclass = uc;

uclass_bind_device(dev)
                    {
                        struct uclass *uc;

uc = dev->uclass;
                        list_add_tail(&dev->uclass_node, &uc->dev_head); // --- N.7
                    };

if (devp) // --- N.8
                        *devp = dev;
                };
            }
        }
    }
}

我们一起来读一下这段展开的code就会看到整个device和driver连接的过程。

N.1的地方通过名字来找到对应的driver,这里就展示了是怎么通过device的名字来找到对应driver的了,这里我把它的入口参数里面的器件名字加上去了,可以看到这一次找的是root_driver,我们可以在文件root.c的最后看到它的声明。

N.2的地方先根据driver的ID(见uclass-id.h)来找到它属于的uclass,即找到它的从属,它先会在gd->uclass_root链表里面去找见N.3处,显然如果是第一次找的话是找不到的,当找不到的时候它便会去预先声明的uclass_driver段里面找并分配出一个uclass结构添加到gd->uclass_root链表里面去,见N.4、N.5处。

找到归属后就为自己分配一个udevice的空间,然后记住自己的名字和driver及从属的uclass见N.6,之后就是把自己添加到从属的uclass的device链表里去,见N.7。

到了N.8后我们的gd->dm_root就初始化好了,指向我们的root device,通过它我们可以找到uclass,找到uclass就可以找到从属于它的device,这就是他们的关系。

经过了上述的描述我们只是初始化好了root device,那其他device呢,它们也是通过一样的过程被一个个添加到链表里面来的,让我们从code上来看看,如下:

initf_dm()
{
    dm_init_and_scan()
    {
        dm_init()
        {
            device_bind_by_name(&root_info, gd->dm_root)
            {
                lists_driver_lookup_name("root_driver");

device_bind_common(drv, "root_driver", gd->dm_root);

uclass_bind_device(dev);
            };
        };

dm_scan_platdata()
        {
            lists_bind_drivers(gd->dm_root)
            {
                struct driver_info *info =
                ll_entry_start(struct driver_info, driver_info);
                const int n_ents = ll_entry_count(struct driver_info, driver_info);
                struct driver_info *entry;
                struct udevice *dev;
                int result = 0;
                int ret;

for (entry = info; entry != info + n_ents; entry++) {
                    ret = device_bind_by_name(gd->dm_root, pre_reloc_only, entry, &dev);
                    if (ret && ret != -EPERM) {
                        dm_warn("No match for driver '%s'\n", entry->name);
                        if (!result || ret != -ENOENT)
                            result = ret;
                    }
                }

return result;
            };
        };
    }
}

可以看出在执行完dm_init()后它将去找到平台上所有的device并把他们的driver找到链接到链表里面去可以看出是device_bind_by_name完成了所有的工作。

为了比较直观的看到他们被相互连接的关系,我这里画了一张图,如下:

图中我假象了还有叫做“xxx_serial”的串口器件被加了进来,从中我们可以直观看出来struct uclass
serial;将作所有serial器件的“基类”它们多将被挂接到serial
uclass的dev_head节点上,每个serial器件的uclass多是指向serial
uclass的。到这里我想你应该对uclass的作用有了一定的理解了,废话就不多说了,下节再见。
---------------------

u-boot器件驱动模型(Device&Drivers)之uclass (转)的更多相关文章

  1. 从串口驱动的移植看linux2.6内核中的驱动模型 platform device & platform driver【转】

    转自:http://blog.csdn.net/bonnshore/article/details/7979705 写在前面的话: 博主新开了个人站点:你也可以在这里看到这篇文章,点击打开链接 本文是 ...

  2. [中英对照]Device Drivers in User Space: A Case for Network Device Driver | 用户态设备驱动: 以网卡驱动为例

    前文初步介绍了Linux用户态设备驱动,本文将介绍一个典型的案例.Again, 如对Linux用户态设备驱动程序开发感兴趣,请阅读本文,否则请飘过. Device Drivers in User Sp ...

  3. 详解Linux2.6内核中基于platform机制的驱动模型 (经典)

    [摘要]本文以Linux 2.6.25 内核为例,分析了基于platform总线的驱动模型.首先介绍了Platform总线的基本概念,接着介绍了platform device和platform dri ...

  4. 让天堂的归天堂,让尘土的归尘土——谈Linux的总线、设备、驱动模型

    本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者: 宋宝华 来源: 微信公众号linux阅码场(id: linuxdev) 公元1951年5月15日的国会听证上, ...

  5. Linux设备驱动模型之I2C总线

    一.I2C子系统总体架构 1.三大组成部分 (1)I2C核心(i2c-core):I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册.注销方法,提供了与具体硬件无关的I2C读写函数. (2)I2 ...

  6. usb驱动开发4之总线设备驱动模型

    在上文说usb_init函数,却给我们留下了很多岔路口.这次就来好好聊聊关于总线设备驱动模型.这节只讲理论,不讲其中的函数方法,关于函数方法使用参考其他资料. 总线.设备.驱动对应内核结构体分别为bu ...

  7. linux RTC 驱动模型分析【转】

    转自:http://blog.csdn.net/yaozhenguo2006/article/details/6824970 RTC(real time clock)实时时钟,主要作用是给Linux系 ...

  8. linux设备驱动模型

    尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要. Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统 ...

  9. linux设备驱动模型(kobject与kset)

    Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统结构的一般性抽象描述.换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要 ...

随机推荐

  1. 【计算机视觉】深度相机(九)--OpenNI API及中间件说明

    本文由官方文档翻译而来 总览 目的 OpenNI 2.0 API(应用程序编程接口)提供了访问PrimerSense的兼容深度传感器.这就使得一个应用程序能够初始化传感器和从设备接收深度(depth) ...

  2. 最新 东方明珠java校招面经 (含整理过的面试题大全)

    从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.东方明珠等10家互联网公司的校招Offer,因为某些自身原因最终选择了东方明珠.6.7月主要是做系统复习.项目复盘.Leet ...

  3. MemCache在.NET中使用Memcached.ClientLibrary详解

    本文说明:memcached分布式缓存的负载均衡配置比例,数据压缩,socket的详细配置等,以及在.net中的常用方法 首先下载客户端的3个dll,ICSharpCode.SharpZipLib.d ...

  4. 前端JS之HTML利用XMLHttpRequest()和FormData()进行大文件分段上传

    用于网页向后端上传大文件 ### 前端代码<body> <input type="file" name="video" id="fi ...

  5. oracle共享数据库操作

    Hello,大家好,这个功能相信新手小白很需要,今天小编因为刚好遇到,所以写出来分享给大家,首先你电脑得有数据库,以及PLSQL工具包,这个相信大家都有了 1.打开NET Manger应用,win10 ...

  6. Spark的lazy特性有什么意义呢?

    [学习笔记] Spark通过lazy特性有什么意义呢? Spark通过lazy特性,可以进行底层的spark应用执行的优化.在生活中,就像三思而后行.谋定而后动. 文章转载自原文:https://bl ...

  7. MySQL 官方样板数据库sakila

    Sakila示例数据库最初由MySQL AB文档团队的前成员Mike Hillyer开发,旨在提供可用于书籍,教程,文章,样本等示例的标准模式. Sakila示例数据库还用于突出MySQL的最新功能, ...

  8. windows下将jar包打入maven仓库

    mvn install:install-file -DgroupId=org.csource -DartifactId=fastdfs-client-java -Dversion=1.27 -Dpac ...

  9. @media screen媒体查询实现页面自适应布局

    @media screen and (min-width:1200px){ //大于等于1200px才会进入 }   @media screen and (max-width:375px) { //小 ...

  10. JavaScript 标准内置对象

    JavaScript 标准内置对象或称全局的对象(global objects)不要和 全局对象(global object)混淆.这里说的全局的对象是说在全局作用域里的对象,全局作用域包含了全局对象 ...