必发365bifa000028.Linux-IIC驱动(详解)

by admin on 2019年2月17日

上一节 大家学习了:  

隆冬了,一个人的都市,连被窝都是冷淡的,是时候考虑去东东亚避避寒了。

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

设若让自家采用,作者要么会挑选马来亚,那一个国家感到能让人不急不躁,值得细细观赏的国家,首先跟大家大饱眼福下东马的有个别游览经验

接下去本节,
学习Linux下怎么运用linux下I2C驱动系统布局来操作24C02

吉隆坡:这些都市是马来西亚京城,相比隆重,可以转正去过多国家,当然也相比较大。


洛杉矶一般自个儿皆以做转账城市,不购物就着力不在当地逗留。一般吃的摊子都集中在阿罗街,肉骨茶,娘惹菜都有。

 

在双子塔里好多东西都比国内便宜,假设想购物的,可以在双子塔购物,比如香水,化妆品,衣裳等一般都比国内便宜。到了清晨,双子塔灯光打上去,也正如可观,广场还有喷泉,在此间紧邻逛逛也是挺好的。

1.
I2C系统布局分析

当然人多的地方也正如杂,偷蒙拐骗也是最多的

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

在双子塔里,小编就碰着过二个自称华夏族的,年纪比较大,接近70了呢,过来跟本人搭话,用汉语跟作者沟通,首先跟我聊点旅游的,从哪儿来,去何地玩,然后就聊起了中华野史,什么抗日战争啊啥的,挺能聊的,最后聊到重点了,说他打车到双子塔的时候钱包落在出租车上了,让自个儿借个几百马币给他回家路费,还要本身的卡号,说回头给自家打钱。作者说让他报警,让警察协理,他不肯,非要借钱,说借几十块也足以的。小编很对不起的跟她说本人帮不了你。

 必发365bifa0000 1

单独广场,那里是个大广场,有很大的绿地,打算去那里玩的请留意,那里法丨论丨功信徒愈多,你在那里逛一下,只怕会有三七个信徒跟你搭讪,给你“洗脑”,叫你退党,去信法丨论丨功,然后会给您起法名。遭遇那种情况躲开就行,不要去信他们

其间紧要的文件介绍如下:

槟城:这边华人极度多,逛街宗旨都以足以中文沟通,门面招牌基本都是中文,走在乌鲁木齐,你会感觉在Hong Kong等同,沟通逛街无压力。

1)algos文件夹(algorithms)

夜宿可以住在隆重的格奥尔格e镇,然后在光大租个摩托车,可以随处浪了。

里头保存I2C的通讯方面的算法

不莱梅首要如故吃吃吃,滁州街到了上午五六点就从头摆出酒馆了,比较有名的有圣上鸡脚,炒粿条,四果汤,肉骨茶。鸡脚偏甜,肉煮的很烂,入口即化,但吃多了会腻。炒粿条感觉相似般,没泰王国阳春面好吃。肉骨茶看个人口味了,如果你耐得住那种中药味,照旧相比较好吃的,里面的食材有猪肉,猪肚,排骨等。四果汤是个甜饮品,个人感觉是最鲜美的,每一遍经过遵义街都会卷入一份,里面银耳、杏仁、龙眼和红菜豆等等,喜欢甜食的可以尝试。

2)busses文件夹

除此以外在莆田街拾分路口向南走一点,有山西鸡饭,也得以品尝,味道也还不易。

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

还有个比较知名的小吃叫煎蕊,其实是赤小豆冰,加上玉石白的米汤,浇上椰奶,口感尤其棒,值得一尝。

3) chips文件夹

贝洛奥里藏特玩的地点,可以看看街头水墨画,水墨画是比较粗放的,要去挨家挨户路口找,主要集中在姓氏桥那些样子,一般旅馆会提供油画的地形图,依照地图找,就很有利找到,入住客栈的时候可以多留意拿一张看看。

里面保存I2C装备驱动相关的文本,如下图所示,比如m41t00,就是XC60TC实时钟

