u-boot器件驱动模型(Device&Drivers)之uclass (转)
一、剧情回顾
在上一篇链接器的秘密里面我们讲到我们用一些特殊的宏让链接器帮我们把一些初始化好的结构体列好队并安排在程序的某一个段里面,这里我例举出了三个和我们主题相关段的分布情况,它们大概如下图所示:(我们可以通过搜索宏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 (转)的更多相关文章
- 从串口驱动的移植看linux2.6内核中的驱动模型 platform device & platform driver【转】
转自:http://blog.csdn.net/bonnshore/article/details/7979705 写在前面的话: 博主新开了个人站点:你也可以在这里看到这篇文章,点击打开链接 本文是 ...
- [中英对照]Device Drivers in User Space: A Case for Network Device Driver | 用户态设备驱动: 以网卡驱动为例
前文初步介绍了Linux用户态设备驱动,本文将介绍一个典型的案例.Again, 如对Linux用户态设备驱动程序开发感兴趣,请阅读本文,否则请飘过. Device Drivers in User Sp ...
- 详解Linux2.6内核中基于platform机制的驱动模型 (经典)
[摘要]本文以Linux 2.6.25 内核为例,分析了基于platform总线的驱动模型.首先介绍了Platform总线的基本概念,接着介绍了platform device和platform dri ...
- 让天堂的归天堂,让尘土的归尘土——谈Linux的总线、设备、驱动模型
本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者: 宋宝华 来源: 微信公众号linux阅码场(id: linuxdev) 公元1951年5月15日的国会听证上, ...
- Linux设备驱动模型之I2C总线
一.I2C子系统总体架构 1.三大组成部分 (1)I2C核心(i2c-core):I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册.注销方法,提供了与具体硬件无关的I2C读写函数. (2)I2 ...
- usb驱动开发4之总线设备驱动模型
在上文说usb_init函数,却给我们留下了很多岔路口.这次就来好好聊聊关于总线设备驱动模型.这节只讲理论,不讲其中的函数方法,关于函数方法使用参考其他资料. 总线.设备.驱动对应内核结构体分别为bu ...
- linux RTC 驱动模型分析【转】
转自:http://blog.csdn.net/yaozhenguo2006/article/details/6824970 RTC(real time clock)实时时钟,主要作用是给Linux系 ...
- linux设备驱动模型
尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要. Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统 ...
- linux设备驱动模型(kobject与kset)
Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统结构的一般性抽象描述.换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要 ...
随机推荐
- php redis mysql apache 下载地址
Mysql:https://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6.36-linux-glibc2.5-x86_64.tar.gz php:ht ...
- NDK学习笔记-JNI多线程
前面讲到记录到ffmpeg音视频解码的时候,采用的是在主线程中进行操作,这样是不行的,在学习了POSIX多线程操作以后,就可以实现其在子线程中解码了,也可以实现音视频同步了 简单示例 在native实 ...
- GitHub克隆下载代码速度慢解决办法
这几天克隆下载GitHub代码奇慢无比,网上搜索了一下解决方案有些不太完整,自己试验出了比较完整的解决方式: 1.在hosts文件里追加以下内容(IP需要替换掉),以下几个域名一个都不要少,有些文章只 ...
- Django2.2连接mysql数据库出现django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.3 or newer is required; you have 0.7.11.None问题
在使用Django2.2开发的时候,想要使用mysql数据库,在settings.py文件中更改命令: DATABASES = { 'default': { 'ENGINE': 'django.db. ...
- 为服务部署 Jekins的使用
docker pull jenkinsci/jenkins docker run -d -p 8080:8080 -v E:/docker/jenkins:/var/jenkins_home --na ...
- NOIP(CSP)答题技巧&小细节
1.主函数类型 通常使用int main(),然而可以使用完全等价的signed main() 解锁 #define int long long 的操作 2.long long 的使用 数列长度/边 ...
- Centos7下,宿主机nginx配合docker环境的php-fpm
一.安装docker并启动 yum install docker systemctl start docker 二.安装nginxCentOS 7默认不能从yum中安装nginx,原因可以自己搜索一下 ...
- VIM纵向编辑【转】
原文:https://www.ibm.com/developerworks/cn/linux/l-cn-vimcolumn/index.html Vim 的纵向编辑模式启动方便,使用灵活,还可以配合上 ...
- zepto学习(二)之tap事件以及tap事件点透处理
前言 为什么通过touch可以触发click事件? touch事件的来源 PC网页上的大部分操作都是用鼠标的,即响应的是鼠标事件,包括mousedown.mouseup.mousemove和click ...
- git 操作实践
git操作: - git是一个用于帮助用户实现版本控制的软件 #首先创建项目 1. cd到项目文件目录 2. 鼠标右键点击 Git Bash Here 3. git init #在项目文件目录生成 . ...