首页 > 系统相关 >linux驱动模型--Apple的学习笔记

linux驱动模型--Apple的学习笔记

时间:2023-11-09 22:33:17浏览次数:52  
标签:Apple -- bus driver kset linux device match struct

一,前言

既然是复习设备驱动,第一步当然是做一个最简单的基于设备树的驱动applechar,然后insmod和rmmod使用下,接着要回忆下driver和device是怎么match的,且把相关结构体复习下。

看了下结构体发现有点忘记了,另外match的函数也忘记了。有些东西不需要死记硬背,通过代码分析的方法论找到它即可。就是在我自己的驱动的probe函数打断点,看谁调用的,不是就都找到了嘛!

二,源码分析

  1. 首先回忆到的设备驱动模型是在do_initcalls把注册的driver遍历进行找设备,主要是driver和device match。先要match,然后就probe了。 我在qemu仿真vexpress开发板,自己的驱动中applechar_probe打断点,然后看到了调用情况
ret_from_fork
kernel_init
kernel_init_freeable
do_basic_setup
do_initcalls
do_initcall_level
do_one_initcall
applechar_drv_init
__platform_driver_register
driver_register
bus_add_driver
driver_attach
bus_for_each_dev
__driver_attach
device_driver_attach
driver_probe_device
really_probe
platform_drv_probe
applechar_probe

通过一个个看这些关键函数,找到__driver_attach里面有driver_match_device用来做匹配了。而至于probe,光从上面看really_probe后为什么要先调用platform_drv_probe而不是applechar_probe的原因是really_probe函数中的drv->probe的drv是device_driver类型,其实是在__platform_driver_register的时候赋值了platform_drv_probe,而在platform_drv_probe函数中drv->probe的drv是platform_driver,我添加的驱动代码中也是platform_driver类型的。

int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
	drv->driver.owner = owner;
	drv->driver.bus = &platform_bus_type;
	drv->driver.probe = platform_drv_probe;
	drv->driver.remove = platform_drv_remove;
	drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}

platform_driver_register是我添加在module_init中的,也就是do_one_initcall的时候遍历到的。

  1. 接下来分析match dts属性的具体代码位置。我一路跟进了代码,通过函数名字先找到位置。 调用路径
bus_add_driver
driver_attach
bus_for_each_dev
__driver_attach
driver_match_device
platform_match
of_driver_match_device

好了,找到关键函数了,接着自己继续看代码 of_driver_match_device->of_match_device->of_match_node->__of_match_node分析

static
const struct of_device_id *__of_match_node(const struct of_device_id *matches,
					   const struct device_node *node)
{
	const struct of_device_id *best_match = NULL;
	int score, best_score = 0;

	if (!matches)
		return NULL;

	for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
		score = __of_device_is_compatible(node, matches->compatible,
						  matches->type, matches->name);
		if (score > best_score) {
			best_match = matches;
			best_score = score;
		}
	}

	return best_match;
}

看到细节的东东了,比如用户写的驱动,为什么of_device_id最后要加一个NULL,原因就是__of_match_node中的for循环的结束是根据NULL来判断的。

static const struct of_device_id apple_char[] = {
    { .compatible = "apple,char" },
    { },
};

继续再看,具体的属性匹配如下np->properties依次扫描compatible内容到最后,这就是dts中的compatible。

static int __of_device_is_compatible(const struct device_node *device,
				     const char *compat, const char *type, const char *name)
{
	struct property *prop;
	const char *cp;
	int index = 0, score = 0;

	/* Compatible match has highest priority */
	if (compat && compat[0]) {
		prop = __of_find_property(device, "compatible", NULL);
		for (cp = of_prop_next_string(prop, NULL); cp;
		     cp = of_prop_next_string(prop, cp), index++) {
			if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
				score = INT_MAX/2 - (index << 2);
				break;
			}
		}
		if (!score)
			return 0;
	}

	/* Matching type is better than matching name */
	if (type && type[0]) {
		if (!__of_node_is_type(device, type))
			return 0;
		score += 2;
	}

	/* Matching name is a bit better than not */
	if (name && name[0]) {
		if (!of_node_name_eq(device, name))
			return 0;
		score++;
	}

	return score;
}

接着思考device_node结构体成员值怎么来的? 答:方法就是找这一路的函数,什么时候突然参数中出现device_node,找到如下dev->of_node语句就把传入的device变成了传入device_node,之后就可以在of_node中找dts中的compile属性了。