其它推荐升旗山,体验下坐火车上山,山头有冷饮店,小吃店,还有个一家猫头鹰博物馆,里面有各式各个千奇百怪的猫头鹰小说,能够买个回想送人。

 必发365bifa0000 2

姓氏桥,比较知名的就是姓周桥了,电影《初恋赤豇豆冰》就是那里取的景。准确来说那不是个桥,其实是水上排屋,但很有生活气息,居民也很粗略和憨厚,很有怀旧风格,可以逛逛。

4) i2c-core.c

只要想买手信纪念品,能够去爱情巷逛逛,有众多小清新的回想品店,咖啡馆等.

那几个文件贯彻了I2C宗旨的效果(I2C总线的初始化、注册和适配器添加和注销等连锁工作)以及/proc/bus/i2c*接口。
5) i2c-dev.c
提供了通用的read( ) 、 write( )
和ioctl( )
等接口,完毕了I2C适配器设备文件的功效,其中I2C设备的主设备号都为89,
次设备号为0~255。

在光大有个电影院,电影都是带中文字母的,如果想看视频,可以在那里看,价格也不贵,价格基本在15马币左右

应用层可以借用这一个接口访问挂接在适配器上的I2C设备的囤积空间或寄存器,
并控制I2C设备的工作章程

兰卡威:甲米那一个城池,个人指出要悠闲的玩他三八日,到了机场,下了飞机,租个小小车,伊始浪啊。作者马上在机场租的车,拿中国驾照就能租到,也得以去支付宝免费领个驾照翻译件,当时小编租的是微型的马来亚国产车,租了八天,喊价220马币,最终砍到180马币,依然挺划算的。

家谕户晓,它和前两遍驱动类似,
I2C也分为总线驱动和装置驱动,总线就是说道相关的,它领会什么收发数据,但不知晓数码含义,设备驱动却通晓多少含义

毛里求斯是个岛屿,车流量少,路况好,油价便宜,租个车,悠闲的逛着,尤其是珍卡西诺海滩自驾去西北方向的那条路,两边都以树林,风景很好,很小清新,而且车很少很少,整条路基本都以你的,新手也统统不用担心开不佳。

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

珍巨人堤道,海上的玩乐设施恐怕挺多的,有拖拽伞,水翼船等等。这里的海鲜依然比较贵的,特别是网红店某浪餐厅,价格贵,人多,个人不太喜欢。街头的印度饭依旧不错的,就好像中国的快餐厅,他们的白米饭十分长很细,而且是用特制的香料蒸熟的,可以品味。如果逛了一天累了,推荐你个马杀鸡的地点,店名叫Teratai
Reflexology,拔火罐手法很热情洋溢,比起珍莫纳克亚海滩有名的alun-alun舒服的多,还利于。

 必发365bifa0000 3

瓜镇,其实就逛逛夜市和买点纪念品的地点,逛免税店的时候见到许多个人买巧克力,价格实在相比有利,喜欢吃巧克力的可以部分回来。巨鹰广场指出夜间去看,灯光打上去后,特别有质地,很宏伟,也相比较美观。

如上图所示,每一条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()则是投机内定总线号,倘诺这几个总线号不合法可能是被占据,就会登记失利.

星期一 Ulu Melaka镇上

礼拜五Kedawang,在珍浪往翁源县路上,经过kedawang村后的3岔路口,还没右转前就是

星期四 瓜镇(Kuah)商业区河畔,baron和My Hotel客栈前

星期五 珍Nang沙滩(Pantai Cenang),Pelangi彩虹酒店前,有条路直往800米

周六 Padang Lalang,丹绒鲁沙滩入口前的畅通圈,旁400米

周二 瓜镇(Kuah)商业区河畔,baron和My Hotel酒店前

周末 Padang Matsirat,机场往东1英里

 

天上之桥,惊奇的地点就是仅用一根柱子来辅助起接二连三两座山体的大桥,然后由8根钢丝绳牵引,整个桥被“吊”在了海拔600多米的太空,不得不惊讶那座桥。当时自个儿上山的时候,云雾缭绕,即使看不太领悟山下风景,但有一番名胜感觉。

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

