葡京娱乐总站平台28.Linux-IIC让(详解)

齐亦然省 我们读了:  

葡京娱乐总站平台 1

IIC接口下之24C02 驱动分析: http://www.cnblogs.com/lifexy/p/7793686.html

眼看同次等很高兴能够请到梦行有谦工作室创始人谦行公子,到39次做关于“公众号排版的六脉络神剑”的享受。六脉神剑任起来有点微妙,通俗点也即是大众号排版课程纲要。

紧接下本节,
学习Linux下怎样采取linux下I2C使得系统布局来操作24C02

立即是一个完整的课程体系,不可能以短短的1单稍内讲得了,何况当晚之享用整整持续2独多时。


眼看无异说什么,不曾有已,可谓是干货满盈,让战友们还脑洞深起,受益无穷!

 

为之啊,我吧开课堂笔记,也不妨分享笔记为你们看。(PS:若看不顶懂得,可以怎么留言要原件!)

1.
I2C体系布局分析

葡京娱乐总站平台 2

1.1先是登linux内核的driver/i2c目录下,如下图所示:

简简单单思维导图

 葡京娱乐总站平台 3

总的来看这些情节,你内心是未是吗本着分享嘉宾点赞了!何况他要么95后!95晚什么!早以大三常,靠做自媒体月入过万。

内部主要之文书介绍如下:

回首自己大三不时,还在摆地摊,我代表深深地砸。对于年纪轻轻的少爷,对公众号排版也时有发生客的独具匠心的见。

1)algos文件夹(algorithms)

那么公子是哪个?请为下看:

内部保存I2C的通信方面的算法

谦行公子,4年自媒体人,梦行有谦工作室创始人,微信排版定制师,多只百万级别大号特约排版顾问。为100多个公众号做过排版咨询,涉及社会、娱乐、人文、教育、科技等等领域。在微信排版方面有独到见解。代表作《微信排版六脉神剑》。https://mp.weixin.qq.com/s/zdz8BpCVFm8vexNJ8NSikw

2)busses文件夹

本着,这即是他。更多造福而去公子公众号“谦行公子”做客。

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

好了!

重复议论,班级为何设开这么的等同不行分享?还要请400大多个战友一道围观,原因产生三。

先是,基于007不出局39吃2017年9月份业内开始,至今也发生4只月左右的时光,前后持续了4轱辘值月。班级整体过于风平浪静,这对于做社群来说,实在不好!

3) chips文件夹

因,整体情况是:

01.班层无交作业的战友增多,特别是“不出局”系统,尤为突出;02.值月生执行力力度不够,当然为发出作为班长我之案由;03.班级科普不做到,战友们人心涣散,视班级群匪厚;04.班级作业点评率差。具体详情,请复看班里刘子静战友(现在之入班长)的总:http://mp.weixin.qq.com/s/dzvFYsCHhlEXjM\_5y9UKSg

〔其中,自己以治本班级过程中犯的一无是处,过于信任战友等以班级中的积极,也有关过于信任自己管理力量。这话听起来如不怎么意外,似乎以抱怨,似乎战友等的坐就是是我保管之果然,并无是此意思!希望大家不用理解偏,单纯是上下一心认知上与管制及的贫乏〕

其中保存I2C设备驱动相关的文书,如下图所著,比如m41t00,就是RTC实时钟

因而,举办这样的一模一样坏分享会的来头有就是是判定过去、改变现状、开始新征程。

从,举办这次分享会的次独目的是,打造39次品牌,强化以007每班级之影响力。

 葡京娱乐总站平台 4

无独有偶使罗胖说“不做给取得用户丢脸的政工”。

以007面临,那我还要何尝不是这样想。不举行让战友等摒弃脸的政工,在39班,我吧无见面为战友等发身于39班而丢弃脸。不见面有朝一日,当您和另外班战友进行自我介绍时,有战友问你当几趟?你不好意思说是39班的。我无希望这样的工作来。这是本人于卖力的地方!也亟需班级战友等陪同我一同使劲!