const struct of_device_id *of_match_device(const struct of_device_id *matches,
					   const struct device *dev)
{
	if ((!matches) || (!dev->of_node))
		return NULL;
	return of_match_node(matches, dev->of_node);  
}

再思考device结构体成员值怎么来的? 答:struct device从bus->p->klist_devices中来,明显klist_devices是一个device链表集合。

int bus_for_each_dev(struct bus_type *bus, struct device *start,
		     void *data, int (*fn)(struct device *, void *))
{
	struct klist_iter i;
	struct device *dev;
	int error = 0;

	if (!bus || !bus->p)
		return -EINVAL;

	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));
	while (!error && (dev = next_device(&i)))
		error = fn(dev, data);
	klist_iter_exit(&i);
	return error;
}

在搜索过程中发现了有相关的help文档bus.rst,于是看到了pci_bus_match,想起来了这些device和driver的match都是用的各个总线的match函数。

3. 好了我主要想复习的device和driver的match及probe找到了。接着要看一个我之前没关注的内容就是sysfs,对linux一切皆文件,所以sysfs才是关键,正好网上看了一篇比较好的blog,那么我需要关注kobject,keset和subsys_private。 从bus->p->klist_devices来分析,bus_type中有subsys_private成员指针。

struct subsys_private {
	struct kset subsys;
	struct kset *devices_kset;
	struct list_head interfaces;
	struct mutex mutex;

	struct kset *drivers_kset;
	struct klist klist_devices;
	struct klist klist_drivers;
	struct blocking_notifier_head bus_notifier;
	unsigned int drivers_autoprobe:1;
	struct bus_type *bus;

	struct kset glue_dirs;
	struct class *class;
};

subsys_private继承了kset,kset还继承了kobject,通过kobject成员可以找到subsys_private。 然后搜索klist_devices可以找到klist_add_tail,就是为此list添加设备,函数名字是bus_add_device。 打个断点看看是否在do_initcalls前就调用了,确实是的,路径如下 do_basic_setup->driver_init->platform_bus_init->device_register->device_add->bus_add_device。 关于bus_add_device的含义看注释,先添加设备属性,再添加软链接,最后将设备添加到devices list中。

/**
 * bus_add_device - add device to bus
 * @dev: device being added
 *
 * - Add device's bus attributes.
 * - Create links to device's bus.
 * - Add the device to its bus's list of devices.
 */
int bus_add_device(struct device *dev)
{
	struct bus_type *bus = bus_get(dev->bus);
	int error = 0;

	if (bus) {

		error = device_add_groups(dev, bus->dev_groups);
        ...
		error = sysfs_create_link(&bus->p->devices_kset->kobj,
						&dev->kobj, dev_name(dev));
        ...
		klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
        ...
	}
	return 0;
    ...
}
  1. kobject.rst里面有kobject和kset相关api使用说明。 kobject是一个目录,里面需要设置属性,然后kset是kobjec的集合,还会处理热拔插。bus_register函数中创建了kset,创建了driver和device。
int kset_register(struct kset *k)
{
	int err;

	if (!k)
		return -EINVAL;

	kset_init(k);
	err = kobject_add_internal(&k->kobj);
	if (err)
		return err;
	kobject_uevent(&k->kobj, KOBJ_ADD);
	return 0;
}

kset_create_and_add里面会先kset_create也就是填充结构体,然后kset_register。如下就调用了热插拔的处理函数kobject_uevent。

int kset_register(struct kset *k)
{
	int err;

	if (!k)
		return -EINVAL;

	kset_init(k);
	err = kobject_add_internal(&k->kobj);
	if (err)
		return err;
	kobject_uevent(&k->kobj, KOBJ_ADD);
	return 0;
}

有如下各种event。搜索了下kobject_uevent在device_add,device_del等时候也会调用。

enum kobject_action {
	KOBJ_ADD,
	KOBJ_REMOVE,
	KOBJ_CHANGE,
	KOBJ_MOVE,
	KOBJ_ONLINE,
	KOBJ_OFFLINE,
	KOBJ_BIND,
	KOBJ_UNBIND,
};

三,关系图

针对match主要结构体我画了一个类图,platform的driver和device都是继承了driver和device,这也就是面向对象的设计方法。

