10_7iic整体框架流程

在内核中 这边把iic整个流程分成了 4层
iic_dtiver at24_iic_eeprom 也就是我们的自己的驱动
i2c-core.c 核心层
i2c/busses/i2c-s3c2410.c 控制器层
平台总线驱动层,或者也是图中的设备树

在这里插入图片描述

硬件描述

我们假设 板子上有三个iic控制器 0 1 2
这里在控制器0 上挂载了gt24c02的eeprom 和一个触摸屏
控制器1 没有挂东西
控制器2 挂了一个触摸屏

第一步,找到iic控制器往平台总线丢

如果是老的内核,开机自启的时候会注册各种控制器的平台总线dev
新的内核就是用设备树 往平台总线上面注册pdev
相似的 这里iic控制器加入到 平台总线中

smdkv210_machine_init()
	i2c_register_board_info(0, smdkv210_i2c_devs0,ARRAY_SIZE(smdkv210_i2c_devs0));
		 i2c_board_info smdkv210_i2c_devs0[] __initdata = {
				{ I2C_BOARD_INFO("at24c02a", 0x50),}, //总线下挂的iic_dev 名字用于进行匹配, 0x50表示从设备地址
				{ I2C_BOARD_INFO("wm8580", 0x1b), },};
				for (status = 0; len; len--, info++)//遍历数组,上面这个数组就有两个设备
					struct i2c_devinfo	*devinfo;
					//主要代码,给每个当前适配器下面的 iic从设备构建devinfo结构体
					devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
					devinfo->busnum = busnum;//表示总线编号--0, 1,2
					devinfo->board_info = *info;//将数组中成员赋值给节点
					list_add_tail(&devinfo->list,& i2c_board_list);  //把devinfo结构体注册到链表i2c_board_list
	i2c_register_board_info(1, smdkv210_i2c_devs1,ARRAY_SIZE(smdkv210_i2c_devs1));
		.name =  "samsung,s3c2410-i2c"
	i2c_register_board_info(2, smdkv210_i2c_devs2,	ARRAY_SIZE(smdkv210_i2c_devs2));
	platform_add_device(&smdkv210_devs,ARRAY_SIZE(smdkv210_devs)); //这里注册各种板子的控制器到平台总线
		smdkv210_i2c_devs1 //看来没有设备树的时候,各种控制器的dev都是在init的时候丢进,plantform中的pdev链表
		smdkv210_dm9000   //就是pdev,硬件资源信息
		smdkv210_fb   //就是pdev,硬件资源信息

这里得到一个i2c_board_list链表 有当前各个从iic设备的信息
同时在平台总线上注册了 三个pdev 对应的是三个iic控制器

第二步,创建iic总线

进入iic核心层
drivers/i2c/i2c-core-base.c
这里很简单就是构造了iic总线,提供了一些匹配规则
让iic控制器和iic驱动进行匹配

postcore_initcall(i2c_init);  //编译到内核,自动注册这个驱动
	static int __init i2c_init(void)
		retval = bus_register(&i2c_bus_type);//core中注册了iic总线,这个函数自动构建出一个dev链表一个drv链表
		retval = i2c_add_driver(&dummy_driver); //总线中增加了驱动-dummy_driver
			static struct i2c_driver dummy_driver = {  //这个driver只是一个模板,教你iic驱动怎么写
				.driver.name	= "dummy",
				.probe		= dummy_probe,
				.remove		= dummy_remove,
				}

note: 总线注册bus_register()
设备注册 device_regisiter(结构体)//结构体里有个属性表示自己是哪个总线的
驱动注册 driver_regisiter(结构体)//结构体里有个属性表示自己是哪个总线的

第三步平台总线匹配

iic 控制器层
i2c/busses/i2c-s3c2410.c
需要把自己控制器drv 丢到平台总线的pdrv 链表中

subsys_initcall(i2c_adap_s3c_init);  //编译到内核,自动注册这个驱动
	i2c_adap_s3c_init(void)
		platform_driver_register(&s3c24xx_i2c_driver); //注册这个平台驱动
			.of_match_table = of_match_ptr(s3c24xx_i2c_match),
				.name =  "samsung,s3c2410-i2c" //可以匹配上面的三个pdev
				.probe		= s3c24xx_i2c_probe,}

第四步,控制器层的probe调用,注册iic控制器到iic_dev链表中

这里匹配成功,创建了图中的iic适配器结构体
里面有相应的算法和,对这个iic控制器的操作方法 同时把这个iic_adap 要注册到 iic_core的 iic_dev链表