说及此处,或许有人要质疑自己,凭啥能成就以众多007班级中脱颖而出?

尚别说,在一个不停写作和不断点评的社群中,只要每月做到“0勿及作业、0迟交作业”,仅这等同长,我想我们便会不辱使命。

何况,咱们39班还有许多煮龙藏虎的战友也!这就是是开分享会的亚独目的。

最终,这个由也是冲我们是一个作文社群,相信广大战友都以微信公众号排版上发出好多底问题,包括我呢是。第三只因是顺应时势需要,别随便她由。

4) i2c-core.c

说到这边,关于设立班级分享算是告一断落。

末尾使特别感谢这次班级付出的战友等,更多的凡班委会成员。他们是刘子静、刘晓雨、罗爱华、唐剑波、郭亚广、冯雷。特别是刘子静战友,处于养伤状态,也要是陪远道而来之父母亲,感谢他当百忙于中腾出时间也班级劳动。对了,这次的群众号排版六脉神剑的宣扬海报也是其计划的。(附图养养眼)

葡京娱乐总站平台 5

凡不是格外有逼格?要招她告私我

另外,咱们的刘晓雨为是,把白天温馨之办事时都因此当服务班级战友,自己倒是仅仅会经得住夜赶文件。对,那正是灯火阑珊处之她。也有罗爱华,郭亚广等战友。

以此间,真诚道平声:你们累了!

本条文件贯彻了I2C核心的职能(I2C总线的初始化、注册与适配器添加和撤回等相关工作)以及/proc/bus/i2c*接口。
5) i2c-dev.c
提供了通用的read( ) 、 write( )
和ioctl( )
等接口,实现了I2C适配器设备文件的效应,其中I2C设备的主设备号都也89,
次设备号为0~255。

于新的道中,班级做了以下几独面的调。

1.班层组建智襄团和适合班长(刘子静(副班长)、罗爱华、刘晓雨、唐剑波、郭亚广、冯雷。),也不怕是班委会。

2.众所周知了班级班规。这是通过班委会成员的决定,以及班级战友的投票决。其内容如下。

01.班级严格执行007行行红包(未到作业/迟交作业/未点评/请假围观)。践行红包统一交由值月生,作为班级资产投资,每月公布一糟糕收益状况。

02.班层禁言时间:24:00~06:00

03.作业点评由零星方面进行:

【值得学习之地方】和【需要改良的地方】

04.每月的评选一首优秀文章。评选规则:

由每个组长(共5组)从4浅作业中推介一首,然后一切战友票选产生“月度优秀作业”。

奖:该篇优胜文将至满战友转发/留言。

05.月度美妙文章的评选,从三点开展:

排版(20分)、喜欢为程度(50划分)、提供价值(30划分)。满分100分

06.各个月值月生带领组长负责统计与督察作业践行情况,在作业雨次日23:00前,公布每次作业和点评情况。

交者,我之感悟就停止了,或许有欠缺,也如宽容自己是当作业雨作业雨当天赶的。

若得清楚呢我只是于得学业任务,也可以掌握是我心目真正的独白,也足以视作你带班级之管理更。

末,我愿意做乃永远真诚之战友,和而精神及之对象。

应用层可以借这些接口访问挂接在适配器上之I2C设备的储存空间还是寄存器,
并控制I2C设备的干活方式

肯定,它和前几乎浅让类似,
I2C也分为总线驱动与配备驱动,总线就是商相关的,它了解怎样收发数据,但不知情多少含义,设备驱动却懂数据含义

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

 葡京娱乐总站平台 6

若果达到图所示,每一样条I2C对诺一个adapter适配器,在kernel中,
adapter适配器是经struct adapter结构体定义,主要是透过i2c
core层将i2c设备及i2c adapter关联起来.

