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

news2024/11/28 2:44:39

文章目录

  • 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/name
    	const 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的name
        
        struct 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));
    else
    	status = -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代码下载地址如下:
正在上传中…

5. 参考资料

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/474654.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android Room数据库如何使用增删改查

先看运行效果图。 1.在app下的build.gradle。在dependencies{}闭包中添加如下依赖 //roomdef room_version "2.3.0"implementation "androidx.room:room-runtime:$room_version"annotationProcessor "androidx.room:room-compiler:$room_version&q…

爱奇艺DRM修炼之路

01 DRM的定义和作用 DRM&#xff0c;即数字版权管理&#xff08;digital rights management&#xff09;&#xff0c;是在数字内容交易过程中&#xff0c;对知识产权进行保护的技术、工具和处理过程。它的目的是防止数字内容被未经授权的用户复制、修改和分发&#xff0c;以保护…

解读TaskMatrix.AI

ChatGPT在广泛的开放域任务上展现出令人瞩目的强大对话、上下文学习和代码生成能力&#xff0c;而且它所获得的常识知识还可以为特定领域的任务生成高级解决方案概要。不过&#xff0c;除了更强大的学习、理解和生成能力&#xff0c;ChatGPT还有哪些问题需要解决呢&#xff1f;…

《利用光体积描记图信号的模糊递推特性估计无袖带血压的级联卷积神经网络模型》阅读笔记

目录 一、论文摘要 二、论文十问 Q1&#xff1a;论文试图解决什么问题&#xff1f; Q2&#xff1a;这是否是一个新的问题&#xff1f; Q3&#xff1a;这篇文章要验证一个什么科学假设&#xff1f; Q4&#xff1a;有哪些相关研究&#xff1f;如何归类&#xff1f;谁是这一…

城市的智能进化,汇成数字中国的璀璨银河

著名城市规划理论家刘易斯芒福德在《城市发展史——起源、演变和前景》中提出&#xff0c;“城市从其起源时代开始便是一种特殊的构造&#xff0c;它专门用来储存并流传人类文明的成果。这种构造致密而紧凑&#xff0c;足以用最小的空间容纳更多的设施。” 他认为&#xff0c;城…

我们如何将 Amazon Snowcone 送入轨道

我从 4 或 5 岁起就一直是太空旅行和美国太空计划的粉丝。我记得读过关于水星和双子星座计划的文章&#xff0c;兴奋地看着鹰号登月舱降落在月球上。 如今&#xff0c;随着每次发射到达近地轨道 (LEO) 的成本似乎都在不断下降&#xff0c;因此有比以往任何时候都要更多的机会&…

建设元宇宙基础设施——PPIO边缘云在云渲染/云游戏的思考和实践

关于“元宇宙”的讨论越发火热&#xff0c;而建设元宇宙不可避免需要布设基础设施&#xff0c;LiveVideoStackCon 2022 北京站邀请到PPIO边缘云联合创始人——王闻宇&#xff0c;同大家探讨元宇宙网络时延的最优解–边缘云基础设施的架构与建设&#xff0c;并介绍PPIO边缘云在泛…

Kafka Broker是如何基于Reactor模式来处理海量用户请求的?

介绍 https://kafka.apache.org/0110/documentation.html 参数名描述默认值queued.max.requestsbroker全局唯一的请求队列&#xff0c;用来保存请求500num.io.threads用来处理请求的线程数8 参考博客 [1]

Python环境设置

在了解Python语言的历史和介绍之后&#xff0c;要想开始学习Python语言&#xff0c;还需要在计算机中安装Python编译环境&#xff0c;本期就来聊聊怎么在计算机中安装Python环境。 在计算机中添加Python环境的几种选择&#xff1a; 直接安装Python程序&#xff0c;即Python官…

Spring Boot配置文件

日升时奋斗&#xff0c;日落时自省 目录 1、配置文件作用 2、配置文件格式 2.1、使用注意 3、properties配置文件 3.1、注释中文问题 3.2、properties语法格式 3.3、读取配置文件 3.3.1、Value读取 3.3.2、PropertySource读取 3.3.3、原生方式读取配置文件 3.4、pr…