s3c24xx_i2c_probe(struct platform_device *pdev)
	struct s3c24xx_i2c *i2c; //创建了一个iic全局变量
	i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL);
	//对这个iic的全局变量初始化 i2c->adap 适配器和算法
	i2c->adap.owner = THIS_MODULE;
	i2c->adap.algo = &s3c24xx_i2c_algorithm;  //指定了算法
		.master_xfer		= s3c24xx_i2c_xfer, //算法里有每个iic_控制器的操作方法
	i2c->adap.retries = 2;
	i2c->adap.class = I2C_CLASS_DEPRECATED;
	i2c->tx_setup = 50;
	i2c_add_adapter(struct i2c_adapter *adapter);


因为构建了 iic_adap 所以iic_adap 也有编号
iic_adap 要注册到 core的 iic_dev链表

第五步,控制器层的probe调用,遍历适配器下面的从设备

i2c_add_adapter(struct i2c_adapter *adapter) 继续上面的函数
这个函数不仅注册iic控制器到iic_dev链表中
在里面还把 这个控制器下面的 iic从设备初始化为client结构体
并且把这个client结构体的父类dev 注册到iic_dev链表中

i2c_add_adapter(struct i2c_adapter *adapter)	
	i2c_register_adapter(adapter);
		dev_set_name(&adap->dev, "i2c-%d", adap->nr); //设置里面dev的名字叫i2c-0
		adap->dev.bus = &i2c_bus_type; //这个dev是要放入,iicbus总线的
		adap->dev.type = &i2c_adapter_type;
		res = device_register(&adap->dev); //注册这个adap到iic总线
		i2c_scan_static_board_info(adap);
			struct i2c_devinfo	*devinfo;//构建devinfo结构体
			list_for_each_entry(devinfo, &__i2c_board_list, list) //i2c_board_list链表找到 这个适配器下的iic从设备
				devinfo->busnum == adapter->nr && !i2c_new_device(adapter,&devinfo->board_info)
					client = kzalloc(sizeof *client, GFP_KERNEL);//创建一个client
					client->adapter = adap;//链接这个client对应的适配器
					client->addr = info->addr; //拿到了i2c_board_info 里面的从设备地址
					i2c_dev_set_name(adap, client, info); //设置 这个dev在总线的名字 /sys/bus/i2c/devices/0-0050   
					client->dev.bus = &i2c_bus_type; //这个client的总线是iic总线
					dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),i2c_encode_flags_to_addr(client));  //名字是适配器编号-i2c设备的地址
					status = device_register(&client->dev); //从设备也注册到iic总线

第六步,构建iic从设备driver,这个要自己写

把自己的写的iic_drv 放入iic总线中,用于匹配 iic_dev
从而调用drv的probe函数

struct i2c_driver at24_drv = {
	.probe = at24_drv_probe,
	.remove = at24_drv_remove,
	.id_table = at24_id_table,//用来比对name符合匹配规则
};
module_init(at24_drv_init);
	static int __init at24_drv_init(void)
		return i2c_add_driver(&at24_drv);//注册到iic_drv总线中用于匹配
		

第七步,构建iic从设备driver,这个要自己写,里面的probe函数开始启动

自己写的驱动弄一个结构体at24_dev,记录一些信息
比如图中的字符设备驱动的结构体dev 类 主次设备号等
还有对应的client

	at24_drv_probe(struct i2c_client *client, const struct i2c_device_id *id);
		at24_dev = kzalloc(sizeof(struct i2c_e2prom), GFP_KERNEL); 		//自己写的驱动弄一个结构体at24_dev,记录一些信息	
		at24_dev->dev_major = register_chrdev(0,"at24_drv", &at24_fops); //创建了字符设备驱动,注册fops
		at24_dev->cls = class_create(THIS_MODULE, "at24_cls");   //自己写的驱动创建了一个类
		at24_dev->dev = device_create(at24_dev->cls, NULL,MKDEV(at24_dev->dev_major, 0),NULL, "at24_e2prom"); //注册字符设备驱动
		//记录当前client
		at24_dev->client = client;
		// 硬件初始化 ---e2prom只要上电就可以功能

第八步应用程序使用read/write读取写iic数据

把发过来的数据组成数据包,进行发送给这个iic适配器

.write = at24_e2prom_drv_write //fops中的wirte函数
	at24_e2prom_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)	
		char *tmp = kzalloc(count, GFP_KERNEL);//分配结构体读取用户空间写下来的数据
		ret = copy_from_user(tmp, buf, count);
		ret = at24_i2c_write(at24_dev->client, tmp, count);
			struct i2c_adapter  *adapter = client->adapter; //拿到适配器
			struct i2c_msg msg;  //组一个iic数据包
			msg.addr = client->addr; //对应的iic从设备地址
			msg.flags = 0;
			msg.len = size;
			msg.buf = buf;
			ret = i2c_transfer(adapter, &msg, 1);//发送数据