在kernel中提供了少于单adapter注册接口,分别吗i2c_add_adapter()和i2c_add_numbered_adapter().由于当系统中可能是多单adapter,因为用各个一样久I2C总线对应一个号,下文中称I2C总线号.这个总线号的PCI中之总线号不同.它和硬件无关,只是软件达到便宜区分而已.

对于i2c_add_adapter()而言,它采取的凡动态总线号,即由于系统被那分析一个总线号,而i2c_add_numbered_adapter()则是团结指定总线号,如果是总线号非法或是受占据,就会报失败.

 

2.接下来就是来分析I2C总线驱动

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

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

 葡京娱乐总站平台 7

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

 

3.进入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_adapter结构体是坐落s3c24xx_i2c->adap下,如下图所示:

 葡京娱乐总站平台 8

 

4.接下来我们进去i2c_add_adapter()函数看看,到底怎么样注册之

int i2c_add_adapter(struct i2c_adapter *adapter)
{
       int   id, res = 0;

retry:
       if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) //调用idr_pre_get()为i2c_adapter预留内存空间
              return -ENOMEM;

       mutex_lock(&core_lists);

       /* "above" here means "above or equal to", sigh */
       res = idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id);
       //调用idr_get_new_above()将结构插入i2c_adapter_idr中,并将插入的位置赋给id,以后可以通过id在i2c_adapter_idr中找到相应的i2c_adapter结构体

       mutex_unlock(&core_lists);

       if (res < 0) {
              if (res == -EAGAIN)
                    goto retry;
              return res;
       }
       adapter->nr = id;
       return i2c_register_adapter(adapter);  //调用i2c_register_adapter()函数进一步来注册.
}

其中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设备调整出去,执行i2c_driver设备的attach_adapter函数来配合

其中,
i2c_driver结构体会在后讲述到

而i2c_adapter适配器结构体的分子组织,如下所示:

struct i2c_adapter {  

 struct module *owner;              //所属模块  
 unsigned int id;                //algorithm的类型,定义于i2c-id.h,  
 unsigned int class;      
 const struct i2c_algorithm *algo;     //总线通信方法结构体指针  
 void *algo_data;               //algorithm数据  
 struct rt_mutex bus_lock;        //控制并发访问的自旋锁  
 int timeout;     
 int retries;                //重试次数  
 struct device dev;             //适配器设备   
 int nr;                          //存放在i2c_adapter_idr里的位置号
 char name[48];              //适配器名称  
 struct completion dev_released;    //用于同步  
 struct list_head userspace_clients;   //client链表头  

};  

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

5.其中s3c24xx_i2c的结构体成员如下所示

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {            
       .master_xfer          = s3c24xx_i2c_xfer,  //主机传输
       .functionality          = s3c24xx_i2c_func,                    
};

static struct s3c24xx_i2c s3c24xx_i2c = {
       .lock              = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),
       .wait              = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
       .tx_setup = 50,                        //用来延时,等待SCL被释放
       .adap             = {                                             // i2c_adapter适配器结构体
              .name                   = "s3c2410-i2c",
              .owner                  = THIS_MODULE,
              .algo                     = &s3c24xx_i2c_algorithm,           //存放i2c_algorithm算法结构体
              .retries           = 2,                                       //重试次数
              .class                    = I2C_CLASS_HWMON,
       },
};

显著这里是直设置了i2c_adapter结构体,所以在s3c24xx_i2c_probe
()函数中没有分配i2c_adapter适配器结构体,

其中,
i2c_adapter结构体的名号等”s3c2410-i2c”,它的通信方式等s3c24xx_i2c_algorithm,重试次数等于2

PS:如果缺i2c_algorithm的i2c_adapter什么也举行不了,就单纯是只I2C设备,而尚未通信方式

s3c24xx_i2c_algorithm中之重中之重函数master_xfer()就是用来产生i2c访问周期要之start stop ack等信号

