IO Virtualization with Virtio.part 1 [十二]

久等了各位!

本篇开始讲解 IO 虚拟化中的 virtio,我会以 Linux 的 IIC 驱动为例,从 IIC 驱动的非虚拟化实现,到 IIC 驱动的半虚拟化实现,再到最后 X-Hyper 中如何通过 virtio 来实现前后端联系,一步步把 virtio 讲清楚。所以我一共会分为4个子篇幅来介绍virtio,内容有点多,需要一点点消化。

本篇我们以 RK 板的 IIC 控制器为例,先讲解 Linux 下的 IIC 驱动框架。

设备树创建 i2c 设备的节点,在设备树遍历时会创建一个 i2c 的 platform 设备出来:

    i2c@fdd40000 {
        compatible = "rockchip,rk3399-i2c";
        reg = <0x00 0xfdd40000 0x00 0x1000>;
        clocks = <0x32 0x07 0x32 0x2d>;
        clock-names = "i2c\0pclk";
        interrupts = <0x00 0x2e 0x04>;
        pinctrl-names = "default";
        pinctrl-0 = <0x35>;
        #address-cells = <0x01>;
        #size-cells = <0x00>;
        status = "okay";
        phandle = <0x17a>;
    }

设备包含如下信息:

  • compatible:兼容性,用于匹配可以用于该设备的驱动;
  • reg:该设备的寄存器基地址和范围;
  • interrupts:i2c 中断控制器使用的中断配置;

这个 platform device 在被放入平台总线时,会匹配对应的 platform driver,那么先来看一下 platform bus 是什么:

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.dma_configure	= platform_dma_configure,
	.pm		= &platform_dev_pm_ops,
};

我们只关注其中的 match 函数,它是当一个新的设备或者一个新的驱动被添加到该总线时会被调用的匹配函数,当一个新的设备被加入时,使用 match 来匹配对应的驱动,当一个新的驱动被添加到该总线时,也会使用这个 match 来匹配设备。

我们以 rk 板的 i2c 平台总线驱动为例,首先我们找到 rk 板的 i2c 平台总线驱动:

static struct platform_driver rk3x_i2c_driver = {
	.probe   = rk3x_i2c_probe,
	.remove  = rk3x_i2c_remove,
	.driver  = {
		.name  = "rk3x-i2c",
		.of_match_table = rk3x_i2c_match,
		.pm = &rk3x_i2c_pm_ops,
	},
};

使用of_match_table 来匹配对应的的设备,使用probe 来初始化设备。

整体流程如下:

在深入分析 IIC 的驱动代码前,先简单看一下 IIC 的整个数据发送和接受流程,这里不会涉及底层硬件的时序,需要读者自己去学习。

IIC 主机向从机写数据:

IIC 主机向从机读数据:

当然IIC的发送接收不止上述两种模式,这里只讨论常用的两种发送和接收数据的方法。

然后我们开始分析 IIC platform driver 的代码了:

我们以 RK board 为例,其总线驱动代码在:drivers\i2c\busses\i2c-rk3x.c 中,具体实现可以参考我之前的文章,这里只给出一个整体框图。最终 i2c 的控制器也会以字符设备节点暴露给用户态,我们可以通过 i2c 控制器的字符设备给相应的 i2c 外设通信。

然后看一下用户层打开一个 i2c 控制器对应的字符设备节点的整体流程图:

整个流程如下:

当应用程序打开一个设备文件时,通过系统调用 sys_open 进入内核,在内核空间中由 do_sys_open 负责发起整个设备文件的打开操作,首先获得该设备文件所对应的 inode,然后调用其中的 i_fop 函数,对字符设备而言,i_fop 函数就是 chrdev_open,后者通过 inode 中的 i_rdev 成员在 cdev_map 中查找该设备所对应的设备对象 cdev,在成功找到了该设备对象后,将 inode 的 cdev 成员指向该字符设备对象,这样下次再对该设备文件节点进行打开操作时,就可以直接通过 i_cdev 成员得到设备节点所对应的字符设备对象了。内核在每次打开一个设备文件时,会产生一个整形的文件描述符 fd 和一个新的 struct file 对象 filp 来跟踪对该文件的这一次操作,在打开设备文件时,内核会将 filp 和 fd 关联起来,同时会将 cdev 中的 ops 赋值给 filp->f_op,同时创建 i2c_client,关联 i2c_adapter,并将 filp 的 private_data 和 i2_client 关联起来。最后 sys_open 系统调用将设备文件描述符 fd 返回到用户空间。

