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,2devinfo->board_info = *info;//将数组中成员赋值给节点list_add_tail(&devinfo->list,& i2c_board_list);  //把devinfo结构体注册到链表i2c_board_listi2c_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_driverstatic 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-0adap->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);//创建一个clientclient->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); //创建了字符设备驱动,注册fopsat24_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"); //注册字符设备驱动//记录当前clientat24_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");//记录当前clientat24_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 driverreturn 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");

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

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

相关文章

STK Components 二次开发-创建地面站

1.地面站只需要知道地面站的经纬高。 // Define the location of the facility using cartographic coordinates.var location new Cartographic(Trig.DegreesToRadians(-75.596766667), Trig.DegreesToRadians(40.0388333333), 0.0); 2.创建地面站 创建方式和卫星一样生成对…

如何使用JMeter测试导入接口/导出接口

今天一上班&#xff0c;被开发问了一个问题&#xff1a;JMeter调试接口&#xff0c;文件导入接口怎么老是不通&#xff1f;还有导出文件接口&#xff0c;不知道文件导到哪里去了&#xff1f; 我一听&#xff0c;这不是JMeter做接口测试经常遇到的嘛&#xff0c;但是一时半会又…

解决视口动画插件jquery.aniview.js使用animate.css时无效的问题(最新版本网页视口动画插件的使用及没作用、没反应)

当网站页面元素进入视口时自动应用过渡效果。CSS过渡效果可以为网页添加动画效果&#xff0c;并提供了一种平滑的转换方式&#xff0c;使元素的变化更加流畅和生动。而通过jQuery插件来获取页面滚动位置决定合适调用动画效果。 一、官网 animate.css官网 一款强大的预设css3动…

Linux的基本指令(四)

目录 前言 时间相关的指令 date指令 时间戳 日志 时间戳转化为具体的时间 cal指令 find指令&#xff08;十分重要&#xff09; grep指令&#xff08;行文本过滤工具&#xff09; 学前补充 什么是打包和压缩&#xff1f; 为什么要打包和压缩&#xff1f; 怎么打包和…

【数据结构】二叉树oj题

