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

文章目录

  • 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代码下载地址如下:
https://download.csdn.net/download/ZHONGCAI0901/87741928

5. 参考资料

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何编写高质量代码、提高编程效率?

一、 前言 高质量代码是指在满足功能需求的基础上&#xff0c;具备高性能、安全、可扩展、易维护、可测试等特点的代码。它不仅可以提高开发效率和代码质量&#xff0c;更能有效减少代码维护成本&#xff0c;促进团队协作和项目成功。因此&#xff0c;编写高质量代码对程序员来…

【Java】什么是SOA架构?与微服务有什么关系?

文章目录 服务化架构微服务架构 我的一个微服务项目&#xff0c;有兴趣可以一起做 服务化架构 我们知道&#xff0c;早期的项目&#xff0c;我们都是把前后端的代码放在同一个项目中&#xff0c;然后直接打包运行这个项目&#xff0c;这种项目我们称之为单体项目&#xff0c;比…

【Vue】Vue-cli,创建项目设置自定义默认配置

Vue2.0&#xff0c;Vue-cli项目配置 步骤一&#xff0c;打开文件夹&#xff0c;导航栏输入cmd&#xff0c;打开命令行窗口步骤二&#xff0c;输入命令步骤三&#xff0c;选择第三个自定义新建项目步骤四&#xff0c;选择需要的项目模块&#xff0c;空格选择完&#xff0c;回车步…

密码学|AES加密算法|学习记录

AES简介 AES加密是分组加密的一种 明文长度为固定的128位 密钥可长度为128&#xff0c;192&#xff0c;256位 128bit16字节&#xff0c;在AES中我们将数据用4x4字节的矩阵表示。&#xff08;注排列顺序为先从上到下再从左到右&#xff09; AES的一般步骤 对于上图最终轮区…

CASAIM高精度自动化三维扫描系统检测塑料件,自动检测形位公差

随着塑料工业的迅速发展&#xff0c;以及塑料制品在航空、航天、电子、机械、船舶和汽车等工业部门的推广应用&#xff0c;对塑料件的质量要求也越来越高。 为了检测塑料件的尺寸偏差以及测量关键部位的3D尺寸和形位公差&#xff0c;对影响总成零件精度的产品、工装、工艺进行精…

第十章_Redis集群(cluster)

是什么 定义 由于数据量过大&#xff0c;单个Master复制集难以承担&#xff0c;因此需要对多个复制集进行集群&#xff0c;形成水平扩展每个复制集只负责存储整个数据集的一部分&#xff0c;这就是Redis的集群&#xff0c;其作用是提供在多个Redis节点间共享数据的程序集。 官…

yield用法理解,配有代码块和解析

包含 yield 关键字的函数&#xff0c;是一个生成器 yield和return的区别 1、return是返回return关键字的值&#xff0c;被调用一次就返回一次&#xff0c;return只能放在一个函数代码块的最后面&#xff0c;运行到return的时候&#xff0c;就结束循环&#xff0c;结束这个函数…

用户画像系列——HBase 在画像标签过期策略中的应用

一、背景 前面系列文章介绍了用户画像的概念、用户画像的标签加工、用户画像的应用。本篇文章主要介绍一些画像的技术细节&#xff0c;让大家更加详细的了解画像数据存储和处理的逻辑 举个现实中的例子&#xff1a; 例子1&#xff1a;因为疫情原因&#xff0c;上线一个平台(…

没有U盘电脑如何使用本地硬盘安装Ubuntu20.04(双系统)

环境: DELL7080台式机 Ubuntu20.04 两块硬盘 问题描述: 没有U盘电脑如何使用本地硬盘安装Ubuntu20.04(双系统) 解决方案: 一、下载镜像文件 1.上线自行下载安装镜像文件 二、分区 1.win10下磁盘管理压缩2个分区一个10G左右制作安装盘,一个几百G安装系统使用 10…

辞了外包,上岸字节我落泪了,400多个日夜没人知道我付出了多少....