比如,在s3c24xx_i2c_algorithm中之主要函数master_xfer()里,调用了:

s3c24xx_i2c_xfer ->
s3c24xx_i2c_doxfer()->s3c24xx_i2c_message_start()

来启动传输message信息, 其中s3c24xx_i2c_message_start()函数代码如下:

static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg)
{

 unsigned int addr = (msg->addr & 0x7f) << 1;              //IIC从设备地址的最低位为读写标志位
       ... ...

       stat = 0;
       stat |=  S3C2410_IICSTAT_TXRXEN;     //设置标志位启动IIC收发使能

       if (msg->flags & I2C_M_RD) {                     //判断是读,还是写
              stat |= S3C2410_IICSTAT_MASTER_RX;       
              addr |= 1;                                          //设置从IIC设备地址为读标志
       } else
              stat |= S3C2410_IICSTAT_MASTER_TX;

       s3c24xx_i2c_enable_ack(i2c);                //使能ACK信号

    iiccon = readl(i2c->regs + S3C2410_IICCON);    //读出IICCON寄存器

       writel(stat, i2c->regs + S3C2410_IICSTAT);   //写入IICSTAT寄存器,使能IIC的读或写标志

       dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);

       writeb(addr, i2c->regs + S3C2410_IICDS);  //将IIC从设备地址写入IICDS寄存器

       /* delay here to ensure the data byte has gotten onto the bus
        * before the transaction is started */

       ndelay(i2c->tx_setup);         //延时,等待SCL被释放,下面便可以发送起始信号+IIC设备地址值


       dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
       writel(iiccon, i2c->regs + S3C2410_IICCON);            

       stat |=  S3C2410_IICSTAT_START;              
       writel(stat, i2c->regs + S3C2410_IICSTAT); 
            //设置IICSTAT寄存器的bit5=1,开始发送起始信号+IIC从设备地址值,并回应ACK
}

由此上面的代码和注释,发现主要是描写副IIC从设备地址,然后发送起始信号+IIC从设备地址值,并回复ACK

显IIC总线驱动i2c-s3c2410.c,主要装适配器adapter,里面拉扯我们做好了IIC通信的架,就是匪懂得发啊内容

俺们进driver/i2c/chips中,看看eeprom设备驱动是怎么勾勒的

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

6.尚是首先来拘禁它们的init入口函数:

 葡京娱乐总站平台 9

其中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从设备
};

正如图所示,
eeprom_driver结构体的ID成员以i2c-id.h中,里面还定义了绝大多数常用I2C装置驱动的装置ID

 葡京娱乐总站平台 10

妇孺皆知,在init函数中通过i2c_add_driver()注册i2c_driver结构体,然后经过i2c_driver
->attach_adapter来匹配内核中的依次总线驱动之适配器,
发送这个装置地址,若有ACK响应,表示相当成功

7.接下来,我们进去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设备驱动注册框架如下所示:

 葡京娱乐总站平台 11

 这里调用了i2c_driver
->attach_adapter(adapter),我们省里面是不是由此发送IIC设备地址,等待ACK响应来配合的

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

 葡京娱乐总站平台 12

一般来说图所示,里面调用了i2c_probe(adapter,
&addr_data, eeprom_detect)函数

 葡京娱乐总站平台 13

达图的第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_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地址,表示强制支持

};

诚如而言,都未会见设置.forces成员,这里仅是由个如

8.1接下继续进入i2c_probe()函数继续分析,如下所示:

int i2c_probe(struct i2c_adapter *adapter,struct i2c_client_address_data *address_data,int (*found_proc) (struct i2c_adapter *, int, int))
{
       ... ...
       err = i2c_probe_address(adapter,forces[kind][i + 1],kind, found_proc);
}

中调用了i2c_probe_address()函数,从名称上来拘禁,显然它们就是是因此来发送起始信号+设备地址,来探测IIC设备地址用底

