I2C子系统驱动框架:     应用程序层(app层) ——————————————————————————————————– i2c driver层: 从设备驱动层(TS  Sensor等) 1. 需要和应用层交互(fops  cdev) 2. 封装数据,但是不知道数据如何写入到硬件,需要调用adapter层的相关函数去写 ——————————————————————————————————– i2c core:维护i2c bus, 包括i2c driver和i2c client链表 1. 实现i2c client和i2c driver的匹配 ——————————————————————————————————– i2c adapter层: i2c控制器层,初始化i2c控制器,实现i2c时序 1. 将数据写入或读取从设备 2. 不知道具体数据(i2c driver提供的数据)是什么,但知道具体如何操作(读/写)从设备         这一层是具体的厂商实现的,比如三星:driver/i2c/busser/i2c-s3c2410.c

框架中,i2c core是由Linux内核实现的(i2c-core.c),i2c adapter是由具体的芯片厂商实现的,比如三星的芯片adapter实现都在driver/i2c/busser/i2c-s3c2410.c。所以这连个部分需要编译到uImage中(make menuconfig -> device driver -> <*> i2c support -> i2c hardware Bus support -> S3C2410 I2C driver)。 如果在/sys/bus/i2c/devices/i2c-0/1/2                表示有i2c-adapter 存在

在总结的时候看到有其他博友整理的框图非常好,我就借过来给大家分享!

从i2c驱动架构图中可以看出,linux内核对i2c架构抽象了一个叫核心层core的中间件,它分离了设备驱动device driver和硬件控制的实现细节(如操作i2c的寄存器),core层不但为上面的设备驱动提供封装后的内核注册函数,而且还为下面的硬件事件提供注册接口(也就是i2c总线注册接口i2c_add_register),可以说core层起到了承上启下的作用。

相关的重要结构体和函数: 1. i2c_client 每一个i2c从设备都需要用一个i2c_client结构体来描述,i2c_client对应真实的i2c物理设备device,但是i2c_client不是我们自己写程序去创建的,而是通过以下常用的方式自动创建的(这个地方不做详细说明,以介绍总体框架为主): platform创建: 1. 注册i2c_board_info 2. 获取对应的adapter,然后i2c_new_device devicetree创建: 3. 通过设备树的一个节点去描述一个从设备,设备树在解析的时候会自动创建client

  1. struct i2c_client {
  2. unsigned short flags; //标志位 (读写)
  3. unsigned short addr; //7位的设备地址(低7位)
  4. char name[I2C_NAME_SIZE]; //设备的名字,用来和i2c_driver匹配
  5. struct i2c_adapter *adapter; //依附的适配器(adapter),适配器指明所属的总线(i2c0/1/2_bus)
  6. struct device dev; //继承的设备结构体
  7. int irq; //设备申请的中断号
  8. struct list_head detected; //已经被发现的设备链表
  9. };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2. i2c_driver driver是指向从设备的驱动程序,由我们自己去实现并通过i2c_add_register注册到i2c的bus中,和i2c clinet进行匹配,匹配成功则调用probe函数。

  1. struct i2c_driver {
  2. int (*probe)(struct i2c_client *, const struct i2c_device_id *); //设备匹配成功调用的函数
  3. int (*remove)(struct i2c_client *); //设备移除之后调用的函数
  4. struct device_driver driver; //设备驱动结构体
  5. const struct i2c_device_id *id_table; //设备的ID表,匹配用platform创建的client
  6. };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3. i2c_adapter i2c总线适配器其实就是一个i2c总线控制器,本质上是一个物理设备,主要用来完成i2c总线控制器相关的数据通信 由芯片厂商去实现的。

  1. struct i2c_adapter {
  2. struct module *owner;
  3. unsigned int class; //允许匹配的设备的类型
  4. const struct i2c_algorithm *algo; //指向适配器的驱动程序,实现发送数据的算法
  5. struct device dev; //指向适配器的设备结构体
  6. char name[48]; //适配器的名字
  7. };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4. i2c_algorithm  i2c算法,适配器对应的驱动程序,每一个适配器对应一个驱动程序,用来描述适配器和设备之间的通信方法 由芯片厂商去实现的。

  1. struct i2c_algorithm {
  2. //传输函数指针,指向实现IIC总线通信协议的函数
  3. int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
  4. };
  • 1
  • 2
  • 3
  • 4