接下来用一个实际的例子去理解一下 IIC 的完成一次 Combined R/W 的流程:

用户侧示例代码如下:

int main(void)
{
    int fd = 0;
    int ret = 0;
    const char *path_name ="/dev/i2c-0";

    uint8_t buf[8] = {0};
    uint8_t start_reg = 0x0;
    struct i2c_msg read_msg[2] = {
        {
            0x20,               /* slave addr */
            0,                  /* operate flags */
            1,                  /* data len */
            &start_reg          /* data buf */
        },
        {
            0x20,               /* slave addr */
            I2C_M_RD,           /* operate flags */
            8,                  /* data len */
            &buf[0]   			/* data buf */
        },
    };

    struct i2c_rdwr_ioctl_data rdwr = {
        .msgs = read_msg,
        .nmsgs = 2
    };

    fd  = open(path_name, O_RDWR);
    ret = ioctl(fd, I2C_SLAVE_FORCE, 0x20);
    ret = ioctl(fd, I2C_RDWR, (unsigned long)&rdwr);
    return 0;
}

这是一次 Combined 的从机数据读取操作,由两部分i2c_msg 组成,第一个 msg 向从机写设备寄存器地址,表示要读取的设备寄存器,然后再发送读数据请求,向从机请求 8 个字节的 data。

对应整个 IIC 的协议段如下:

然后我们通过 ioctl 进入内核,并最终调用 i2cdev_fops->i2cdev_ioctl。

static const struct file_operations i2cdev_fops = {
	...
    .compat_ioctl	= compat_i2cdev_ioctl,
    ...
};

static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct i2c_client *client = file->private_data;
    ...
	switch (cmd) {
    ...
	case I2C_RDWR: {
        ...
		return i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa);
	}
    ...
	return 0;
}

static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client,
		unsigned nmsgs, struct i2c_msg *msgs)
{
    ...
	res = i2c_transfer(client->adapter, msgs, nmsgs);
    ...
	return res;
}

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
    ...
	ret = __i2c_transfer(adap, msgs, num);
	return ret;
}

int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
    ...
	ret = adap->algo->master_xfer(adap, msgs, num);
    ...
	return ret;
}

可以看到最后就是调用了i2c_adapter 中的master_xfer。

static int rk3x_i2c_xfer(struct i2c_adapter *adap,
			 struct i2c_msg *msgs, int num)
{
	struct rk3x_i2c *i2c = (struct rk3x_i2c *)adap->algo_data;
    ...
	for (i = 0; i < num; i += ret) {
		ret = rk3x_i2c_setup(i2c, msgs + i, num - i);
        ...
		rk3x_i2c_start(i2c);
        ...
	}
    ...
}

rk3x_i2c_xfer 中初始化MRXADDR 和 MRXRADDR 寄存器并初始化 i2c 的初始化状态机状态。然后通过rk3x_i2c_start 发送 start 信号开始 i2c 的整个流程,并在rk3x_i2c_irq 中维护整个 i2c 数据发送和接受的状态机:

static irqreturn_t rk3x_i2c_irq(int irqno, void *dev_id)
{
    ...
	switch (i2c->state) {
	case STATE_START:
		rk3x_i2c_handle_start(i2c, ipd);
		break;
	case STATE_WRITE:
		rk3x_i2c_handle_write(i2c, ipd);
		break;
	case STATE_READ:
		rk3x_i2c_handle_read(i2c, ipd);
		break;
	case STATE_STOP:
		rk3x_i2c_handle_stop(i2c, ipd);
		break;
	case STATE_IDLE:
		break;
	}
    ...
}

在 Combined W/R 模式下的流程图和状态机如下所示:

流程图:

IIC 驱动状态机:

到这里我们简单的介绍了Linux下的IIC驱动框架和其工作流程,如果要深入理解还需自己阅读驱动代码,下一篇幅将介绍IIC半虚拟化的Virtio前端驱动。

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

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

相关文章

ITOM系统在IT运维中的作用

在企业IT运维中&#xff0c;ITOM系统&#xff08;IT运营管理&#xff09;通过整合、自动化和监控&#xff0c;能够帮助企业有效管理IT基础设施&#xff0c;提高运维效率和系统的可靠性。本文将从多个角度探讨ITOM系统在IT运维中的作用及其功能。 提升运维效率&#xff1a;统一平…

水上救命稻草,充气救生板的关键救援效能|深圳鼎跃

当今社会&#xff0c;突发事件如影随形&#xff0c;其诱因常隐匿于生活细微之处。就像我们在户外游玩时&#xff0c;水域周边看似风平浪静&#xff0c;实则暗藏危机&#xff0c;稍有疏忽便可能失足落水。此类意外的发生毫无征兆&#xff0c;往往令人在瞬间陷入危险境地&#xf…

Mac M2 Pro安装MySQL 8.4.3

絮絮叨叨 MacBook Pro&#xff0c;芯片&#xff1a;Apple M2 Pro, macOS: Sonoma 14.0一直知道很多软件对Mac M1或M2的支持不好&#xff0c;但没想到在安装MySQL 8.x上也让我吃尽了苦头本文除了介绍如何安装MySQL 8.4.3外&#xff0c;还会记录笔者遇到的一些问题以及解决方法 …

Leecode刷题C语言之切蛋糕的最小总开销②

执行结果:通过 执行用时和内存消耗如下&#xff1a; int compare(const void* a, const void* b) {return (*(int*)b - *(int*)a); }long long minimumCost(int m, int n, int* horizontalCut, int horizontalCutSize, int* verticalCut, int verticalCutSize) {qsort(horizon…

FreeRTOS的内存管理(选择heap4.c文件的理由)

目录 1. 了解FreeRTOS内存管理 2. 了解内存碎片 3.了解各个heap.c的内存分配方法 1.heap1.c 2.heap2.c 3.heap3.c 4.heap4.c 5.heap5.c 总结&#xff1a; 内存管理是一个系统基本组成部分&#xff0c;FreeRTOS 中大量使用到了内存管理&#xff0c;比如创建任务、信号量…

[服务器][教程]Ubuntu24.04 Server开机自动挂载硬盘教程

1. 查看硬盘ID ls -l /dev/disk/by-uuid可以看到对应的UUID所对应的分区 2. 创建挂载文件夹 创建好文件夹即可 3. 修改配置文件 sudo vim /etc/fstab把对应的UUID和创建的挂载目录对应即可 其中# Personal mount points下面的是自己新添加的 &#xff1a;分区定位&#xff…

Python用K-Means均值聚类、LRFMC模型对航空公司客户数据价值可视化分析指标应用|数据分享...

全文链接&#xff1a;https://tecdat.cn/?p38708 分析师&#xff1a;Yuling Fang 信息时代的来临使得企业营销焦点从产品中心转向客户中心&#xff0c;客户关系管理成为企业的核心问题&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 客户关系管理的关键是客…

HTML——46.制作课程表

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>课程表</title></head><body><h3>课程表</h3><table border"1" cellspacing"0"><tr><th colspan"…

强化学习(1)

Reinforcement Learning Goal-directed learing from ineraction with the environment. 1. Basic Element 基本元素 1.1 Agent 玩家 1.2 Environment 1.3 Goal 2. Main Element 主要元素 2.1 State 2.2 Action 状态与行为往复 2.3 Reward 目标&#xff1a;最大化总…

《代码随想录》Day21打卡!

写在前面&#xff1a;祝大家新年快乐&#xff01;&#xff01;&#xff01;2025年快乐&#xff0c;2024年拜拜~~~ 《代码随想录》二叉树&#xff1a;修剪二叉搜索树 本题的完整题目如下&#xff1a; 本题的完整思路如下&#xff1a; 1.本题使用递归进行求解&#xff0c;所以分…