8.2进入i2c_probe_address()函数:

static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,int (*found_proc) (struct i2c_adapter *, int, int))
{

       /*判断设备地址是否有效,addr里存放的是设备地址前7位,比如AT24C02=0xA0,那么addr=0x50*/
       if (addr < 0x03 || addr > 0x77) {
              dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",addr);    //打印地址无效,并退出
              return -EINVAL;
       }

       /*查找链表中其它IIC设备的设备地址,若这个设备地址已经被使用,则return*/
       if (i2c_check_addr(adapter, addr))
              return 0; 

       if (kind < 0) {
              if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,I2C_SMBUS_QUICK, NULL) < 0)      //进入I2C传输函数
         return 0;
       ... ...
}

 

8.3
其中i2c_smbus_xfer()传输函数如下:

s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,char read_write, u8 command, int size,union i2c_smbus_data * data)
{
       s32 res;

       flags &= I2C_M_TEN | I2C_CLIENT_PEC;

       if (adapter->algo->smbus_xfer) {   //如果adapter适配器有smbus_xfer这个函数
              mutex_lock(&adapter->bus_lock);                            //加互斥锁
              res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,command,size,data);  
                                            //调用adapter适配器里的传输函数
              mutex_unlock(&adapter->bus_lock);                  //解互斥锁
       } else                          //否则使用默认函数传输设备地址
              res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,command,size,data);
       return res;
}

扣押了方代码后,显然我们的s3c2410-i2c适配器没有algo->smbus_xfer函数,而是采用i2c_smbus_xfer_emulated()函数,如下图所示:

 葡京娱乐总站平台 14

PS:通常适配器都是休支持之,使用默认的i2c_smbus_xfer_emulated()函数

8.4
接下看i2c_smbus_xfer_emulated()函数如何传输的:

static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,unsigned short flags,char read_write, u8 command, int size, union i2c_smbus_data * data)
{
       unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];              //属于 msg[0]的buf成员
       unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];              //属于 msg[1]的buf成员
       int num = read_write == I2C_SMBUS_READ?2:1;              //如果为读命令,就等于2,表示要执行两次数据传输
       struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },
                    { addr, flags | I2C_M_RD, 0, msgbuf1 }};           //定义两个i2c_msg结构体,


       msgbuf0[0] = command;             //IIC设备地址最低位为读写命令
       ... ...

if (i2c_transfer(adapter, msg, num) < 0)
              return -1;

              /*设置i2c_msg结构体成员*/
              if (read_write == I2C_SMBUS_READ)
              switch(size) {
              ... ...
              case I2C_SMBUS_BYTE_DATA:              //如果是读字节
              if (read_write == I2C_SMBUS_READ)
                     msg[1].len = 1;
              else {
                     msg[0].len = 2;
                     msgbuf0[1] = data->byte;
              }
              break;
              ... ...
              }
       ... ...

       if (i2c_transfer(adapter, msg, num) < 0)             //将 i2c_msg结构体的内容发送给I2C设备
              return -1;
       ... ...
}

其中i2c_msg结构体的构造,如下所示:

struct i2c_msg {
       __u16 addr;          //I2C从机的设备地址
       __u16 flags;           //当flags=0表示写, flags= I2C_M_RD表示读
       __u16 len;              //传输的数据长度,等于buf数组里的字节数
       __u8 *buf;              //存放数据的数组
};

地方代码中因故读操作需要简单单i2c_msg,写操作需要一个i2c_msg,是因读IIC设备是有限独流程

于达成同节省IIC接口下的24C02
驱动分析:
http://www.cnblogs.com/lifexy/p/7793686.html里便曾分析到了,

假定发送一个S起始信号则就是一个i2c_msg,如下两只读写操作图所示:

 葡京娱乐总站平台 15

葡京娱乐总站平台 16