5. i2c_msg  把要发送的数据封装成msg结构体(比如16个字节进行拆分)

  1. struct i2c_msg {
  2. __u16 addr; /* slave address */
  3. __u16 flags; /* 1 - 读 0 - 写 */
  4. __u16 len; /* msg length */
  5. __u8 *buf; /* 要发送的数据 */
  6. };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

6. i2c_add_register 注册一个i2c_driver结构体,通过name或者id_tables或者of_match_table去匹配一个i2c_client,如果匹配成功,则会调用i2c_driver结构体里面的probe函数,并将对应的i2c_client结构体传过来。 #define  i2c_add_driver(driver)  i2c_register_driver(THIS_MODULE, driver) int i2c_register_driver(struct module *owner, struct i2c_driver *driver)

7. i2c_transfer  负责通过对应的i2c总线对依附于这个adapter的从机设备(i2c_client)进行读写数据(双向的)。其中要读写的数据要封装成为一个i2c_msg结构体,根据msg的flags标志位是0还是1来决定是读还是写。其实i2c_transfer是对master_xfer的封装。

  1. /**
  2. * i2c_transfer - execute a single or combined I2C message
  3. * @adap: Handle to I2C bus
  4. * @msgs: One or more messages to execute before STOP is issued to
  5. * terminate the operation; each message begins with a START.
  6. * @num: Number of messages to be executed.
  7. *
  8. * Returns negative errno, else the number of messages executed.
  9. *
  10. * Note that there is no requirement that each message be sent to
  11. * the same slave address, although that is the most common model.
  12. */
  13. int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
  14. eg.
  15. /* i2c_msg指明要操作的从机地址,方向,缓冲区 */
  16. struct i2c_msg msg[] = {
  17. {client->addr, 0, 1, &txbuf}, //0表示写,向往从机写要操作的寄存器的地址
  18. {client->addr, 1, 1, &rxbuf}, //读数据
  19. };
  20. /* 通过i2c_transfer函数操作msg */
  21. ret = i2c_transfer(client->adapter, msg, 2); //执行2条msg
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

这几个重要结构体之间的关系: a – i2c_adapter与i2c_algorithm i2c_adapter对应与物理上的一个适配器,而i2c_algorithm对应一套通信方法,一个i2c适配器需要i2c_algorithm中提供的(i2c_algorithm中的又是更下层与硬件相关的代码提供)通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用i2c_algorithm的指针。 i2c_algorithm中的关键函数master_xfer()用于产生i2c访问周期需要的start、stop、ack信号,以i2c_msg为单位发送和接收通信数据。 i2c_msg也非常关键,调用驱动中的发送接收函数需要填充该结构体

b –i2c_driver和i2c_client     i2c_driver对应一套驱动方法 i2c_client对应真实的i2c物理设备device,每个i2c设备都需要一个i2c_client来描述 i2c_driver与i2c_client的关系是一对多。一个i2c_driver上可以支持多个同等类型的i2c_client.

c – i2c_adapter和i2c_client i2c_adapter和i2c_client的关系与i2c硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个i2c设备,所以i2c_adapter中包含依附于它的i2c_client的链表

下面给出一个i2c子系统实例代码(用设备树实现): 主机 -  三星的某款cpu 从机 - mpu6050三轴加速度传感器