去天空之桥,首先要先抵达东方村,然后买票坐缆车上山,55马币票价包涵上山和下山的来回缆车。须要专注的是缆车全程有八个下车点,第几个下车点在半山处,有个观景台,可以下车看看半山的景物,然后再上车到达山顶前往天空之桥。降雨或气候不好时候恐怕会倒闭景点,去从前留意气候变化。

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

别的珍南去天空桥的旅途,有家面包店,叫The
Loaf,在游船俱乐部哪个地方,咖啡是很有特点的,咖啡冻成冰块,然后给你一杯热牛奶,本身把热牛奶倒进去,就是一杯温咖啡了。还有冰淇淋也是,热面包上加一颗冰淇淋球端到您餐桌上,相比较有风味。上边附上地图:

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

在斯里兰卡西南角,有个红树林,可以在网上报三二十八日游,也可让饭馆前台帮您预订十九日游,一般当天去都能走,相比较原始的树丛,全程坐船上游览,森林中中国足球球协会一流联赛多猴子,船长还会带东西去嗨食老鹰。再往深处走可以去往蝙蝠洞,洞里相比较臭,用手电往上照,会看出密密麻麻倒挂着的蝙蝠,有凑数恐惧症的慎入。

 必发365bifa0000 4

马六甲:那些都市重点就是吃,其次就是看看古迹了。吃的最显赫就是沙爹了,推荐去佳必多,料很足,价格也不贵

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

再有就是鸡场街了,整条街都是吃的,但那条街上最让小编相比流连忘返的就是榴莲泡芙了,把泡芙一口咬进嘴里,那榴莲在嘴里爆浆的感觉,一流爽。招牌上会写着一口吃榴莲泡芙,可是街头有个别店也有山寨的,本身多留神下店面,正牌的店名应该叫Taste
Better

 

再引进个吃千层蛋糕的地点,店名叫Nadeje Patisserie
Cafe,地址在百盛的KFC旁边,店门在外头,不是在百盛里面。那里有七种二种的千层蛋糕。然而相对要多带几张嘴去,可以各买一种口味,轮流吃,可惜小编唯有一张嘴,当时确实是各个绝望。

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);
       ... ...
}

村办提议体验下马六甲旋转塔,整个马六甲尽收眼底,还能眺望整个马六甲海峡。票价23马币,人齐就起飞。

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

那时郑成功为马六甲做出过多贡献,未来过去还可以来看郑成功回想馆

 必发365bifa0000 5

有关西马,大约就介绍这么些了,还有别的题材来说,可以私笔者询问,东东亚有什么难点的可以随时提问,小编看出就会回涨。

 

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入口函数:

 必发365bifa0000 6

其中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

 必发365bifa0000 7

明朗,在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
()函数里第2实施以下几步:

放入到i2c_bus_type链表

取出adapters链表中有着的i2c_adapter,然后实施i2c_driver->attach_adapter()

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

 必发365bifa0000 8

 这里调用了i2c_driver
->attach_adapter(adapter),大家看看里面是否透过发送IIC设备地址,等待ACK响应来同盟的

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

 必发365bifa0000 9

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

 必发365bifa0000 10

上图的第1个参数就是i2c_adapter适配器,第2个参数addr_data变量,里面存放了IIC设备地址的音讯,第四个参数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()函数,如下图所示:

 必发365bifa0000 11

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起首信号则就是3个i2c_msg,如下五个读写操作图所示:

 必发365bifa0000 12

必发365bifa0000 13

而在i2c_transfer()函数中,最后又是调用了事先分析的i2c_adapter->algo->master_xfer()发送函数,如下图所示:

 必发365bifa0000 14

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

基本每发送3个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设备驱动最终注册框架图如下所示:

 必发365bifa0000 15

 

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为例,如下图所示:

 必发365bifa0000 16

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. 定义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.测试运营

如下图所示:

 必发365bifa0000 17

  

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图