而在i2c_transfer()函数中,最终以是调用了之前分析的i2c_adapter->algo->master_xfer()发送函数,如下图所示:

 葡京娱乐总站平台 17

其中i2c_transfer()的参数*adap表示经过哪个适配器传输出去,msgs表示I2C消息,num表示msgs的数额

根本每发送一个Msg都见面先发出S开始信号和配备地址.直到具有Msg传输完毕,最后来P停止信号。

当i2c_transfer()返回值为正数,表示都传正数个数据,当回负数,说明I2C传输出错

 

8.5 所以在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设备驱动最终注册框架图如下所示:

 葡京娱乐总站平台 18

 

9.接下来就是来分析回调函数如何注册i2c_client从设备的

优先来探望i2c_client结构体:

struct i2c_client {  

 unsigned short flags;//标志    

 unsigned short addr; //该i2c从设备的设备地址,存放地址高7位  

 char name[I2C_NAME_SIZE];   //设备名字

 struct i2c_adapter *adapter;//依附的i2c_adapter,表示该IIC设备支持哪个适配器  

 struct i2c_driver *driver;//依附的i2c_driver ,表示该IIC从设备的驱动是哪个

 struct device dev;//设备结构体    

 int irq;//设备所使用的结构体    

 struct list_head detected;//链表头  

 };  

或者坐driver/i2c/chips/eeprom.c为例,如下图所示:

 葡京娱乐总站平台 19

9.1此的回调函数是eeprom_detect()函数,代码如下所示:

static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;        //定义一个i2c_client结构体局部变量

new_client =kzalloc(sizeof(struct i2c_client), GFP_KERNEL);      //分配i2c_client结构体为全局变量


/*设置i2c_client结构体*/
new_client->addr = address;               //设置设备地址
new_client->adapter = adapter;          //设置依附的i2c_adapter
new_client->driver = &eeprom_driver;  //设置依附的i2c_driver
new_client->flags = 0;                         //设置标志位为初始值
strlcpy(new_client->name, "eeprom", I2C_NAME_SIZE);     //设置名字


 /*注册i2c_client*/
 if ((err = i2c_attach_client(new_client)))
        goto exit_kfree;    //注册失败,便释放i2c_client这个全局变量
 ... ...

exit_kfree:
       kfree(new_client);
exit:
       return err;
}

当注册了i2c_client从设备后,便可动用i2c_transfer()来实现与设施传输数据了

 

10.接下来,我们就参考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

 

具体驱动代码如下所示:

/*
 *  I2C-24C02
 */
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

static struct i2c_client *at24c02_client;         //从设备结构体
static struct class *at24c02_class;                //类结构体
static unsigned int at24c02_major;                 

 /*1.定义file_operations结构体 ,
  *  设置字符设备的读写函数(实现对24C02的读写操作)
  */
static ssize_t at24c02_read(struct file *file, char __user *buf, size_t size, loff_t * offset)
{
       struct i2c_msg msg[2];
       u8 addr;
       u8 data;
       int ret;

        if(size!=1)
            return -EINVAL;

       copy_from_user(&addr,buf,1);                       //获取读地址

        msg[0].addr=at24c02_client->addr;
        msg[0].flags=0;                                            //写标志
        msg[0].len  =1;
        msg[0].buf  =&addr;                                     //写入要读的地址

        msg[1].addr=at24c02_client->addr;
        msg[1].flags=I2C_M_RD;                               //读标志
        msg[1].len  =1;
        msg[1].buf  =&data;                                     //读出数据 

        ret=i2c_transfer(at24c02_client->adapter, msg, 2);     
        if(ret==2)      //表示2个msg传输成功
        {
             copy_to_user(buf,&data,1);                       //上传数据       
             return 0;
        }
        else
            return -EAGAIN;
}

