【Bus】编写一个Demo虚拟的总线-设备-驱动模型

news/2024/11/20 21:25:47/

文章目录

  • 1. 前言
  • 2. 总线驱动模型三要素
    • 2.1 总线
    • 2.2 设备
    • 2.3 驱动
  • 3. Demo Code
    • 3.1 virt_bus_core.c
    • 3.2 virt_device.c
    • 3.3 virt_driver.c
  • 4. 工程代码下载地址
  • 5. 参考资料

1. 前言

Linux平台为了驱动的可重用性,虚拟了很多的虚拟总线。很经典的就是platform总线,只要platform device和platform driver的名字匹配就调用driver的probe函数。

在分析内核源码时,经常会遇到各种总线。为了方便和加深理解,本篇文章写了一个虚拟Demo总线来加深对总线模型的理解。下面是总线-设备-驱动模型大致架构如下:
在这里插入图片描述

2. 总线驱动模型三要素

三要素主要是包括:总线设备驱动

2.1 总线

  • struct bus_type 结构体:
    struct bus_type {const char		*name; // 总线名称,如:/sys/bus/nameconst struct attribute_group **bus_groups; // 总线属性const struct attribute_group **dev_groups; // 设备属性,指向为每个加入总线的设备建立的默认属性链表const struct attribute_group **drv_groups; // 驱动程序属性/* match:当任何属于该Bus的device或者device_driver添加到内核时,内核都会调用该接口,如果新加的device或device_driver匹配上了自己的另一半的话,该接口要返回非零值,此时Bus模块的核心逻辑就会执行后续的处理。 */int (*match)(struct device *dev, struct device_driver *drv);/* uevent:一个由具体的bus core层实现的回调函数。当任何属于该Bus的device,发生添加、移除或者其它动作时,Bus模块的核心逻辑就会调用该接口,以便bus core层能够修改环境变量。 */int (*uevent)(struct device *dev, struct kobj_uevent_env *env);int (*probe)(struct device *dev); // device和driver匹配后调用,并且最终会调用到driver的probe。int (*remove)(struct device *dev); // 设备移除调用void (*shutdown)(struct device *dev); // 关机调用......
    };
    
    这里定义了一个name为virt_bus的虚拟总线,只实现了部分功能。非常重要的是virt_device_match函数,它决定了device和driver是否匹配成功,定义如下:在这里插入图片描述
  • 总线注册关键函数:
    int bus_register(struct bus_type *bus) // 注册一条总线
    void bus_unregister(struct bus_type *bus) // 注销一条总线
    
    Demo的平台总线的注册、注销如下:
    在这里插入图片描述
    注册成功后,会在/sys/bus/目录下生成一个新的bus name:virt_bus,如下:
    在这里插入图片描述

2.2 设备

  • struct device 结构体:
     struct device // linux/device.h{struct bus_type    *bus; //代表该设备挂在哪条总线上...}
    
  • 设备注册关键函数:
    int device_register(struct device *dev)  // 注册一个设备
    void device_unregister(struct device *dev) // 注销一个设备
    
    Demo的设备注册、注销如下:
    (备注:dev_set_name这个函数很关键,一定要设置device的name,否则会导致device注册失败。)
    在这里插入图片描述
    设备注册成功后,会在/sys/bus/virt_bus/devices总线目录下生成该设备的name:tst3125-002d,它就是通过dev_set_name函数来设置的。效果如下:
    在这里插入图片描述

2.3 驱动

当新的driver注册到该总线时,会遍历该总线上所有设备是否有匹配。同理,当新的device注册到该总线时,也会遍历总线上所有的驱动是否有匹配。

  • struct device 结构体:
     struct device_driver //device.h{const char  *name; // driver的namestruct bus_type        *bus; //代表该驱动挂在哪条总线上int (*probe) (struct device *dev);  //探测函数int (*remove) (struct device *dev);  //移除驱动...}
    
  • 驱动注册关键函数:
    int driver_register(struct device_driver *drv)
    void driver_unregister(struct device_driver *drv)
    
    Demo的设备注册、注销如下:
    在这里插入图片描述
    驱动注册成功后,会在/sys/bus/virt_bus/drivers总线目录下生成该驱动的name:tst3125。如果匹配成功,就会调用driver里面的probe函数。具体效果如下:
    在这里插入图片描述

3. Demo Code