设备树描述: 当设备树被内核解析后会生成一个依附于i2c-0这个adapter的i2c_client

  1. @i2c-0 {//表示这个i2c_client所依附的adapter是i2c-0
  2. //对应i2c_client的name = "invensense,mpu6050"
  3. compatible = "invensense,mpu6050";
  4. //对应i2c_client的addr = 0x69 -- 从机设备的地址
  5. reg = <0x69>;
  6. //对应i2c_client的irq
  7. interrupts = <70>;
  8. };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

driver代码:

  1. #include <linux/module.h>
  2. #include <linux/init.h>
  3. #include <linux/i2c.h>
  4. #include <linux/cdev.h>
  5. #include <linux/fs.h>
  6. #include <asm/uaccess.h>
  7. #include "mpu6050.h"
  8. MODULE_LICENSE("GPL");
  9. #define SMPLRT_DIV 0x19
  10. #define CONFIG 0x1A
  11. #define GYRO_CONFIG 0x1B
  12. #define ACCEL_CONFIG 0x1C
  13. #define TEMP_OUT_H 0x41
  14. #define TEMP_OUT_L 0x42
  15. #define PWR_MGMT_1 0x6B
  16. int MAJOR = 255;
  17. int MINOR = 0;
  18. struct mpu6050_device {
  19. struct cdev cdev;
  20. dev_t devno;
  21. struct i2c_client * client;
  22. }mpu6050_dev;
  23. /* 读取mpu6050中一个字节的数据,将读取的数据的地址返回 */
  24. static int mpu6050_read_byte(struct i2c_client * client, unsigned char reg_add)
  25. {
  26. int ret;
  27. /* 要读取的那个寄存器的地址 */
  28. char txbuf = reg_add;
  29. /* 用来接收读到的数据 */
  30. char rxbuf[1];
  31. /* i2c_msg指明要操作的从机地址,方向,缓冲区 */
  32. struct i2c_msg msg[] = {
  33. {client->addr, 0, 1, &txbuf}, //0表示写,向往从机写要操作的寄存器的地址
  34. {client->addr, I2C_M_RD, 1, rxbuf}, //读数据
  35. };
  36. /* 通过i2c_transfer函数操作msg */
  37. ret = i2c_transfer(client->adapter, msg, 2); //执行2条msg
  38. if (ret < 0)
  39. {
  40. printk("i2c_transfer read err\n");
  41. return -1;
  42. }
  43. return rxbuf[0];
  44. }
  45. static int mpu6050_write_byte(struct i2c_client * client, unsigned char reg_addr, unsigned char data)
  46. {
  47. int ret;
  48. /* 要写的那个寄存器的地址和要写的数据 */
  49. char txbuf[] = {reg_addr, data};
  50. /* 1个msg,写两次 */
  51. struct i2c_msg msg[] = {
  52. {client->addr, 0, 2, txbuf}
  53. };
  54. ret = i2c_transfer(client->adapter, msg, 1);
  55. if (ret < 0)
  56. {
  57. printk("i2c_transfer write err\n");
  58. return -1;
  59. }
  60. return 0;
  61. }
  62. static int mpu6050_open(struct inode * inodep, struct file * filep)
  63. {
  64. printk("%s called\n", __func__);
  65. mpu6050_write_byte(mpu6050_dev.client, PWR_MGMT_1, 0x00);
  66. mpu6050_write_byte(mpu6050_dev.client, SMPLRT_DIV, 0x07);
  67. mpu6050_write_byte(mpu6050_dev.client, CONFIG, 0x06);
  68. mpu6050_write_byte(mpu6050_dev.client, GYRO_CONFIG, 0xF8);
  69. mpu6050_write_byte(mpu6050_dev.client, ACCEL_CONFIG, 0x19);
  70. return 0;
  71. }
  72. static int mpu6050_release(struct inode * inodep, struct file * filep)
  73. {
  74. printk("%s called\n", __func__);
  75. return 0;
  76. }
  77. void get_temp(union mpu6050_data * data)
  78. {
  79. data->temp = mpu6050_read_byte(mpu6050_dev.client, TEMP_OUT_L);
  80. data->temp |= mpu6050_read_byte(mpu6050_dev.client, TEMP_OUT_H) << 8;
  81. }
  82. static long mpu6050_ioctl(struct file * filep, unsigned int cmd, unsigned long arg)
  83. {
  84. union mpu6050_data data;
  85. switch (cmd)
  86. {
  87. case GET_TEMP:
  88. get_temp(&data);
  89. break;
  90. default:
  91. break;
  92. }
  93. if (copy_to_user((unsigned int *)arg, &data, sizeof(data)))
  94. return -1;
  95. return 0;
  96. }
  97. struct file_operations mpu6050_fops = {
  98. .owner = THIS_MODULE,
  99. .open = mpu6050_open,
  100. .release = mpu6050_release,
  101. .unlocked_ioctl = mpu6050_ioctl,
  102. };
  103. /* 匹配函数,设备树中的mpu6050结点对应转换为一个client结构体 */
  104. static int mpu6050_probe(struct i2c_client * client, const struct i2c_device_id * id)
  105. {
  106. int ret;
  107. printk("mpu6050 match ok!\n");
  108. mpu6050_dev.client = client;
  109. /* 注册设备号 */
  110. mpu6050_dev.devno = MKDEV(MAJOR, MINOR);
  111. ret = register_chrdev_region(mpu6050_dev.devno, 1, "mpu6050");
  112. if (ret < 0)
  113. goto err1;
  114. cdev_init(&mpu6050_dev.cdev, &mpu6050_fops);
  115. mpu6050_dev.cdev.owner = THIS_MODULE;
  116. ret = cdev_add(&mpu6050_dev.cdev, mpu6050_dev.devno, 1);
  117. if (ret < 0)
  118. goto err2;
  119. return 0;
  120. err2:
  121. unregister_chrdev_region(mpu6050_dev.devno, 1);
  122. err1:
  123. return -1;
  124. }
  125. static int mpu6050_remove(struct i2c_client * client)
  126. {
  127. printk("mpu6050 removed!\n");
  128. cdev_del(&mpu6050_dev.cdev);
  129. unregister_chrdev_region(mpu6050_dev.devno, 1);
  130. return 0;
  131. }
  132. /* 用来匹配mpu6050的设备树 */
  133. static struct of_device_id mpu6050_of_match[] = {
  134. {.compatible = "invensense,mpu6050"},
  135. {},
  136. };
  137. struct i2c_driver mpu6050_driver = {
  138. .driver = {
  139. .name = "mpu6050",
  140. .owner = THIS_MODULE,
  141. .of_match_table = of_match_ptr(mpu6050_of_match),
  142. },
  143. .probe = mpu6050_probe,
  144. .remove = mpu6050_remove,
  145. };
  146. static int mpu6050_init(void)
  147. {
  148. printk("%s called\n", __func__);
  149. i2c_add_driver(&mpu6050_driver);
  150. return 0;
  151. }
  152. static void mpu6050_exit(void)
  153. {
  154. printk("%s called\n", __func__);
  155. i2c_del_driver(&mpu6050_driver);
  156. return ;
  157. }
  158. module_init(mpu6050_init);
  159. module_exit(mpu6050_exit);

  160. http://blog.csdn.net/hanp_linux/article/details/72832158