static ssize_t at24c02_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
       struct i2c_msg msg[1];
       u8 val[2];      
       int ret;

        if(size!=2)         //地址   数据
            return -EINVAL;

        copy_from_user(val,buf,2);                       //获取 地址   数据

        msg[0].addr=at24c02_client->addr;
        msg[0].flags=0;                                       //写标志
        msg[0].len  =2;
        msg[0].buf  =val;                                     //写入要写的地址   数据

        ret=i2c_transfer(at24c02_client->adapter, msg, 1);     
        if(ret==1)      //表示1个msg传输成功
        {           
             return 0;
        }
        else
            return -EAGAIN;
}

static struct  file_operations at24c02_fops={
        .owner = THIS_MODULE,
    .read  = at24c02_read,
    .write = at24c02_write,
};


/*2.定义i2c_client_address_data结构体,保存24C02的设备地址*/
static unsigned short ignore[] = { I2C_CLIENT_END };
static unsigned short normal_addr[] = {0X50,  I2C_CLIENT_END };
static unsigned short   force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};
static unsigned short   * forces[] =     {force_addr, NULL};    

static struct i2c_client_address_data   at24c02_addr={
            .normal_i2c=normal_addr,
            .probe=ignore,
            .ignore=ignore,
           //  .forces=forces,                  // 强制地址
};

/*3. 定义一个i2c_driver驱动结构体*/
static int   at24c02_attach_adapter(struct i2c_adapter *adapter);
static int   at24c02_detach_client(struct i2c_client *client);
static int at24c02_detect(struct i2c_adapter *adap, int addr, int kind);

/* This is the driver that will be inserted */
static struct i2c_driver at24c02_driver = {
    .driver = {
        .name    = "at24c02",
    },

    .attach_adapter    = at24c02_attach_adapter,       //绑定回调函数
    .detach_client    = at24c02_detach_client,                //解绑回调函数
};

/*3.1 设置i2c_driver-> attach_adapter*/
static int   at24c02_attach_adapter(struct i2c_adapter *adapter)
{
        return i2c_probe(adapter,&at24c02_addr, at24c02_detect);
}


/*3.2 设置i2c_driver-> detach_client*/
static int   at24c02_detach_client(struct i2c_client *client)
{
    printk("at24c02_detach_client\n");

    i2c_detach_client(at24c02_client) ;   
    kfree(at24c02_client);

    class_device_destroy(at24c02_class,MKDEV(at24c02_major, 0));
    class_destroy(at24c02_class);

    return 0;
}

/*4.写回调函数,里面注册i2c_client,字符设备*/ 
static int at24c02_detect(struct i2c_adapter *adap, int addr, int kind)
{
   printk("at24c02_detect\n");

    /* 4.1 分配并设置i2c_client */
    at24c02_client= kzalloc(sizeof(struct i2c_client), GFP_KERNEL);

    at24c02_client->addr = addr;
    at24c02_client->adapter = adap;
    at24c02_client->driver = &at24c02_driver;
    at24c02_client->flags = 0;
    strlcpy(at24c02_client->name, "at24c02", I2C_NAME_SIZE);

   /*4.2 使用i2c_attach_client()将i2c_client与适配器进行连接*/
    i2c_attach_client(at24c02_client) ;

    /*4.3 注册字符设备*/
    at24c02_major= register_chrdev(0, "at24c02", &at24c02_fops);  
    at24c02_class=class_create(THIS_MODULE, "at24c02");   
    class_device_create(at24c02_class,0, MKDEV(at24c02_major, 0),0,"at24c02");
     return 0;
}


/*5. 写init入口函数,exit出口函数*/
static int at24c02_init(void)
{
    i2c_add_driver(&at24c02_driver);
    return 0;
}
static void at24c02_exit(void)
{
    i2c_del_driver(&at24c02_driver);
}

module_init(at24c02_init);
module_exit(at24c02_exit);
MODULE_LICENSE("GPL");

 

11.测试运行

一般来说图所示:

 葡京娱乐总站平台 20