3.1 virt_bus_core.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>#include "virt_bus_core.h"extern struct device_type virt_client_type;const struct virt_device_id *virt_match_id(const struct virt_device_id *id,const struct virt_client *client)
{if (!(id && client))return NULL;while (id->name[0]) {if (strcmp(client->name, id->name) == 0)return id;id++;}return NULL;
}struct virt_client *virt_verify_client(struct device *dev)
{return (dev->type == &virt_client_type)? to_virt_client(dev): NULL;
}static int virt_device_match(struct device *dev, struct device_driver *drv)
{struct virt_client	*client = virt_verify_client(dev);struct virt_driver	*driver;driver = to_virt_driver(drv);/* Finally an virt match */if (virt_match_id(driver->id_table, client))return 1;return 0;
}static int virt_device_probe(struct device *dev)
{struct virt_client	*client = virt_verify_client(dev);struct virt_driver	*driver;int status;if (!client)return 0;driver = to_virt_driver(dev->driver);if (driver->probe)status = driver->probe(client,virt_match_id(driver->id_table, client));elsestatus = -EINVAL;return status;
}static int virt_device_remove(struct device *dev)
{struct virt_client	*client = virt_verify_client(dev);struct virt_driver	*driver;int status = 0;if (!client || !dev->driver)return 0;driver = to_virt_driver(dev->driver);if (driver->remove) {dev_dbg(dev, "remove\n");status = driver->remove(client);}return status;
}static void virt_client_dev_release(struct device *dev)
{kfree(to_virt_client(dev));
}static ssize_t
show_name(struct device *dev, struct device_attribute *attr, char *buf)
{return sprintf(buf, "%s\n", dev->type == &virt_client_type ?to_virt_client(dev)->name : "unknown");
}
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);static struct attribute *virt_dev_attrs[] = {&dev_attr_name.attr,NULL
};
ATTRIBUTE_GROUPS(virt_dev);struct bus_type virt_bus_type = {.name		= "virt_bus",.match		= virt_device_match,.probe		= virt_device_probe,.remove		= virt_device_remove,
};
EXPORT_SYMBOL_GPL(virt_bus_type);struct device_type virt_client_type = {.name       = "virt_client_device",.groups		= virt_dev_groups,.release    = virt_client_dev_release,
};
EXPORT_SYMBOL_GPL(virt_client_type);struct virt_client *
virt_register_device(struct virt_board_info const *info)
{struct virt_client	*client;int         status;client = kzalloc(sizeof(*client), GFP_KERNEL);if (!client)return ERR_PTR(-ENOMEM);client->addr = info->addr;strlcpy(client->name, info->type, sizeof(client->name));client->dev.bus = &virt_bus_type;client->dev.type = &virt_client_type;dev_set_name(&client->dev, "%s-%04x", info->type, info->addr);status = device_register(&client->dev);if (status)goto out_err_silent;printk("device [%s] registered\n", client->name);return client;out_err_silent:kfree(client);return ERR_PTR(status);
}
EXPORT_SYMBOL_GPL(virt_register_device);void virt_unregister_device(struct virt_client *client)
{if (IS_ERR_OR_NULL(client))return;device_unregister(&client->dev);
}
EXPORT_SYMBOL_GPL(virt_unregister_device);int virt_register_driver(struct module *owner, struct virt_driver *driver)
{int res;/* add the driver to the list of virt drivers in the driver core */driver->driver.owner = owner;driver->driver.bus = &virt_bus_type;/* When registration returns, the driver core* will have called probe() for all matching-but-unbound devices.*/res = driver_register(&driver->driver);if (res)return res;printk("driver [%s] registered\n", driver->driver.name);return 0;
}
EXPORT_SYMBOL(virt_register_driver);void virt_unregister_driver(struct virt_driver *driver)
{driver_unregister(&driver->driver);printk("driver [%s] unregistered\n", driver->driver.name);
}
EXPORT_SYMBOL(virt_unregister_driver);static int __init virt_bus_core_init(void)
{int retval;retval = bus_register(&virt_bus_type);if (retval)return retval;return 0;
}static void __exit virt_bus_core_exit(void)
{bus_unregister(&virt_bus_type);
}module_init(virt_bus_core_init);
module_exit(virt_bus_core_exit);
MODULE_LICENSE("GPL");

3.2 virt_device.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/of_device.h>
#include <linux/string.h>#include "virt_bus_core.h"static struct virt_client *tst3125_client = NULL;static int __init virt_device_tst3125_init(void)
{static struct virt_board_info board_info = {.type = "tst3125",.addr = 0x2d,};printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);tst3125_client = virt_register_device(&board_info);return 0;
}static void __exit virt_device_tst3125_exit(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);virt_unregister_device(tst3125_client);
}module_init(virt_device_tst3125_init);
module_exit(virt_device_tst3125_exit);
MODULE_LICENSE("GPL");