自己写的驱动代码



#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/i2c.h>

#include <asm/io.h>
#include <asm/uaccess.h>




//  全局的设备对象
struct i2c_e2prom{
	int dev_major;
	struct class *cls;
	struct device *dev;
	struct i2c_client *client;//记录当前匹配的client
};

struct i2c_e2prom *at24_dev;

//编写一个类似i2c_master_recv/i2c_master_send
int at24_i2c_read(struct i2c_client *client, char *buf, int size)
{
	int ret;
	struct i2c_adapter  *adapter = client->adapter;

	struct i2c_msg msg;
	msg.addr = client->addr;
	msg.flags = I2C_M_RD;
	msg.len = size;
	msg.buf = buf;
	// 参数1---适配器
	//参数2--消息包
	// 参数3--消息的个数
	ret = i2c_transfer(adapter, &msg, 1);


	return ret==1?size:ret;
}


int at24_i2c_write(struct i2c_client *client, char *buf, int size)
{

	int ret;
	struct i2c_adapter  *adapter = client->adapter;

	struct i2c_msg msg;
	msg.addr = client->addr;
	msg.flags = 0;
	msg.len = size;
	msg.buf = buf;
	
	ret = i2c_transfer(adapter, &msg, 1);

	return ret==1?size:ret;

}

int at24_e2prom_drv_open (struct inode *inode, struct file *filp)
{

	return 0;
}

ssize_t at24_e2prom_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
{
	printk("-----------%s-----------\n", __FUNCTION__);
	int ret;
	
	if(count < 0 || count > 256)
		return -EINVAL;

	char *tmp = kzalloc(count, GFP_KERNEL);
	

	// 1, 从硬件中获取数据
	ret = at24_i2c_read(at24_dev->client, tmp, count);
	if(ret < 0)
	{
		printk("at24_i2c_read error\n");
		goto err_free;
	}

	// 2 ,将数据给用户
	ret = copy_to_user(buf, tmp, count);
	if(ret > 0)
	{
		printk("copy_to_user error\n");
		goto err_free;
	}


	kfree(tmp);
	
	return count;


err_free:
	kfree(tmp);
	return ret;


}

ssize_t at24_e2prom_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
{
	printk("-----------%s-----------\n", __FUNCTION__);
	int ret;
	
	if(count < 0 || count > 256)
		return -EINVAL;

	char *tmp = kzalloc(count, GFP_KERNEL);
	
	// 1, 从用户空间将数据获取到
	ret = copy_from_user(tmp, buf, count);
	if(ret > 0)
	{
		printk("copy_from_user error\n");
		goto err_free;
	}
	// 2,  将数据写入硬件中去
	ret = at24_i2c_write(at24_dev->client, tmp, count);
	if(ret < 0)
	{
		printk("at24_i2c_write error\n");
		goto err_free;
	}


	kfree(tmp);
	
	return count;


err_free:
	kfree(tmp);
	return ret;


}

int at24_e2prom_drv_close(struct inode *inode, struct file *filp)
{

	return 0;
}

const struct file_operations at24_fops = {
	.open = at24_e2prom_drv_open,
	.read = at24_e2prom_drv_read,
	.write = at24_e2prom_drv_write,
	.release = at24_e2prom_drv_close,

};


int at24_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	printk("-----id->name = %s, id->driver_data = 0x%x\n",id->name, id->driver_data);
	/*
			// 申请设备号

			// 创建设备文件

			// 硬件初始化

			// 实现fops
			

	*/
	at24_dev = kzalloc(sizeof(struct i2c_e2prom), GFP_KERNEL);

	at24_dev->dev_major = register_chrdev(0,"at24_drv", &at24_fops);

	at24_dev->cls = class_create(THIS_MODULE, "at24_cls");

	at24_dev->dev = device_create(at24_dev->cls, NULL,
				MKDEV(at24_dev->dev_major, 0),NULL, "at24_e2prom");

	//记录当前client
	at24_dev->client = client;
	
	// 硬件初始化 ---e2prom只要上电就可以功能

	// i2c系统中为从设备传输数据的方法

	
	return 0;

}


int at24_drv_remove(struct i2c_client *client)
{

	device_destroy(at24_dev->cls, MKDEV(at24_dev->dev_major, 0));
	class_destroy(at24_dev->cls );
	unregister_chrdev(at24_dev->dev_major, "at24_drv");
	kfree(at24_dev);
	return 0;
}
			