linux驱动模型--Apple的学习笔记_设备驱动

linux驱动模型--Apple的学习笔记_设备驱动_02

四,小结

复习的时候我就突然觉得,其实没必要再看代码分析了,这些代码都不变的,早有人分析过,直接看结果是最快的,我这样花费时间主要目的是学习分析方法,直接拿来的结果和自己思考为什么推导出来的结果,过程完全是不同的,所以理解程度也会不同,复习效果也是不同的。我还是喜欢靠自己思考~



标签:Apple,--,bus,driver,kset,linux,device,match,struct
From: https://blog.51cto.com/AppleCai/8286008

相关文章

  • winter-comes
    wintercomesCreated:2023-10-10T17:49+08:00Published:2023-11-09T21:53+08:00目录志愿活动自然辩证法课堂摘录九月初三欧利昂(Orion)复习重阳节和朋友巴黎圣母院毕导毕业一棵开花的树脸红志愿活动参加了一个志愿活动,是协助学工办老师整理文件,来之前不知道志愿内容,然后没有......
  • 用飞书来谈恋爱,飞书机器人定时给女朋友问好
    前言用飞书机器人每天定时给女朋友发今天日期,在一起天数及女朋友所在地天气情况。后续更新更多定制化好玩的消息内容(距离两个人的生日天数,根据天气温度提醒女朋友加减衣服以及有雨出门带伞,在一起纪念日,及其他有意义的日子提醒)0.先看1.1版本效果技术要求云服务器(或会GithubAction)......
  • Adobe Photoshop 2023最新激活(附图文教程)
    介绍Photoshop软件是一个非常强大的数字图像处理和编辑软件,具有直观易用的用户界面,各种图像编辑和处理工具,各种图层和蒙版功能,各种滤镜和插件。无论是初学者还是有经验的设计师都可以使用该软件轻松地处理、修改和创建各种类型的图像,以满足不同领域的需求。安装步骤下载地址  htt......
  • Java学习—this关键字
    this关键字this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针。在java中,这是一个引用当前对象的引用变量。javathis关键字的用法如下:this关键字可用来引用当前类的实例变量。this关键字可用于调用当前类方法(隐式)。this()可以用来调用当前类的构造函数。this关......
  • Android开发只能转行了吗?转行又能转去哪里呢?
    前言疫情几年,压力山大,不敢跳槽,害怕裁员,卷得一批,多少有点力不从心了。年年互联网寒冬,一二线企业裁员比例不断上升,竞争已然成为常态!或许Android开发早就趋于饱和,新一代技术“人工智能”又正在崛起,属于Android爆火的时代早就已经过去了。只能转行了吗?转行又能转去哪里呢?Android转行方......
  • Android入门教程 | RecyclerView使用入门
    ......
  • 原来阿里字节大厂程序员的简历长这样!
    1前言疫情过后,IT行业内卷就不说了,有很多小伙伴跟我咨询面试环节及简历上的事,都想在简历方面有些突出,博眼球。我发现大部分初、中级甚至高级程序员的简历逻辑都比较混乱,花里胡哨,没有突出重点,筛选简历的人员一眼看上去不知道优缺劣势。通过本人多年的被面试和面试别人的经验看过......
  • 算法day1数组|力扣704二分查找,27移除元素
    数组基础理论数组是存放在连续内存空间上的相同类型数据的集合。可以通过下标轻松获取数据,但是增删元素的时候需要移动其他元素Vector和array的区别vector的底层实现是array,但是vector是容器不是数组数组的元素不能删除,只能覆盖小技巧:取中间intmid=l+r>>1;//有时候怕溢......
  • 20231109打卡
    早上,我准时开始了新一轮的学习。首先,我学习了算法与数据结构中的迪杰斯特拉算法和弗洛伊德算法。迪杰斯特拉算法是一种用来解决最短路径问题的算法,而弗洛伊德算法则可以求解任意两点之间的最短路径。通过课堂讲解和实例演示,我逐渐理解了它们的原理和应用。我通过编写代码实践了这......
  • process-exporter 监控linux机器进程使用情况
    process-exporter监控linux机器进程使用情况背景前期一直想进行关于IP地址的来源和目的地的监控但是耗费了很多精力都没有搞定.感觉应该去偷师一下安全监控软件的使用方式.今天晚上再github上面漫无目的的进行exporter的查找依旧一无所获,但是找到了process-expor......