前言&#xff1a; 没有绝对的天才&#xff0c;只有持续不断的付出。对于我们每一个平凡人来说&#xff0c;改变命运只能依靠努力幸运&#xff0c;但如果你不够幸运&#xff0c;那就只能拉高努力的占比。 2023年3月&#xff0c;我有幸成为了字节跳动的一名自动化测试工程师&am…

Node.js的简介

一、什么是node.js Node.js是JavaScript语言的服务器运行环境。 Node.js 就是运行在服务端的 JavaScript。 Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。 Node.js是一个事件驱动I/O服务端JavaScript环境&#xff0c;基于Google的V8引擎&#xff0c;V8引擎执行…

基于 SpringBoot+Vue+Java 的留守儿童系统的研究与实现(附源码,教程)

文章目录 1.研究背景2. 技术栈3.系统分析4系统设计5系统的详细设计与实现5.1系统功能模块5.2管理员功能模块 1.研究背景 以往的留守儿童爱心的管理&#xff0c;一般都是纸质文件来管理留守儿童爱心信息&#xff0c;传统的管理方式已经无法满足现代人们的需求&#xff1b;使用留…

PCL学习二:PCL基础应用教程

参考引用 PCL Basic UsagePCL 点云库官网教程 1. pcl_viewer 基本使用 1.1 pcl_viewer 安装测试 pcl_data 源码克隆$ git clone https://github.com/PointCloudLibrary/data.git进入 /pcl_data/tutorials&#xff08;如下图&#xff09;$ cd ~/pcl_data/tutorials # 此处为重…

Chapter 4 :Constraining I/O Delay(ug903)

4.1 About Constraining I/O Delay 要在设计中准确地建模外部时序上下文&#xff0c;必须为输入和输出端口提供时序信息。由于XilinxVivado集成设计环境&#xff08;IDE&#xff09;只能识别FPGA边界内的时序&#xff0c;因此必须使用以下命令来指定超出这些边界的延迟…

200人 500人 园区网设计

实验要求&#xff1a; ① 设置合理的STP优先级、边缘端口、Eth-trunk ② 企业内网划分多个vlan &#xff0c;减少广播域大小&#xff0c;提高网络稳定性 ③ 所有设备&#xff0c;在任何位置都可以telnet远程管理 ④ 出口配置NAT ⑤ 所有用户均为自动获取ip地址 ⑥ 在企业…

Matlab高光谱遥感、数据处理与混合像元分解及典型案例

站在学员的角度去理解“高光谱”&#xff0c;用大家能听的懂的语言去讲述高光谱的基本概念和理论&#xff0c;帮助学员深入理解这项技术的底层科学机理。方法篇&#xff0c;将高光谱技术与MATLAB工具结合起来&#xff0c;采用MATLAB丰富的工具箱&#xff0c;快速复现高光谱数据…

Java 重写(Override)与重载(Overload)

重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变&#xff0c;核心重写&#xff01; 重写的好处在于子类可以根据需要&#xff0c;定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。 重写方法不…

【MySQL自学之路】第4天——模式、表、视图、索引(数据定义详细版)

目录 前言 数据库 数据库的建立 数据库的使用 数据库的查看 数据库的删除 模式 查看所有的模式 模式和数据库之间的关系 ​编辑建立模式 删除模式 表 数据类型 查看一个数据库下面的所有表(必须进入要查看的数据库) 创建基本表 查看表结构&#xff08;查看表建…

Arduino学习笔记5

一.直流电机控制实验 1.源代码 int dianJiPin9;//定义数字9接口接电机驱动IN1的控制口void setup() {pinMode(dianJiPin,OUTPUT);//定义电机驱动IN1的控制口为输出接口 } void loop() {digitalWrite(dianJiPin,LOW);//关闭电机delay(1000);//延时digitalWrite(dianJiPin,HIGH…

【Vue 基础】vue-cli初始化项目及相关说明

目录 1. 创建项目 2. 项目文件介绍 3. 项目的其它配置 3.1 项目运行时&#xff0c;让浏览器自动打开 3.2 关闭eslint校验功能 3.3 src文件夹简写方法 1. 创建项目 vue create 项目名 2. 项目文件介绍 创建好的项目中包含如下文件&#xff1a; &#xff08;1&#xff09…