参考资料

  • Linux内核文档:

    • Documentation\devicetree\bindings\i2c\i2c-gpio.txt
  • Linux内核驱动程序:使用GPIO模拟I2C
    • drivers\i2c\busses\i2c-gpio.c
  • Linux内核真正的I2C控制器驱动程序
    • drivers\i2c\busses\i2c-imx.c

一、回顾

1.1 I2C驱动程序的层次

  • App:应用程序,想做某些事情。
  • I2C device driver:内核中的i2c驱动和i2c设备,驱动知道怎么做这些事情。
  • I2C Core:i2c一些通用的函数,对于这些事情的辅助作用。
  • I2C Controller Driver:一些相关的函数,具体做这些事情的控制器。

1.2 I2C总线驱动模型

左右两边一一匹配比较,不写了。

二、I2C_Adapter驱动框架

2.1 i2c_adapter

i2c_adpater里面有两个比较重要的结构体:const struct i2c_algorithm *algo;int nr;
一个是算法,一个是第几条总线。

struct i2c_adapter {struct module *owner;unsigned int class;       /* classes to allow probing for */const struct i2c_algorithm *algo; /* the algorithm to access the bus 算法结构体 */void *algo_data;/* data fields that are valid for all devices   */const struct i2c_lock_operations *lock_ops;struct rt_mutex bus_lock;struct rt_mutex mux_lock;int timeout;            /* in jiffies */int retries;struct device dev;      /* the adapter device */int nr;     /* 表示第几条总线 */char name[48];struct completion dev_released;struct mutex userspace_clients_lock;struct list_head userspace_clients;struct i2c_bus_recovery_info *bus_recovery_info;const struct i2c_adapter_quirks *quirks;
};

2.2 i2c_algorithm

然后在i2c_algorithm中主要是master_xferfunctionality函数。

struct i2c_algorithm {/* If an adapter algorithm can't do I2C-level access, set master_xferto NULL. If an adapter algorithm can do SMBus access, setsmbus_xfer. If set to NULL, the SMBus protocol is simulatedusing common I2C messages *//* master_xfer should return the number of messages successfullyprocessed, or a negative value on error */int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,u8 command, int size, union i2c_smbus_data *data);/* To determine what the adapter supports */u32 (*functionality) (struct i2c_adapter *);#if IS_ENABLED(CONFIG_I2C_SLAVE)int (*reg_slave)(struct i2c_client *client);int (*unreg_slave)(struct i2c_client *client);
#endif
};
  • master_xfer:这是最重要的函数,它实现了一般的I2C传输,用来传输一个或多个i2c_msg
  • master_xfer_atomic:
    • 可选的函数,功能跟master_xfer一样,在atomic context环境下使用,原子版本
    • 比如在关机之前、所有中断都关闭的情况下,用来访问电源管理芯片
  • smbus_xfer:实现SMBus传输,如果不提供这个函数,SMBus传输会使用master_xfer来模拟
  • smbus_xfer_atomic:
    • 可选的函数,功能跟smbus_xfer一样,在atomic context环境下使用
    • 比如在关机之前、所有中断都关闭的情况下,用来访问电源管理芯片
  • functionality:返回所支持的flags:各类I2C_FUNC_*
  • reg_slave/unreg_slave:
    • 有些I2C Adapter也可工作与Slave模式,用来实现或模拟一个I2C设备
  • reg_salve就是让把一个i2c_client注册到I2C Adapter,换句话说就是让这个I2C Adapter模拟该i2c_client
    • unreg_slave:反注册

三、编写驱动程序框架

分配、设置、注册一个i2c_adapter结构体,这个结构体是在i2c_driver的probe函数里注册的。

  • i2c_adapter的核心是i2c_algorithm
  • i2c_algorithm的核心是masterxfer函数

3.1 所涉及的函数

  • 分配
    struct i2c_adapter *adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);

  • 设置

adap->owner = THIS_MODULE;
adap->algo = &example_algo;
  • 注册:i2c_add_adapter / i2c_add_umbered_adapter