const struct i2c_device_id at24_id_table[] = {
		{"at24c02a", 0x2222},
		{"at24c04a", 0x4444},
		{"at24c08a", 0x8888},
};

struct i2c_driver at24_drv = {
	.probe = at24_drv_probe,
	.remove = at24_drv_remove,
	.driver = {
		.name = "at24_e2prom_drv", //不会用于比对
					// /sys/bus/i2c/drivers/at24_e2prom_drv

	},

	.id_table = at24_id_table,
};


static int __init at24_drv_init(void)
{

	//注册一个i2c driver
	return i2c_add_driver(&at24_drv);

}

static void __exit at24_drv_exit(void)
{
	 i2c_del_driver(&at24_drv);
}

module_init(at24_drv_init);
module_exit(at24_drv_exit);
MODULE_LICENSE("GPL");


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

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

相关文章

Telesquare TLR-2005Ksh 路由器 RCE漏洞复现

0x01 产品简介 Telesquare Tlr-2005Ksh是韩国Telesquare公司的一款 Sk 电讯 Lte 路由器。 0x02 漏洞概述 Telesquare TLR-2005Ksh存在安全漏洞&#xff0c;未经授权的攻击者可通过setSyncTimeHost执行任意命令获取服务器权限。 0x03 复现环境 FOFA&#xff1a;app"TELE…

SCI一区级 | Matlab实现GWO-CNN-LSTM-selfAttention多变量多步时间序列预测

SCI一区级 | Matlab实现GWO-CNN-LSTM-selfAttention多变量多步时间序列预测 目录 SCI一区级 | Matlab实现GWO-CNN-LSTM-selfAttention多变量多步时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现GWO-CNN-LSTM-selfAttention灰狼算法优化卷积长短…

USB总线-Linux内核USB3.0 Hub驱动分析(十四)

1.概述 USB Hub提供了连接USB主机和USB设备的电气接口。USB Hub拥有一个上行口&#xff0c;至少一个下行口&#xff0c;上行口连接上一级的Hub的下行口或者USB主机&#xff0c;连接主机的为Root Hub&#xff0c;下行口连接下一级Hub的上行口或者USB设备。经过Hub的扩展&#x…

一、Spring_IOCDI(1)

&#x1f33b;&#x1f33b; 目录 一、前提介绍1.1 为什么要学?1.2 学什么?1.3 怎么学? 二、Spring相关概念2.1 初始Spring2.1.1 Spring家族2.1.2 了解 Spring 发展史 2.2 Spring系统架构2.2.1 系统架构图2.2.2 课程学习路线 2.3 Spring核心概念2.3.1 目前项目中的问题2.3.2…

C#,《小白学程序》第二十三课:大数的除法(BigInteger Divide)

1 文本格式 /// <summary> /// 比较a&#xff0c;b的大小&#xff0c;返回1&#xff0c;0&#xff0c;-1 /// 数据从低位&#xff08;右&#xff09;往高位&#xff08;左&#xff09;存储; /// </summary> /// <param name"a"></param> ///…

【古诗生成AI实战】之四——模型包装器与模型的训练

在上一篇博客中&#xff0c;我们已经利用任务加载器task成功地从数据集文件中加载了文本数据&#xff0c;并通过预处理器processor构建了词典和编码器。在这一过程中&#xff0c;我们还完成了词向量的提取。 接下来的步骤涉及到定义模型、加载数据&#xff0c;并开始训练过程。…

kubernetes架构及核心组件简单介绍

目录 整体架构控制面kube-apiserver访问控制通知 kube-scheduler概述默认调度策略 kube-controller-manageretcd架构Raft协议日志复制 数据面kubeletkube-proxy 整体架构 集群架构图 控制面 控制面是kubernetes的核心组件&#xff0c;负责管理和控制集群的整体行为&#xf…

前端入门(三)Vue生命周期、组件原理、脚手架、插槽插件、存储、组件事件、动画、跨域与代理

文章目录 Vue生命周期Vue 组件化编程 - .vue文件非单文件组件组件的注意点组件嵌套Vue实例对象和VueComponent实例对象Js对象原型与原型链Vue与VueComponent的重要内置关系 应用单文件组件构建 Vue脚手架 - vue.cli项目文件结构组件相关高级属性引用名 - ref数据接入 - props混…

云匣子 FastJson反序列化RCE漏洞复现