iOS 中的 nil、Nil、NULL、NSNull 僵尸对象和野指针

iOS 中的 nil、Nil、NULL、NSNull 僵尸对象和野指针-CSDN博客 类型含义使用场景示例nil表示一个指向 Objective - C 对象的空指针。在 Objective - C 和 Swift&#xff08;与 Objective - C 交互时&#xff09;中用于表示对象不存在。当一个对象变量没有指向任何有效的对象实例…

CPT203 Software Engineering 软件工程 Pt.6 软件管理(中英双语)

文章目录 10. Project Management&#xff08;项目管理&#xff09;10.1 Project Management Overview10.1.1 Project Management Importance&#xff08;项目管理的重要性&#xff09;10.1.2 Criteria for Project Management&#xff08;项目管理的准则&#xff09;10.1.3 Ch…

Java [后端] 开发日常记录(1)

目录 1、常用的注解 2、对字符串的处理 3、对JSON串的处理 -- The End -- 详细如下&#xff1a; 1、常用的注解 若返回的字段中有NUll&#xff0c;则不返回 JsonInclude(value JsonInclude.Include.NON_NULL) //在实体类中添加这个注解 JsonInclude(JsonInclude.Include.NON…

流计算需要框架吗?SPL 可能是更好的选择

流数据源通常是动态、无界的&#xff0c;看起来与静态、有限的批数据源区别较大&#xff0c;传统的数据库技术在架构上难以直接处理流数据源&#xff0c;只能让位于后来者。heron\samza\storm\spark\flink等计算框架最先完成突破&#xff0c;在流计算技术中占得先发优势。这些框…

设计模式の状态策略责任链模式

文章目录 前言一、状态模式二、策略模式三、责任链模式 前言 本篇是关于设计模式中的状态模式、策略模式、以及责任链模式的学习笔记。 一、状态模式 状态模式是一种行为设计模式&#xff0c;核心思想在于&#xff0c;使某个对象在其内部状态改变时&#xff0c;改变该对象的行为…

鸿蒙UI开发——使用WidthTheme实现局部深浅色

1、场景描述 在实际的应用开发中&#xff0c;我们可能需要在界面中局部应用深色或者浅色的界面样式&#xff0c;与全局的深色、亮色同时生效。场景例如&#xff1a;深/亮色预览。此时&#xff0c;我们可以使用WithTheme能力来达到我们的效果。 2、WithTheme WithTheme组件可…

20241231取消掉夸克浏览器为默认浏览器

20241231取消掉夸克浏览器为默认浏览器 2024/12/31 17:59 因为有些资源必须用夸克网盘下载&#xff01;^_ 地区特色问题。对于百度网盘&#xff0c;如果你分享BBC的纪录片合集&#xff0c;马上给你无效掉&#xff01;^_ 但是夸克有一点夜郎自大了&#xff0c;把客户的默认浏览器…

详细教程:SQL2008数据库备份与还原全流程!

数据的安全性至关重要&#xff0c;无论是操作系统、重要文件、磁盘存储&#xff0c;还是企业数据库&#xff0c;备份都是保障其安全和完整性的关键手段。拥有备份意味着即使发生误删、系统崩溃或病毒攻击等问题&#xff0c;也能迅速通过恢复功能解决&#xff0c;避免数据丢失带…

一、Hadoop概述

文章目录 一、Hadoop是什么二、Hadoop发展历史三、Hadoop三大发行版本1. Apache Hadoop2. Cloudera Hadoop3. Hortonworks Hadoop 四、Hadoop优势1. 高可靠性2. 高扩展性3. 高效性4. 高容错性 五、Hadoop 组成1. Hadoop1.x、2.x、3.x区别2. HDFS 架构概述3. YARN 架构概述4. Ma…

docker-开源nocodb,使用已有数据库

使用已有数据库 创建本地数据库 数据库&#xff1a;nocodb 用户&#xff1a;nocodb 密码&#xff1a;xxxxxx修改docker-compose.yml 默认网关的 IP 地址是 172.17.0.1&#xff08;适用于 bridge 网络模式&#xff09;version: "2.1" services:nocodb:environment:…