I2C子系统驱动框架及应用 (转)的更多相关文章

  1. I2C总线驱动框架详解

    一.I2C子系统总体架构 1.三大组成部分 (1)I2C核心(i2c-core):I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册.注销方法,I2C通信方法(”algorithm”)上层的,与 ...

  2. I2C子系统之驱动SSD1306 OLED

    理解I2C设备驱动框架,主要围绕四个结构体去分析就容易了. struct i2c_algorithm:提供I2C协议的实现的操作,如:master_xfer实现数据收发的最基本方法. struct i ...

  3. i2c总线驱动,总线设备(适配器),从设备,从设备驱动的注册以及匹配

    常用链接 我的随笔 我的评论 我的参与 最新评论 我的标签 随笔分类 ARM裸机(13) C(8) C++(8) GNU-ARM汇编 Linux驱动(24) Linux应用编程(5) Makefile ...

  4. input子系统驱动

    input子系统驱动 框架分析 核心层 文件为:/drivers/input/input.c: 首先找到入口函数为**static int __init input_init(void)**,在该函数 ...

  5. Linux i2c子系统(一) _动手写一个i2c设备驱动

    i2c总线是一种十分常见的板级总线,本文以linux3.14.0为参考, 讨论Linux中的i2c驱动模型并利用这个模型写一个mpu6050的驱动, 最后在应用层将mpu6050中的原始数据读取出来 ...

  6. I2C驱动框架(四)

    参考:I2C子系统之platform_driver初始化——I2C_adap_s3c_init() 在完成platform_device的添加之后,i2c子系统将进行platform_driver的注 ...

  7. I2C驱动框架(三)

    参考:I2C子系统之platform_device初始化——smdk2440_machine_init() I2C驱动框架还应用了另一种总线-设备-驱动模型,平台设备总线platform_bus_ty ...

  8. Linux驱动编程--基于I2C子系统的I2C驱动

    代码中,我添加了很多注释,应该不难理解,有错误大家可以指出来,我再改正 #include <linux/kernel.h> #include <linux/module.h> ...

  9. 跟着内核学框架-从misc子系统到3+2+1设备识别驱动框架

    misc子系统在Linux中是一个非常简单的子系统,但是其清晰的框架结构非常适合用来研究设备识别模型.本文从misc子系统的使用出发,通过了解其机制来总结一套的设备识别的驱动框架,即使用使用同一个驱动 ...