在处理oj题之前我们需要先处理一下之前遗留的问题 在二叉树中寻找为x的节点 BTNode* BinaryTreeFind(BTNode* root, int x) {if (root NULL)return NULL;if (root->data x)return root;BTNode* ret1 BinaryTreeFind(root->left, x);BTNode* ret2 BinaryTreeFind(ro…

Windows系统下更新后自带的画图软件出现马赛克bug

一.bug的样子&#x1f357; 在使用橡皮后&#xff0c;原来写的内容会变成马赛克。而我们希望它是纯白色的。 二.解决方法&#x1f357; 第一步 第二步 第三步 三. 解决后的效果&#x1f357; 用橡皮擦随便擦都不会出现马赛克了。 更新过后&#xff0c;想用win自带的画图软件会出…

【前沿技术了解】web图形Canvas、svg、WebGL、数据可视化引擎的技术选型

目录 Canvas&#xff1a;HTML5新增 Canvas标签&#xff08;画布&#xff09; 渲染上下文canvas.getContext(contextType[, contextAttributes]) 上下文类型&#xff08;contextType&#xff09; 上下文属性 (contextAttributes) 示例 动画 setInterval(function, delay)…

2023/11/26总结

一些学习记录&#xff1a; 在对数据库进行一系列操作的时候&#xff0c;遇到一个问题&#xff0c;在插入数据的时候&#xff0c;我数据库对应的是自增id&#xff0c;但是插入后想获取到这个id去使用。我以为是不可以马上获取的&#xff0c;然后看到 项目进度 购物车&#xff…

[pyqt5]PyQt5之如何设置QWidget窗口背景图片问题

目录 PyQt5设置QWidget窗口背景图片 QWidget 添加背景图片问题QSS 背景图样式区别PyQt设置窗口背景图像&#xff0c;以及图像自适应窗口大小变化 总结 PyQt5设置QWidget窗口背景图片 QWidget 添加背景图片问题 QWidget 创建的窗口有时并不能直接用 setStyleSheet 设置窗口部分…

【学习记录】从0开始的Linux学习之旅——驱动模块编译与加载

一、概述 Linux操作系统通常是基于Linux内核&#xff0c;并结合GNU项目中的工具和应用程序而成。Linux操作系统支持多用户、多任务和多线程&#xff0c;具有强大的网络功能和良好的兼容性。本文主要讲述如何编译及加载linux驱动模块。 二、概念及原理 应用程序通过系统调用与内…

软件介绍02- flameshot截图软件(linux系统可用)

1 软件介绍 在Windows和mac平台一直都使用着snipaste截图&#xff0c;非常好用&#xff0c;又能够钉图。遗憾是并没有开发linux版本&#xff0c;真不知道为什么。 好在终于找到一款截图软件&#xff0c;flameshot截图软件&#xff0c;可以平替snipaste。 下载网址&#xff1a;…

C语言WFC绘制矩形

代码实现&#xff1a; void CCGDrawingView::Rectangle(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, COLORREF color,CDC* pDC) {CPen redPen(PS_SOLID, 1, color);CBrush redBursh(color);CPen* pOldPen pDC->SelectObject(&redPen);CBrush* p…

Django(十一、auth认证模块)

文章目录 一、auth介绍auth认证相关模块及操作扩展auth_user表 一、auth介绍 Django自带一个admin路由&#xff0c;但是需要我们提供管理员账户和密码&#xff0c;如果想要使用admin后台管理&#xff0c;需要先创建表&#xff0c;然后创建管理员账户。 直接执行数据类迁移命令…

hivesql 将json格式字符串转为数组

hivesql 将json格式字符串转为数组 完整过程SQL在文末 json 格式字符串 本案例 json 字符串参考格式&#xff0c;请勿使用本数据 {"data": [{"province": 11,"id_card": "110182198903224674","name": "闾丘饱乾"…

【JAVA学习笔记】71 - JDBC入门

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter25/src/com/yinhai/dao_ 一、JDBC概述 1.基本介绍 1. JDBC为访问不同的数据库提供了统一的接口&#xff0c;为使用者屏蔽了细节问题。 2. Java程序员使用JDBC,可以连接任何提供了JDBC驱动…

YARN工作流程详解

图1 图2 图1 -作业提交阶段&#xff1a; 1、client 提交job,向 ResourceManager【RM】 申请job_id; 2、RM 返回 job_id 及资源提交路径 给 client 3、client 把job所需的资源提交 到 3中指定的路径中 4、client 上传完成资源后&#xff0c;向RM 发送执行作业请求&#xff0c;RM…

BGP选路实验

要求 1 使用PreVal策略&#xff0c;确保R4通过R2到达192.168.10.0/24 2 使用AS_Path策略&#xff0c;确保R4通过R3到达192.168.11.0/24 3 配置MED策略&#xff0c;确保R4通过R3到达192.168.12.0/24 4 使用Local Preference策略&#xff0c;确保R1通过R2到达192.168.1.0/24 5 使…

力扣日记11.25-【二叉树篇】对称二叉树

力扣日记&#xff1a;【二叉树篇】对称二叉树 日期&#xff1a;2023.11.25 参考&#xff1a;代码随想录、力扣 101. 对称二叉树 题目描述 难度&#xff1a;简单 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,…

Blender 连续 5 天遭受大规模 DDoS 攻击

Blender 发布公告指出&#xff0c;在2023年11月18日至23日期间&#xff0c;blender.org 网站遭受了持续的分布式拒绝服务&#xff08;DDoS&#xff09;攻击&#xff0c;攻击者通过不断发送请求导致服务器超载&#xff0c;使网站运营严重中断。此次攻击涉及数百个 IP 地址的僵尸…

ATK-ESP8266 WIFI模块串口通信通用实现方案

ATK-ESP8266 WIFI模块是一种常用的无线模块&#xff0c;它可以通过串口与外部设备进行通信&#xff0c;实现数据的收发和控制。本文将介绍一种通用的实现方案&#xff0c;帮助您在项目中使用ATK-ESP8266 WIFI模块进行串口通信。 【方案概述】 这个通用实现方案涵盖了ATK-ESP82…