C++-FFmpeg-1-VS2019-x264-fdk_aac-x265-pdb-QT5.14-makefile

1.环境搭建&#xff1a; 1.1VS2019 用的是控制台编译。 1.2.msys2 模拟linux的命令和指令。 2.源码编译与安装&#xff1a; 2.1.x264: ffmpeg :编码用X264 2.2x265: ffmpeg :编码用X265 c写的。msys2编译。 2.3.fdk-aac 音频编码。 2.4 ffmpeg源码4.3: 2.5.SDL2.0 视频渲…

ESP8266_RTOS_SDK之SPIFFS

需要在ESP8266的FLASH中存储一些可变参数&#xff0c;有两种方式&#xff0c;一种是调用SPI Flash API直接指定地址读写FLASH&#xff1b;二是在SPI FLASH上创建一块SPIFFS 分区&#xff0c;以读写文件的形式存取数据。 下面记录第二种方式&#xff0c;使用SPIFFS文件系统存取…

干货 | Elasticsearch 8.X 性能优化实战

Elasticsearch 是实现用户无缝搜索体验的关键工具。它通过提供快速、准确和相关的搜索结果&#xff0c;彻底改变了用户与应用程序的互动方式。然而&#xff0c;要确保 Elasticsearch 部署达到最佳性能&#xff0c;就必须关注关键指标&#xff0c;并对诸如索引、缓存、查询、搜索…

【计算机图形学】课堂习题汇总

在直线的光栅化算法中&#xff0c;如果不考虑最大位移方向则可能得到怎样的直线&#xff1f; A&#xff1a;斜率为1的线 B&#xff1a;总是垂直的 C&#xff1a;离散的点&#xff0c;无法构成直线 D&#xff1a;总是水平的 在直线的改进的Bresenham算法中&#xff0c;每当误…

Qt音视频开发42-网络推流(视频推流/本地摄像头推流/桌面推流/网络摄像头转发推流等)

一、前言 上次实现的文件推流&#xff0c;尽管优点很多&#xff0c;但是只能对现在存在的生成好的音视频文件推流&#xff0c;而现在更多的场景是需要将实时的视频流重新推流分发&#xff0c;用户在很多设备比如手机/平板/网页/电脑/服务器上观看&#xff0c;这样就可以很方便…

IP-GUARD如何通过流量控制策略限制客户端下载文件?

如何通过流量控制策略限制客户端下载文件? 可通过流量控制策略限制接收流量上限速度,实现控制客户端下载文件效果。流量控制支持网络地址和端口范围限制。 网络流量统计能否基于用户进行统计? 目前最新的客户端版本已经支持控制应用程序的网络流量,在应用层实现了控制…

专利进阶(二):专利撰写常用技术及算法汇总(持续更新中)

文章目录 一、前言二、常用技术及算法2.1 区跨链技术2.2 聚类算法2.3 边缘算法2.4 蚁群算法2.4.1 路径构建2.4.2 信息素更新 2.5 哈希算法2.5.1 常见算法 2.6 数字摘要2.72.82.92.10 三、拓展阅读 一、前言 专利撰写过程中使用已有技术或算法解决新问题非常常见&#xff0c;本…

基于SpringBoot的冬奥会科普平台

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理平台应运而生&#xff0c;各行各业相继进入信息管理时代&…

【五一创作】Scratch资料

Scratch软件是免费的、免费的、免费的。任何需要花钱才能下载Scratch软件的全是骗子。 1、什么是Scratch Scratch是麻省理工学院的“终身幼儿园团队”开发的一种图形化编程工具。是面向青少年的一款模块化&#xff0c;积木化、可视化的编程语言。 什么是模块化、积木化&…

leetcode每日一题【7】

第一题&#xff1a;67. 二进制求和 给你两个二进制字符串 a 和 b &#xff0c;以二进制字符串的形式返回它们的和。示例 1&#xff1a;输入:a "11", b "1" 输出&#xff1a;"100" 示例 2&#xff1a;输入&#xff1a;a "1010", b …