随机推荐

  1. 第一个 MVC 应用程序(上半部分)(《精通 ASP.NET MVC5》 的第二章)

    本章将使用 ASP.NET MVC 框架创建一个简单的数据录入应用程序. 笔者会将过程分解成一个个的步骤,以便能够看出如何构造 ASP.NET MVC 应用程序.(对于一些未进行解释的内容,笔者提供了 ...

  2. WebSocket教程(二)

    运行环境:jdk8 tomcat8 无须其他jar包. package com.reach.socketController; import java.io.IOException; import j ...

  3. Shell脚本的学习(二)

    Shell脚本的学习(二) 方法: 1) 一个计算器: 2)递归实现打印目录    3)方法调用

  4. curl常用功能

    <?php //创建一个新cURL资源 $ch = curl_init(); //******************************************************** ...

  5. sql语句:查询、排序、不区分大小写查询,及联合使用

    1.不排序时的查找语句: offset message  为表名,talker 为列名. 此语句的意思是:从message 表中查找 talker 等于wxid_0930479303212的数据,从第 ...

  6. 013PHP文件处理——filemtime filectime fileatime date_default_timezone_set touch

    <?php /** * filemtime filectime fileatime date_default_timezone_set touch */ /*filemtime:获取文件的修改时 ...

  7. jsp jsp属性范围

    jsp提供了4中属性分别是 当前页:一个属性只能在一个页面中取得,跳转淘其他页面无法取得. 一次服务器请求:一个页面中设置的属性,只要经过了服务跳转,而跳转之后的页面可以继续取得 一次回话:一个用户设 ...

  8. httpclient 连接参数

    http.socket.timeout(读取超时) 套接字毫秒级超时时间(SO_TIMEOUT),这就是等待数据,换句话说,在两个连续的数据包之间最大的闲置时间. 如果超时是0表示无限大的超时时间,即 ...

  9. C++模板详解——使用篇

    假如我们需要取得两个变量中较大的变量,或许,我们可以通过重载的方式实现,如下. int max(int fir,int sec); float max(float fir,float sec); do ...

  10. js根据选中的复选框,隐藏那一行

    如图,选择复选框,点击“隐藏”按钮,隐藏选中行 1.JavaScript代码: function getCheckedIds() { var checkedSubject = $('#showSbgl ...