3.3 virt_driver.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/of_device.h>
#include <linux/string.h>#include "virt_bus_core.h"static int tst3125_probe(struct virt_client *client, const struct virt_device_id *id)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);printk("[%s] client->name = %s id->name = %s\n", __FUNCTION__, client->name, id->name);return 0;
}static int tst3125_remove(struct virt_client *client)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);printk("[%s] client->name = %s\n", __FUNCTION__, client->name);return 0;
}static const struct of_device_id of_match_ids_tst3125[] = {{ .compatible = "virtual,tst3125",		.data = NULL },{ /* END OF LIST */ },
};static const struct virt_device_id tst3125_ids[] = {{ "tst3125",	(kernel_ulong_t)NULL },{ /* END OF LIST */ }
};static struct virt_driver virt_tst3125_driver = {.driver = {.name = "tst3125",.of_match_table = of_match_ids_tst3125,},.probe = tst3125_probe,.remove = tst3125_remove,.id_table = tst3125_ids,
};static int __init virt_driver_tst3125_init(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return virt_register_driver(THIS_MODULE, &virt_tst3125_driver);
}static void __exit virt_driver_tst3125_exit(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);virt_unregister_driver(&virt_tst3125_driver);
}module_init(virt_driver_tst3125_init);
module_exit(virt_driver_tst3125_exit);
MODULE_LICENSE("GPL");

4. 工程代码下载地址

完整的实验工程Demo代码下载地址如下:
https://download.csdn.net/download/ZHONGCAI0901/87741928

5. 参考资料

https://blog.csdn.net/qq_16504163/article/details/118562670


http://www.ppmy.cn/news/54058.html

相关文章

【Qt】根据界面所在显示器自适应调整ui大小

根据界面所在显示器自适应调整ui大小 获取屏幕信息 使用QDesktopWidget、QApplication::screens()等获取屏幕宽高、DPI等信息,详见上一篇概述。 设置界面元素的大小类型 我们需要将窗口、布局和控件的大小类型(size type)设置为相对单位,如: 设置窗口的尺寸策略为Qt::Size…

LeetCode 216 组合总和 III

题目&#xff1a; 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a;只使用数字1到9&#xff0c;每个数字最多使用一次 &#xff0c;返回所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 示例 1: 输…

题目:16版.字符串算法

1、实验要求 本实验要求&#xff1a;根据用户输入的字符串&#xff0c;计算字符&#xff1a;”sh”重复出现的次数。 1-1. 创建工程并配置环境&#xff1a; 1-1.1. 限制1. 工程取名&#xff1a;SE_JAVA_EXP_E028。 1-1.2. 限制2. 创建包&#xff0c;取名&#xff1a;cn.campsg.…

c++标准模板(STL)(std::array)(三)

定义于头文件 <array> template< class T, std::size_t N > struct array;(C11 起 std::array 是封装固定大小数组的容器。 此容器是一个聚合类型&#xff0c;其语义等同于保有一个 C 风格数组 T[N] 作为其唯一非静态数据成员的结构体。不同于 C 风格数组…

go破冰之旅·6·go中各种运算符(一)

一次5-10分钟即可搞定&#xff0c;实用效率&#xff01; 回顾一下&#xff0c;上文&#xff1a;go破冰之旅5常量、变量、数据类型 提到了go中常量、变量、数据类型这些基础元素&#xff0c;本文来看看go中各种运算符是怎么玩的。 因篇幅及时长关系&#xff0c;本文对算术运算…

学生无线耳机哪款好?两百左右适合学生党的无线耳机推荐

学生无线耳机哪款好&#xff1f;现如今&#xff0c;学生党也成为了蓝牙耳机的主要用户群体之一。接下来&#xff0c;我来给学生群体推荐几款两百左右的无线耳机&#xff0c;一起来看看吧。 一、南卡小音舱Lite2蓝牙耳机 参考价&#xff1a;299 南卡小音舱的音质和佩戴体验都在…

ChatGPT 目前到底能帮助我们程序员做什么?

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&#x1…

视频文件切片

1.为什么网络点播系统使用m3u8更有优势?为何点播要用M3U8来搞&#xff1f;存成一个文件不更好吗&#xff1f; 一个MP4文件可能几百M或几个G&#xff0c;如果读取整个MP4文件的信息并且需要下载一段内容&#xff0c;首次打开播放超慢&#xff08;加载时间长&#xff09;。如果把…