一、剧情回顾
在上一篇链接器的秘密里面我们讲到我们用一些特殊的宏让链接器帮我们把一些初始化好的结构体列好队并安排在程序的某一个段里面,这里我例举出了三个和我们主题相关段的分布情况,它们大概如下图所示:(我们可以通过搜索宏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. navicat破解版的下载与激活

    原文链接:http://www.cnblogs.com/djwhome/p/9289295.html 以前一直使用的老版的破解版的navicat,但是最近老是报错 而且连接还特别慢,今天终于不忙了额, ...

  2. Spring实例化Bean三种方法:构造器、静态工厂、实例工厂

    Spring中Bean相当于java中的类,可以通过xml文件对bean进行配置和管理. 一.Bean的实例化: 构造器实例化.静态工厂实例化.实例工厂方式实例化. 目录: 构造器实例化: xml配置 ...

  3. 如何使用RedisTemplate访问Redis数据结构之Hash

    Redis的Hash数据机构 Redis的散列可以让用户将多个键值对存储到一个Redis键里面. public interface HashOperations<H,HK,HV> Hash ...

  4. Netty源码剖析-构建链接

    参考文献:极客时间傅健老师的<Netty源码剖析与实战>Talk is cheap.show me the code! ----主线: 和启动一样也是有两个线程完成的,boss threa ...

  5. Docker 常用命令和Dockerfile

    Docker 简介 官方的解释为:Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,也可以实现 ...

  6. k8s-secret用法

    创建username和password文件: $ echo -n "admin" > ./username $ echo -n "1f2d1e2e67df" ...

  7. Ubuntu 安装 QtCreator (version : Qt 5.9.8)

    平台 :Ubuntu 16.04 QT         :5.9.8 (open source)     首先去QT安装包下载安装包,为了保持与arm板子的统一,本人选择了 5.9.8 版本的QT 可 ...

  8. LocalDate与Date转化

    // 01. java.util.Date --> java.time.LocalDateTimepublic void UDateToLocalDateTime() { java.util.D ...

  9. java8【一、lambda表达式语法】

    特点 lambda表达式允许将函数作为方法的参数 lambda表达式更加简洁 特征 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值. 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需 ...

  10. UEditor编辑器

    1.UEditor编辑器官网:http://ueditor.baidu.com/website/ 2.下载文件:选择  1.4.3.3 .Net版本 UTF-8板 3.建一个ueditor文件夹,将下 ...