0x01 产品简介 云匣子是租户连接云资源的安全管理工具&#xff0c;帮助云租户更加安全、精细的管理云上的虚拟机、数据库等资源。 云安宝结合多年的运维和安全实践&#xff0c;将云上的运维和安全有机结合&#xff0c;实现对运维过程的事前规划、事中控制和 事后审计。在此之上…

Retrofit怎么返回一个JSON字符串?

项目用已经使用了 Retrofit&#xff0c;定义了接口方法&#xff0c;返回了 JSON 转换后的实体对象&#xff0c;炒鸡方便。但是总有意料之外的时候&#xff0c;比如我不需要返回实体对象&#xff0c;我要返回纯纯的 JSON 字符串&#xff0c;怎么办呢&#xff1f; 先看源码 通过…

数据里有{1,2,3,4,5,6,7,8,9},请随机打乱顺序,生成一个新的数组

问题&#xff1a;数据里有{1,2,3,4,5,6,7,8,9}&#xff0c;请随机打乱顺序&#xff0c;生成一个新的数组。 思路&#xff1a; 旧数组 nums&#xff0c;新数组 newNums 1、先创建一个新数组&#xff0c;用来存打乱数据后的元素&#xff0c;新旧数组的长度要一致 2、然后遍历数组…

使用STM32与MFRC522 IC进行RFID卡的读取与识别(含代码)

利用STM32与MFRC522 IC进行RFID卡的读取和识别&#xff0c;可以实现对RFID卡的读取和获取卡片标识信息。MFRC522 IC是一种高集成度的13.56MHz RFID芯片&#xff0c;常用于门禁系统、物流跟踪和智能支付等领域。下面将介绍如何使用STM32与MFRC522 IC进行RFID卡的读取和识别&…

Mycat实现读写分离

Mycat实现读写分离 Mycat支持MySQL主从复制状态绑定的读写分离机制。这里实现的也是基于MySQL主从复制的读写分离。 MySQL主从复制配置 首先要配置MySQL的主从复制&#xff0c;这里配置的是一主一次从。可以参考下面的文章。 https://blog.csdn.net/wsb_2526/article/detail…

人工智能-优化算法和深度学习

优化和深度学习 对于深度学习问题&#xff0c;我们通常会先定义损失函数。一旦我们有了损失函数&#xff0c;我们就可以使用优化算法来尝试最小化损失。在优化中&#xff0c;损失函数通常被称为优化问题的目标函数。按照传统惯例&#xff0c;大多数优化算法都关注的是最小化。…

ElasticSearch02

ElasticSearch客户端操作 ElasticSearch 版本&#xff1a;7.8 学习视频&#xff1a;尚硅谷 笔记&#xff1a;https://zgtsky.top/ 实际开发中&#xff0c;主要有三种方式可以作为elasticsearch服务的客户端&#xff1a; 第一种&#xff0c;使用elasticsearch提供的Restful接口…

【数据库】缓冲区管理器结构,几种常用替换策略分析,pin钉住缓冲区块防止错误的替换,以及缓冲区管理带来的代价优化

缓冲区管理 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会定期更新&…

微软发布了Orca 2,一对小型语言模型,它们的性能超越了体积更大的同类产品

尽管全球目睹了OpenAI的权力斗争和大规模辞职&#xff0c;但作为AI领域的长期支持者&#xff0c;微软并没有放慢自己的人工智能努力。今天&#xff0c;由萨提亚纳德拉领导的公司研究部门发布了Orca 2&#xff0c;这是一对小型语言模型&#xff0c;它们在零样本设置下对复杂推理…

STK Components 二次开发-地面站传感器

上一篇我们说了创建地面站&#xff0c;那么这次我们在地面站添加一些特效。 1. 创建地面站 var locationPoint1 new PointCartographic(m_earth, new Cartographic(Trig.DegreesToRadians(117.17066), Trig.DegreesToRadians(31.84056), 240.359)); m_facility new Platfor…

计算机视觉面试题-01

计算机视觉面试通常涉及广泛的主题&#xff0c;包括图像处理、深度学习、目标检测、特征提取、图像分类等。以下是一些可能在计算机视觉面试中遇到的常见问题&#xff1a; 图像处理和计算机视觉基础 图像是如何表示的&#xff1f; 图像在计算机中可以通过不同的表示方法&…

以非常规思维去做一个嵌入式音视频开发项目!

前言&#xff1a; 大家好&#xff0c;在上周的文章里面&#xff0c;给大家介绍了一个音视频项目&#xff0c;本周继续来分享音视频项目&#xff0c;之前说过&#xff0c;如果你不知道做什么功能开发嘛&#xff0c;因为接触的少&#xff1b;我突然想到&#xff0c;可以去参考市面…