瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统,主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。
?
【公众号】迅为电子
【粉丝群】824412014(加群获取驱动文档+例程)
【视频观看】嵌入式学习之Linux驱动(第九期_设备模型_全新升级)_基于RK3568
【购买链接】迅为RK3568开发板瑞芯微Linux安卓鸿蒙ARM核心板人工智能AI主板???????
经过前面的实验章节,我们不管是先加载device.ko还是driver.ko,驱动和设备都可以匹配成功。所以我们可以猜测不管是device驱动还是driver驱动,都会有匹配操作。
在drivers/base/core.c文件中的device_ add 函数中调用了bus_ probe_ device 函数。如下图所示:
图110-1
bus_ probe_ device 函数,如下图所示,此函数中最重要的是device_initial_probe函数
图110-2
device_initial_probe函数,如下所示
图110-3
__device_attach函数如下所示:
static int __device_attach(struct device *dev, bool allow_async)
{
int ret = 0;
device_lock(dev);
if (dev->p->dead) {
goto out_unlock;
} else if (dev->driver) {
// 如果设备已经绑定了驱动程序,则返回1
if (device_is_bound(dev)) {
ret = 1;
goto out_unlock;
}
// 尝试将设备与驱动程序进行绑定
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
else {
// 绑定失败,将设备的驱动程序指针置为NULL
dev->driver = NULL;
ret = 0;
}
} else {
// 如果设备没有驱动程序,需要遍历总线上的驱动程序进行匹配
struct device_attach_data data = {
.dev = dev,
.check_async = allow_async,
.want_async = false,
};
// 如果设备有父设备,调用 pm_runtime_get_sync() 增加父设备的引用计数
if (dev->parent)
pm_runtime_get_sync(dev->parent);
// 遍历总线上的驱动程序,调用 __device_attach_driver() 进行匹配
ret = bus_for_each_drv(dev->bus, NULL, &data,
__device_attach_driver);
if (!ret && allow_async && data.have_async) {
/*
* 如果无法同步找到适合的驱动程序,并且允许异步探测以及有驱动程序要求异步探测,
* 则尝试进行异步探测。
*/
dev_dbg(dev, "scheduling asynchronous probe\n");
// 增加设备的引用计数,以确保在异步探测期间设备不会被释放
get_device(dev);
// 调度异步任务 __device_attach_async_helper() 进行异步探测
async_schedule(__device_attach_async_helper, dev);
} else {
// 如果无法异步探测或者没有驱动程序要求异步探测,则调用 pm_request_idle() 进入空闲状态
pm_request_idle(dev);
}
// 如果设备有父设备,调用 pm_runtime_put() 减少父设备的引用计数
if (dev->parent)
pm_runtime_put(dev->parent);
}
out_unlock:
// 解锁设备
device_unlock(dev);
return ret;
}
__device_attach函数的主要逻辑如下:
首先,通过调用 device_lock(dev) 锁定设备,确保在进行设备附加操作时不会被其他线程干扰。接下来,检查设备的状态。如果设备已经被标记为死亡,那么直接跳转到解锁设备的位置,并返回0。
如果设备已经绑定了驱动程序,则返回1。如果设备没有绑定驱动程序,则尝试将设备与驱动程序进行绑定。如果绑定成功,返回1;否则,将设备的驱动程序指针置为NULL,并返回0。
如果设备既没有绑定驱动程序,也没有被标记为死亡,那么需要遍历总线上的驱动程序进行匹配。为此,定义了一个结构体 struct device_attach_data,其中包含了设备、是否允许异步探测以及是否有驱动程序要求异步探测的信息。
如果设备有父设备,调用 pm_runtime_get_sync(dev->parent) 增加父设备的引用计数。
调用 bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver) 遍历总线上的驱动程序,并调用 __device_attach_driver 进行匹配。__device_attach_driver 是一个回调函数,用于判断驱动程序是否适配当前设备。
如果无法同步找到适合的驱动程序,并且允许异步探测以及有驱动程序要求异步探测,则调度异步任务 __device_attach_async_helper 进行异步探测。在异步探测之前,会增加设备的引用计数以确保设备在异步探测期间不会被释放。异步探测会在后台进行,不会阻塞当前线程。
如果无法异步探测或者没有驱动程序要求异步探测,则调用 pm_request_idle(dev) 进入空闲状态,让设备进入省电模式。
在上述函数中,使用device_bind_driver函数将设备与驱动程序进行绑定,如下所示:
图110-4
driver_bound函数如下所示,用于将驱动程序绑定到设备上。
static void driver_bound(struct device *dev)
{
if (device_is_bound(dev)) {
// 如果设备已经绑定了驱动程序,则输出警告信息并返回
printk(KERN_WARNING "%s: device %s already bound\n",
__func__, kobject_name(&dev->kobj));
return;
}
// 输出绑定信息
pr_debug("driver: '%s': %s: bound to device '%s'\n", dev->driver->name,
__func__, dev_name(dev));
// 将设备添加到驱动程序的设备链表中
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
// 更新设备的驱动程序链接状态
device_links_driver_bound(dev);
// 检查设备的电源管理回调函数
device_pm_check_callbacks(dev);
/*
* 确保设备不再位于延迟探测列表中,并启动重试所有待处理设备
*/
driver_deferred_probe_del(dev);
driver_deferred_probe_trigger();
// 如果设备有总线,调用总线通知链进行通知
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_BOUND_DRIVER, dev);
// 发送内核对象事件通知
kobject_uevent(&dev->kobj, KOBJ_BIND);
}
上述代码的作用是将驱动和设备进行绑定,首先,通过调用 device_is_bound(dev) 检查设备是否已经绑定了驱动程序。如果设备已经绑定了驱动程序,则输出警告信息并返回。如果设备未绑定驱动程序,将输出绑定信息,其中包括驱动程序的名称、函数名和设备的名称。接下来,通过调用 klist_add_tail() 将设备添加到驱动程序的设备链表中。这样,驱动程序可以通过遍历该链表来访问所有已绑定的设备。
然后,调用 device_links_driver_bound() 更新设备的驱动程序链接状态。这个函数会确保设备和驱动程序之间的链接关系是正确的。
至此,加载驱动和加载设备先后顺序分析完毕。