IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> IIC驱动 -> 正文阅读

[系统运维]IIC驱动

参考这个大神:28.Linux-IIC驱动(详解) - 诺谦 - 博客园

1. I2C体系结构分析

1.1首先进入linux内核的driver/i2c目录下,如下图所示:

?其中重要的文件介绍如下:

1)algos文件夹(algorithms)

里面保存I2C的通信方面的算法

2)busses文件夹

里面保存I2C总线驱动相关的文件,比如i2c-omap.c、 i2c-versatile.c、 i2c-s3c2410.c等。

3)?chips文件夹

里面保存I2C设备驱动相关的文件

4)?i2c-core.c

这个文件实现了I2C核心的功能(I2C总线的初始化、注册和适配器添加和注销等相关工作)

5)?i2c-dev.c

提供了通用的read( ) 、 write( ) 和ioctl( ) 等接口,实现了I2C适配器设备文件的功能,其中I2C设备的主设备号都为89, 次设备号为0~255。
应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器, 并控制I2C设备的工作方式

显然,它和前几次驱动类似, I2C也分为总线驱动和设备驱动,总线就是协议相关的,它知道如何收发数据,但不知道数据含义,设备驱动却知道数据含义

1.2 I2C驱动架构,如下图所示:

如上图所示,每一条I2C对应一个adapter适配器,在kernel中, adapter适配器是通过struct adapter结构体定义,主要是通过i2c core层将i2c设备与i2c adapter关联起来。

2.分析I2C总线驱动

参考 drivers/i2c/busses/i2c-s3c2410.c

先进入init入口函数,如下图所示:

在init函数中,注册了一个?“s3c2440-i2c”的platform_driver平台驱动,来看看probe函数做了些什么:

进入s3c24xx_i2c_probe函数:

struct i2c_adapter  adap;

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
    struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
       ... ...

       /*获取,使能I2C时钟*/
       i2c->clk = clk_get(&pdev->dev, "i2c");               //获取i2c时钟
       clk_enable(i2c->clk);                                         //使能i2c时钟

       ... ....
       /*获取资源*/
       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
       i2c->regs = ioremap(res->start, (res->end-res->start)+1);

       ... ....

       /*设置i2c_adapter适配器结构体, 将i2c结构体设为adap的私有数据成员*/
    i2c->adap.algo_data = i2c;          //i2c_adapter适配器指向s3c24xx_i2c;
       i2c->adap.dev.parent = &pdev->dev;

 
    /* initialise the i2c controller */
       /*初始化2440的I2C相关的寄存器*/
       ret = s3c24xx_i2c_init(i2c);
       if (ret != 0)
              goto err_iomap;

       ... ...
       /*注册中断服务函数*/
       ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,pdev->name, i2c);
       ... ...

       /*注册i2c_adapter适配器结构体*/
       ret = i2c_add_adapter(&i2c->adap);
       --->  i2c_register_adapter();  //调用i2c_register_adapter()函数进一步来注册.
       ... ...
}

其中i2c_adapter结构体是放在s3c24xx_i2c->adap,如下图所示:

i2c_register_adapter()函数代码如下所示:

static int i2c_register_adapter(struct i2c_adapter *adap)
{
       struct list_head  *item;                  //链表头,用来存放i2c_driver结构体的表头
       struct i2c_driver *driver;                //i2c_driver,用来描述一个IIC设备驱动
       list_add_tail(&adap->list, &adapters);   //添加到内核的adapter链表中
        ... ...
       list_for_each(item,&drivers) {            //for循环,从drivers链表里找到i2c_driver结构体的表头
           driver = list_entry(item, struct i2c_driver, list); //通过list_head表头,找到i2c_driver结构体
           if (driver->attach_adapter)  
               /* We ignore the return code; if it fails, too bad */
               driver->attach_adapter(adap);    //调用i2c_driver的attach_adapter函数来看看,这个新注册的设配器是否支持i2c_driver }}

在i2c_register_adapter()函数里主要执行以下几步:

①将adapter放入i2c_bus_type的adapter链表

②i2c_adapter表示物理上的一个i2C设备(适配器), 在i2c-s3c2410.c中,是存放在s3c24xx_i2c结构体下的(struct ?i2c_adapter ?adap)成员中

③将所有的i2c设备调出来,执行i2c_driver设备的attach_adapter函数来匹配

其中, i2c_driver结构体会在后面讲述到,显然IIC总线驱动i2c-s3c2410.c,主要设置适配器adapter,然后注册适配器,里面帮我们做好了IIC通信的架构,就是不知道发什么内容。

3.分析IIC设备驱动

参考: driver/i2c/chips/eeprom.c

先来看它的init入口函数:

?其中struct ?i2c_driver? eeprom_driver的成员如下:

static struct i2c_driver eeprom_driver = {
       .driver = {
              .name     = "eeprom",               //名称
        },
       .id              = I2C_DRIVERID_EEPROM,    //IIC设备标识ID
       .attach_adapter  = eeprom_attach_adapter,  //用来与总线驱动的适配器匹配,匹配成功添加到适配器adapter中
       .detach_client   = eeprom_detach_client,   //与总线驱动的适配器解绑,分离这个IIC从设备
};

显然,在init函数中通过i2c_add_driver()注册i2c_driver结构体,然后通过i2c_driver ->attach_adapter来匹配内核中的各个总线驱动的适配器, 发送这个设备地址,若有ACK响应,表示匹配成功。

接下来,我们进入i2c_add_driver()来看看是不是这样的:

int i2c_add_driver(struct module *owner, struct i2c_driver *driver)
{
       driver->driver.owner = owner;
       driver->driver.bus = &i2c_bus_type;    //将i2c_driver放在i2c_bus_type链表中   

       res = driver_register(&driver->driver); //注册一个i2c_driver
       ... ...

       if (driver->attach_adapter) {
              struct i2c_adapter *adapter;               //定义一个i2c_adapter适配器
          list_for_each_entry(adapter, &adapters, list)  //for循环提取出adapters链表中所有的i2c_adapter适配器,放入到adapter结构体中
      {
              driver->attach_adapter(adapter); //来匹配取出来的i2c_adapter适配器
          }
  }
      ... ...
return 0;
}

在i2c_add_driver ()函数里主要执行以下几步:

放入到i2c_bus_type链表

取出adapters链表中所有的i2c_adapter,然后执行i2c_driver->attach_adapter()

所以i2c_adapter适配器和i2c_driver设备驱动注册框架如下所示:

这里调用了i2c_driver ->attach_adapter(adapter),我们看看里面是不是通过发送IIC设备地址,等待ACK响应来匹配的。

以struct i2c_driver eeprom_driver 为例,进入i2c_driver ->eeprom_attach_adapter()函数:

如下图所示,里面调用了i2c_probe(adapter, &addr_data, eeprom_detect)函数:

上图的第1个参数就是i2c_adapter适配器,第2个参数addr_data变量,里面存放了IIC设备地址的信息,第3个参数eeprom_detect就是具体的设备探测回调函数i2c_probe()函数,会通过adapter适配器发送IIC设备地址addr_data,如果收到ACK信号,就调用eeprom_detect()回调函数来注册i2c_client结构体,该结构体对应真实的物理从设备,而i2c_driver对应的是设备驱动,也就是说,只有当适配器支持这个设备驱动,才会注册i2c_client从设备。

而在i2c_driver ->detach_client()中,则注销i2c_client结构体

其中addr_data变量是struct i2c_client_address_data结构体,它的成员如下所示:

struct i2c_client_address_data {
       unsigned short *normal_i2c;     //存放正常的设备高7位地址数据
       unsigned short *probe;          //存放不受*ignore影响的高7位设备地址数据
       unsigned short *ignore;         //存放*ignore的高7位设备地址数据
       unsigned short **forces;        //forces表示适配器匹配不了该设备,也要将其放入适配器中

};

当上面结构体的数组成员以I2C_CLIENT_END结尾,则表示地址已结束,比如at24c02设备为例,看这个结构体如何定义的:

#define  AT24C02_ADDR           (0xA0>>1)           //AT24C02地址

static unsigned short  ignore[]      = { I2C_CLIENT_END };
static unsigned short  normal_addr[] = { AT24C02_ADDR, I2C_CLIENT_END };
static unsigned short   force_addr[] = {ANY_I2C_BUS, AT24C02_ADDR ,2C_CLIENT_END};
static unsigned short   * forces[]   = {force_addr,NULL}; 
//ANY_I2C_BUS:表示支持所有适配器总线,若填指定的适配器总线ID,则表示该设备只支持指定的那个适配器

static struct i2c_client_address_data  addr_data = {
       .normal_i2c       = normal_addr,    //存放at24c02地址
       .probe            = ignore,         //表示无地址
       .ignore           = ignore,         //表示无地址
       .forces           = forces,         //存放强制的at24c02地址,表示强制支持
};

所以在i2c_driver ->attach_adapter(adapter)函数里主要执行以下几步:

1)?调用?i2c_probe(adap,?i2c_client_address_data设备地址结构体, 回调函数);

2)?将要发的设备地址结构体打包成i2c_msg,

3)?然后执行i2c_transfer()来调用i2c_adapter->algo->master_xfer()将i2c_msg发出去

4)若收到ACK回应,便进入回调函数,注册i2c_client从设备,使该设备与适配器联系在一起

所以适配器和iic设备驱动最终注册框架图如下所示:

参考driver/i2c/chips/eeprom.c驱动,来写出24C02驱动以及测试程序

驱动代码步骤如下:

1.定义file_operations结构体 ,设置字符设备的读写函数(实现对24C02的读写操作)
? //构造i2c_msg结构体, 使用i2c_transfer()来实现与设备传输数据

2.定义i2c_client_address_data结构体,里面保存24C02的设备地址

3.?定义一个i2c_driver驱动结构体
?? ?3.1?设置i2c_driver-> attach_adapter
  ? ?//里面直接调用 i2c_probe(adap, i2c_client_address_data结构体, 回调函数);
?? ?3.2?设置i2c_driver-> detach_client
 ??????//里面卸载i2c_client, 字符设备

4.写回调函数,里面注册i2c_client,字符设备( 字符设备用来实现读写24C02里的数据)
?? ?4.1?分配并设置i2c_client
????4.2?使用i2c_attach_client()将i2c_client与适配器进行连接
?? ?4.3?注册字符设备

5.?写init入口函数,exit出口函数
init: 使用i2c_add_driver()注册i2c_driver
exit: 使用i2c_del_driver ()卸载i2c_driver

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-11-18 11:32:08  更:2021-11-18 11:33:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 0:48:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码