ret = i2c_add_adapter(adap);    //不管adap->nr是什么,都动态设置adap->nr
ret = i2c_add_numbered_adapter(adap);  //如果adap->nr == -1,则动态分配nr;佛则使用该nr
  • 反注册
    i2c_del_adapter(adap);

3.2 编写设备树文件

/ {i2c-bus-virtual {compatible = "100ask,i2c-bus-virtual";                //使用字符串匹配i2c_adpter_drv};
};

3.3 编写adapter_drv的思路

  1. adapter驱动是一个platform_driver的驱动,所以我们先增加platforminit函数和exit函数
  2. 在注册时需要一个platform_driver的结构体,所以我们需要创建一个i2c_bus_virtual_driver结构体。
  3. 在i2c_bus_virtual_driver中包括,probe函数,remove函数,driver结构体匹配设备树
  4. 在probe函数中需要创建一个全局的i2c_adapter结构体,里面包括传输算法,属于第几个总线。
  5. 在remove函数中去删除i2c_adapter结构体。
  6. 因为只是模拟eeprom的设备,所以就在传输函数上增加了对数据存储和读取的模拟。

3.4 编写模拟eeprom的思路

  1. 在初始化adapter时,需要提供一个i2c_algorithm的结构体,这个结构体是用来发送和就收数据的算法
  2. 在i2c_algorithm结构体中,主要有master_xfer和functionality需要实现,functionality是提供adapter支持的传输数据类型
  3. master_xfer中把数据解析出来,只处理地址为0x50的数据。使用eeprom_emulate_xfer去实现模拟发送和接收
  4. eeprom_emulate_xfer如果是发送就直接对着地址写入内容,这个buffer在内赫里创建了512个字节。如果是读就从对应的地址读入内容。

3.5 调试步骤

# 复制dtb文件到tftp目录
cp arch/arm/boot/dts/imx6ull-14x14-ebf.dtb ~/tftpboot/
# 复制ko文件到文件目录
sudo cp i2c_adapter_drv.ko ~/imx6ull/nfs_rootfs/home/picture/12_i2c/
# 加载内核
[root@ebf6ull:/home/picture/12_i2c]# insmod i2c_adapter_drv.ko
[  254.236831] i2c_adapter_drv: loading out-of-tree module taints kernel.
[  254.254930] sii902x 1-0039: No reset pin found
# 检测是否生成virtual bus
[root@ebf6ull:/home/picture/12_i2c]# i2cdetect -y -a -l
i2c-1   i2c             21a4000.i2c                             I2C adapter
i2c-4   i2c             i2c-bus-virtual                         I2C adapter
i2c-0   i2c             21a0000.i2c                             I2C adapter
[root@ebf6ull:/home/picture/12_i2c]# i2cdetect -F 4
Functionalities implemented by /dev/i2c-4:
I2C                              yes
SMBus Quick Command              yes
SMBus Send Byte                  yes
SMBus Receive Byte               yes
SMBus Write Byte                 yes
SMBus Read Byte                  yes
SMBus Write Word                 yes
SMBus Read Word                  yes
SMBus Process Call               yes
SMBus Block Write                yes
SMBus Block Read                 yes
SMBus Block Process Call         yes
SMBus PEC                        yes
I2C Block Write                  yes
I2C Block Read                   yes
[root@ebf6ull:/home/picture/12_i2c]# i2cdetect -y -a 40  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# 读写测试
[root@ebf6ull:/home/picture/12_i2c]# i2cset -f -y 4 0x50 0 0x55
[root@ebf6ull:/home/picture/12_i2c]# i2cget -f -y 4 0x50 0
0x55
[root@ebf6ull:/home/picture/12_i2c]# i2cset -f -y 4 0x50 1 0x56
[root@ebf6ull:/home/picture/12_i2c]# i2cget -f -y 4 0x50 1
0x56

四、相关源码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/slab.h>static struct i2c_adapter *g_adapter;static unsigned char eeprom_buffer[512];
static int eeprom_cur_addr = 0;static void eeprom_emulate_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{int i;if(msg->flags & I2C_M_RD) {for(i=0;i<msg->len;i++) {msg->buf[i] = eeprom_buffer[eeprom_cur_addr++];if(eeprom_cur_addr == 512)eeprom_cur_addr = 0;}} else {if(msg->len >= 1) {eeprom_cur_addr = msg->buf[0];for(i=1;i<msg->len;i++) {eeprom_buffer[eeprom_cur_addr++] = msg->buf[i];if(eeprom_cur_addr == 512)eeprom_cur_addr = 0;}}}
}static int i2c_bus_virtual_xfer(struct i2c_adapter *i2c_adap,struct i2c_msg msgs[], int num)
{int i;//emulate eeprom addr=0x50for(i=0;i<num;i++) {// do transfer msgs[i]if(msgs[i].addr == 0x50) {eeprom_emulate_xfer(i2c_adap, &msgs[i]);} else {i = -EIO;break;}}return i;
}static u32 i2c_bus_virtual_func(struct i2c_adapter *adap)
{return (I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |I2C_FUNC_SMBUS_READ_BLOCK_DATA |I2C_FUNC_SMBUS_BLOCK_PROC_CALL |I2C_FUNC_PROTOCOL_MANGLING);
}const struct i2c_algorithm i2c_bus_virtual_algo = {.master_xfer = i2c_bus_virtual_xfer,.functionality = i2c_bus_virtual_func,
};static int i2c_bus_virtual_probe(struct platform_device *pdev)
{/* get info from device tree, to set i2c_adapter or hardware  *//* alloc, set, register i2c_adapter */g_adapter = kzalloc(sizeof(*g_adapter), GFP_KERNEL);g_adapter->owner = THIS_MODULE;g_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;g_adapter->nr = -1;snprintf(g_adapter->name, sizeof(g_adapter->name), "i2c-bus-virtual");g_adapter->algo = &i2c_bus_virtual_algo;i2c_add_adapter(g_adapter);      //i2c_add_numerd_adapter(g_adapter);return 0;
}static int i2c_bus_virtual_remove(struct platform_device *dev)
{i2c_del_adapter(g_adapter);return 0;
}static const struct of_device_id i2c_bus_virtual_ids[] = {{   .compatible = "100ask,i2c-bus-virtual",  },{ /* end of list */ }
};struct platform_driver i2c_bus_virtual_driver = {.driver = {.name = "i2c-bus-virtual",.of_match_table = i2c_bus_virtual_ids,},.probe = i2c_bus_virtual_probe,.remove = i2c_bus_virtual_remove,
};static int i2c_bus_virtual_init(void)
{int ret;ret = platform_driver_register(&i2c_bus_virtual_driver);if(ret)printk(KERN_ERR"i2c_driver register failed\n");return 0;
}static void i2c_bus_virtual_exit(void)
{platform_driver_unregister(&i2c_bus_virtual_driver);
}module_init(i2c_bus_virtual_init);
module_exit(i2c_bus_virtual_exit);MODULE_AUTHOR("tuo");
MODULE_LICENSE("GPL");

I2C_Adapter驱动创建讲解与编写相关推荐

  1. RTC芯片——DS1302驱动方式讲解(附代码)

    RTC芯片--DS1302驱动方式讲解(附代码) 最近的一个项目中用到了DS1302rtc芯片,中间弯弯绕绕也费了点时间,好在最后还是成功搞定,现做一下总结,希望能让各位少走些弯路. 写代码前所需了解 ...

  2. 具体芯片的I2C_Adapter驱动分析

    具体芯片的I2C_Adapter驱动分析 文章目录 具体芯片的I2C_Adapter驱动分析 参考资料: 一. I2C控制器内部结构 1.1 通用的简化结构 1.2 IMX6ULL的I2C控制器内部结 ...

  3. rust实现一个mysql驱动_使用Rust编写用户态驱动程序

    概览 在云计算技术的发展史上,如何提高单个服务器的并发度,一直是热门的研究课题.在20年前,就有著名的"C10K"问题,即如何利用单个服务器每秒应对10K个客户端的同时访问.这么多 ...

  4. DRM框架(vkms)分析(9)----drm驱动创建fbdevice分析(以rockchip_drm_drv为例)

    本文主要介绍DRM框架里的fbdev兼容逻辑 一 framebuffer框架简单介绍 framebuffer框架下fbdev的注册主要三步步: (1)创建fbdev操作函数,以rockchip为例: ...

  5. Linux驱动实践:如何编写【 GPIO 】设备的驱动程序?

    作 者:道哥,10+年嵌入式开发老兵,专注于:C/C++.嵌入式.Linux. 关注下方公众号,回复[书籍],获取 Linux.嵌入式领域经典书籍:回复[PDF],获取所有原创文章( PDF 格式). ...

  6. Django 博客教程(三):创建应用和编写数据库模型

    创建 django 博客应用 在上一章节中我们创建了 django 博客的工程,并且成功地运行了它.然而这一切都是 django 为我们创建的项目初始内容,django 不可能为我们初始化生成我们需要 ...

  7. 1、django安装,问题,创建项目,编写第一个demo

    1.1.django官网 https://www.djangoproject.com/ 1.2.安装 获取最新的正式版本 pip install Django==3.0.6 -i https://py ...

  8. 冰豹lua驱动设置_通过编写“猜数字”游戏学习 Lua | Linux 中国

    通过编写一个简单的游戏来认识 Lua,它是一种动态类型的.轻量级的.高效的.可嵌入的脚本语言. 来源:https://linux.cn/article-13000-1.html 作者:Seth Ken ...

  9. Xilinx Vivado的使用详细介绍(1):创建工程、编写代码、行为仿真、Testbench

    新建工程 打开Vivado软件,直接在欢迎界面点击Create New Project,或在开始菜单中选择File - New Project即可新建工程. 点击Next 输入工程名称和路径. 选择R ...

最新文章

  1. 第三章、一文告诉你FastDFS分布式如何部署
  2. linux下批量修改文件名精彩解答案例分享
  3. 动态规划习题,关系式推导和求模技巧
  4. 想写一篇关于.net下COM工作原理的文章
  5. 如何保证进程间同步工作_冬季建房如何保证混凝土浇筑效果好,做好养护工作...
  6. [设计模式-结构型]桥接(Bridge )
  7. 网校网络工程师视频下载
  8. 马化腾提问_互联网融合创新会带来哪些改变_知乎回答部分总结
  9. 深度学习-栈式自编码算法
  10. python入门代码-Python入门学习系列——Python代码测试
  11. php图片位置偏移代码,关于PHP的fseek()方法,究竟是什么偏移(位置)?
  12. 关系数据库理论:范式判断、函数依赖、无损分解、正则覆盖
  13. win10 uwp 使用 Border 布局
  14. 修改Worldpress主题的Footer/Header部分
  15. Android 一体机研发之修改系统设置————声音
  16. 【有利可图网】PS实战系列:果汁喷溅的效果,你知道是怎么做的吗?
  17. mes系统的主要功能是什么?看完这篇你就懂了
  18. C++设计模式——中介者模式(高屋建瓴)
  19. 什么是外键约束?外键约束下的多表操作是什么?
  20. Yahoo 前端雅虎军规

热门文章

  1. uniapp 用 uView 组件库中的u-picker 实现地区的 省-市-区 三级联动
  2. AMC1100采用全差分隔离放大器的隔离式电流与电压测量
  3. 无线攻击实战(AirCrack、Fern WiFi Cracker)
  4. 记一次使用frpc/frps进行内网穿透
  5. java集成ios内购\与ios退款通知处理
  6. linux usb hub irq,am5728 连接USB hub tusb4041i不能够正常工作
  7. php计数器归零,PHP简单实现文本计数器的方法
  8. floyd求最小环——poj1734Sightseeing trip
  9. JAVA毕设项目双河市北疆红提追溯系统(java+VUE+Mybatis+Maven+Mysql)
  10. C语言int16_t和uint16_t的区别