日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

linux下IIC驱动解释

發(fā)布時(shí)間:2023/12/8 linux 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux下IIC驱动解释 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

原文地址:https://www.cnblogs.com/lifexy/p/7816324.html

1. I2C體系結(jié)構(gòu)分析

1.1首先進(jìn)入linux內(nèi)核的driver/i2c目錄下,如下圖所示:

?

其中重要的文件介紹如下:

1)algos文件夾(algorithms)

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

2)busses文件夾

里面保存I2C總線驅(qū)動(dòng)相關(guān)的文件,比如i2c-omap.c、 i2c-versatile.c、 i2c-s3c2410.c等。

3)?chips文件夾

里面保存I2C設(shè)備驅(qū)動(dòng)相關(guān)的文件,如下圖所示,比如m41t00,就是RTC實(shí)時(shí)鐘

?

4)?i2c-core.c
這個(gè)文件實(shí)現(xiàn)了I2C核心的功能(I2C總線的初始化、注冊(cè)和適配器添加和注銷(xiāo)等相關(guān)工作)以及/proc/bus/i2c*接口。
5)?i2c-dev.c
提供了通用的read( ) 、 write( ) 和ioctl( ) 等接口,實(shí)現(xiàn)了I2C適配器設(shè)備文件的功能,其中I2C設(shè)備的主設(shè)備號(hào)都為89, 次設(shè)備號(hào)為0~255。
應(yīng)用層可以借用這些接口訪問(wèn)掛接在適配器上的I2C設(shè)備的存儲(chǔ)空間或寄存器, 并控制I2C設(shè)備的工作方式

顯然,它和前幾次驅(qū)動(dòng)類似, I2C也分為總線驅(qū)動(dòng)和設(shè)備驅(qū)動(dòng),總線就是協(xié)議相關(guān)的,它知道如何收發(fā)數(shù)據(jù),但不知道數(shù)據(jù)含義,設(shè)備驅(qū)動(dòng)卻知道數(shù)據(jù)含義

1.2 I2C驅(qū)動(dòng)架構(gòu),如下圖所示:

?

如上圖所示,每一條I2C對(duì)應(yīng)一個(gè)adapter適配器,在kernel中, adapter適配器是通過(guò)struct adapter結(jié)構(gòu)體定義,主要是通過(guò)i2c core層將i2c設(shè)備與i2c adapter關(guān)聯(lián)起來(lái).

在kernel中提供了兩個(gè)adapter注冊(cè)接口,分別為i2c_add_adapter()和i2c_add_numbered_adapter().由于在系統(tǒng)中可能存在多個(gè)adapter,因?yàn)閷⒚恳粭lI2C總線對(duì)應(yīng)一個(gè)編號(hào),下文中稱為I2C總線號(hào).這個(gè)總線號(hào)的PCI中的總線號(hào)不同.它和硬件無(wú)關(guān),只是軟件上便于區(qū)分而已.

對(duì)于i2c_add_adapter()而言,它使用的是動(dòng)態(tài)總線號(hào),即由系統(tǒng)給其分析一個(gè)總線號(hào),而i2c_add_numbered_adapter()則是自己指定總線號(hào),如果這個(gè)總線號(hào)非法或者是被占用,就會(huì)注冊(cè)失敗.

?

2.接下來(lái)便來(lái)分析I2C總線驅(qū)動(dòng)

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

先進(jìn)入init入口函數(shù),如下圖所示:

?

在init函數(shù)中,注冊(cè)了一個(gè)?“s3c2440-i2c”的platform_driver平臺(tái)驅(qū)動(dòng),我們來(lái)看看probe函數(shù)做了些什么

?

3.進(jìn)入s3c24xx_i2c_probe函數(shù)

struct i2c_adapter adap;static int s3c24xx_i2c_probe(struct platform_device *pdev) {struct s3c24xx_i2c *i2c = &s3c24xx_i2c;... .../*獲取,使能I2C時(shí)鐘*/i2c->clk = clk_get(&pdev->dev, "i2c"); //獲取i2c時(shí)鐘clk_enable(i2c->clk); //使能i2c時(shí)鐘... ..../*獲取資源*/res = platform_get_resource(pdev, IORESOURCE_MEM, 0);i2c->regs = ioremap(res->start, (res->end-res->start)+1);... ..../*設(shè)置i2c_adapter適配器結(jié)構(gòu)體, 將i2c結(jié)構(gòu)體設(shè)為adap的私有數(shù)據(jù)成員*/i2c->adap.algo_data = i2c; //i2c_adapter適配器指向s3c24xx_i2c;i2c->adap.dev.parent = &pdev->dev;/* initialise the i2c controller *//*初始化2440的I2C相關(guān)的寄存器*/ret = s3c24xx_i2c_init(i2c);if (ret != 0)goto err_iomap;... .../*注冊(cè)中斷服務(wù)函數(shù)*/ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,pdev->name, i2c);... .../*注冊(cè)i2c_adapter適配器結(jié)構(gòu)體*/ret = i2c_add_adapter(&i2c->adap);... ... }

其中i2c_adapter結(jié)構(gòu)體是放在s3c24xx_i2c->adap下,如下圖所示:

?

?

4.接下來(lái)我們進(jìn)入i2c_add_adapter()函數(shù)看看,到底如何注冊(cè)的

int i2c_add_adapter(struct i2c_adapter *adapter) {int id, res = 0;retry:if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) //調(diào)用idr_pre_get()為i2c_adapter預(yù)留內(nèi)存空間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);//調(diào)用idr_get_new_above()將結(jié)構(gòu)插入i2c_adapter_idr中,并將插入的位置賦給id,以后可以通過(guò)id在i2c_adapter_idr中找到相應(yīng)的i2c_adapter結(jié)構(gòu)體mutex_unlock(&core_lists);if (res < 0) {if (res == -EAGAIN)goto retry;return res;}adapter->nr = id;return i2c_register_adapter(adapter); //調(diào)用i2c_register_adapter()函數(shù)進(jìn)一步來(lái)注冊(cè). }

其中i2c_register_adapter()函數(shù)代碼如下所示:

static int i2c_register_adapter(struct i2c_adapter *adap) {struct list_head *item; //鏈表頭,用來(lái)存放i2c_driver結(jié)構(gòu)體的表頭struct i2c_driver *driver; //i2c_driver,用來(lái)描述一個(gè)IIC設(shè)備驅(qū)動(dòng)list_add_tail(&adap->list, &adapters); //添加到內(nèi)核的adapter鏈表中... ...list_for_each(item,&drivers) { //for循環(huán),從drivers鏈表里找到i2c_driver結(jié)構(gòu)體的表頭driver = list_entry(item, struct i2c_driver, list); //通過(guò)list_head表頭,找到i2c_driver結(jié)構(gòu)體if (driver->attach_adapter) /* We ignore the return code; if it fails, too bad */driver->attach_adapter(adap); //調(diào)用i2c_driver的attach_adapter函數(shù)來(lái)看看,這個(gè)新注冊(cè)的設(shè)配器是否支持i2c_driver} }

在i2c_register_adapter()函數(shù)里主要執(zhí)行以下幾步:

將adapter放入i2c_bus_type的adapter鏈表
將所有的i2c設(shè)備調(diào)出來(lái),執(zhí)行i2c_driver設(shè)備的attach_adapter函數(shù)來(lái)匹配

其中, i2c_driver結(jié)構(gòu)體會(huì)在后面講述到

而i2c_adapter適配器結(jié)構(gòu)體的成員結(jié)構(gòu),如下所示:

struct i2c_adapter { struct module *owner;              //所屬模塊 unsigned int id;                //algorithm的類型,定義于i2c-id.h, unsigned int class; const struct i2c_algorithm *algo;     //總線通信方法結(jié)構(gòu)體指針 void *algo_data;             //algorithm數(shù)據(jù) struct rt_mutex bus_lock;        //控制并發(fā)訪問(wèn)的自旋鎖 int timeout; int retries;                //重試次數(shù) struct device dev;             //適配器設(shè)備 int nr;         //存放在i2c_adapter_idr里的位置號(hào)char name[48];              //適配器名稱 struct completion dev_released;    //用于同步 struct list_head userspace_clients; //client鏈表頭 };

i2c_adapter表示物理上的一個(gè)i2C設(shè)備(適配器), 在i2c-s3c2410.c中,是存放在s3c24xx_i2c結(jié)構(gòu)體下的(struct ?i2c_adapter ?adap)成員中

5.其中s3c24xx_i2c的結(jié)構(gòu)體成員如下所示

static const struct i2c_algorithm s3c24xx_i2c_algorithm = { .master_xfer = s3c24xx_i2c_xfer, //主機(jī)傳輸.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, //用來(lái)延時(shí),等待SCL被釋放.adap = { // i2c_adapter適配器結(jié)構(gòu)體.name = "s3c2410-i2c",.owner = THIS_MODULE,.algo = &s3c24xx_i2c_algorithm, //存放i2c_algorithm算法結(jié)構(gòu)體.retries = 2, //重試次數(shù).class = I2C_CLASS_HWMON,}, };

顯然這里是直接設(shè)置了i2c_adapter結(jié)構(gòu)體,所以在s3c24xx_i2c_probe ()函數(shù)中沒(méi)有分配i2c_adapter適配器結(jié)構(gòu)體,

其中, i2c_adapter結(jié)構(gòu)體的名稱等于"s3c2410-i2c",它的通信方式等于s3c24xx_i2c_algorithm,重試次數(shù)等于2

PS:如果缺少i2c_algorithm的i2c_adapter什么也做不了,就只是個(gè)I2C設(shè)備,而沒(méi)有通信方式

s3c24xx_i2c_algorithm中的關(guān)鍵函數(shù)master_xfer()就是用于產(chǎn)生i2c訪問(wèn)周期需要的start stop ack等信號(hào)

比如,在s3c24xx_i2c_algorithm中的關(guān)鍵函數(shù)master_xfer()里,調(diào)用了:

s3c24xx_i2c_xfer -> s3c24xx_i2c_doxfer()->s3c24xx_i2c_message_start()

來(lái)啟動(dòng)傳輸message信息, 其中s3c24xx_i2c_message_start()函數(shù)代碼如下:

static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg) {unsigned int addr = (msg->addr & 0x7f) << 1; //IIC從設(shè)備地址的最低位為讀寫(xiě)標(biāo)志位... ...stat = 0;stat |= S3C2410_IICSTAT_TXRXEN; //設(shè)置標(biāo)志位啟動(dòng)IIC收發(fā)使能if (msg->flags & I2C_M_RD) { //判斷是讀,還是寫(xiě)stat |= S3C2410_IICSTAT_MASTER_RX; addr |= 1; //設(shè)置從IIC設(shè)備地址為讀標(biāo)志} elsestat |= S3C2410_IICSTAT_MASTER_TX;s3c24xx_i2c_enable_ack(i2c); //使能ACK信號(hào)iiccon = readl(i2c->regs + S3C2410_IICCON); //讀出IICCON寄存器writel(stat, i2c->regs + S3C2410_IICSTAT); //寫(xiě)入IICSTAT寄存器,使能IIC的讀或?qū)憳?biāo)志dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);writeb(addr, i2c->regs + S3C2410_IICDS); //將IIC從設(shè)備地址寫(xiě)入IICDS寄存器/* delay here to ensure the data byte has gotten onto the bus* before the transaction is started */ndelay(i2c->tx_setup); //延時(shí),等待SCL被釋放,下面便可以發(fā)送起始信號(hào)+IIC設(shè)備地址值dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);writel(iiccon, i2c->regs + S3C2410_IICCON); stat |= S3C2410_IICSTAT_START; writel(stat, i2c->regs + S3C2410_IICSTAT); //設(shè)置IICSTAT寄存器的bit5=1,開(kāi)始發(fā)送起始信號(hào)+IIC從設(shè)備地址值,并回應(yīng)ACK }

通過(guò)上面的代碼和注釋,發(fā)現(xiàn)主要是寫(xiě)入IIC從設(shè)備地址,然后發(fā)送起始信號(hào)+IIC從設(shè)備地址值,并回應(yīng)ACK

顯然IIC總線驅(qū)動(dòng)i2c-s3c2410.c,主要設(shè)置適配器adapter,里面幫我們做好了IIC通信的架構(gòu),就是不知道發(fā)什么內(nèi)容

我們進(jìn)入driver/i2c/chips中,看看eeprom設(shè)備驅(qū)動(dòng)是如何寫(xiě)的

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

6.還是首先來(lái)看它的init入口函數(shù):

?

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

static struct i2c_driver eeprom_driver = {.driver = {.name = "eeprom", //名稱},.id = I2C_DRIVERID_EEPROM, //IIC設(shè)備標(biāo)識(shí)ID.attach_adapter = eeprom_attach_adapter, //用來(lái)與總線驅(qū)動(dòng)的適配器匹配,匹配成功添加到適配器adapter中.detach_client = eeprom_detach_client, //與總線驅(qū)動(dòng)的適配器解綁,分離這個(gè)IIC從設(shè)備 };

如下圖所示, eeprom_driver結(jié)構(gòu)體的ID成員在i2c-id.h中,里面還定義了大部分常用I2C設(shè)備驅(qū)動(dòng)的設(shè)備ID

?

顯然,在init函數(shù)中通過(guò)i2c_add_driver()注冊(cè)i2c_driver結(jié)構(gòu)體,然后通過(guò)i2c_driver ->attach_adapter來(lái)匹配內(nèi)核中的各個(gè)總線驅(qū)動(dòng)的適配器, 發(fā)送這個(gè)設(shè)備地址,若有ACK響應(yīng),表示匹配成功

7.接下來(lái),我們進(jìn)入i2c_add_driver()來(lái)看看是不是這樣的

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); //注冊(cè)一個(gè)i2c_driver... ...if (driver->attach_adapter) {struct i2c_adapter *adapter; //定義一個(gè)i2c_adapter適配器list_for_each_entry(adapter, &adapters, list) //for循環(huán)提取出adapters鏈表中所有的i2c_adapter適配器,放入到adapter結(jié)構(gòu)體中{driver->attach_adapter(adapter); //來(lái)匹配取出來(lái)的i2c_adapter適配器}}... ... return 0; }

在i2c_add_driver ()函數(shù)里主要執(zhí)行以下幾步:

放入到i2c_bus_type鏈表

取出adapters鏈表中所有的i2c_adapter,然后執(zhí)行i2c_driver->attach_adapter()

?

所以i2c_adapter適配器和i2c_driver設(shè)備驅(qū)動(dòng)注冊(cè)框架如下所示:

?

?這里調(diào)用了i2c_driver ->attach_adapter(adapter),我們看看里面是不是通過(guò)發(fā)送IIC設(shè)備地址,等待ACK響應(yīng)來(lái)匹配的

8.以struct i2c_driver eeprom_driver 為例,進(jìn)入i2c_driver ->eeprom_attach_adapter()函數(shù)

?

如下圖所示,里面調(diào)用了i2c_probe(adapter, &addr_data, eeprom_detect)函數(shù)

?

上圖的第1個(gè)參數(shù)就是i2c_adapter適配器,第2個(gè)參數(shù)addr_data變量,里面存放了IIC設(shè)備地址的信息,第3個(gè)參數(shù)eeprom_detect就是具體的設(shè)備探測(cè)回調(diào)函數(shù)i2c_probe()函數(shù),會(huì)通過(guò)adapter適配器發(fā)送IIC設(shè)備地址addr_data,如果收到ACK信號(hào),就調(diào)用eeprom_detect()回調(diào)函數(shù)來(lái)注冊(cè)i2c_client結(jié)構(gòu)體,該結(jié)構(gòu)體對(duì)應(yīng)真實(shí)的物理從設(shè)備,而i2c_driver對(duì)應(yīng)的是設(shè)備驅(qū)動(dòng),也就是說(shuō),只有當(dāng)適配器支持這個(gè)設(shè)備驅(qū)動(dòng),才會(huì)注冊(cè)i2c_client從設(shè)備,后面會(huì)講這個(gè)回調(diào)函數(shù)如何注冊(cè)i2c_client

而在i2c_driver ->detach_client()中,則注銷(xiāo)i2c_client結(jié)構(gòu)體

其中addr_data變量是struct i2c_client_address_data結(jié)構(gòu)體,它的成員如下所示:

struct i2c_client_address_data {unsigned short *normal_i2c; //存放正常的設(shè)備高7位地址數(shù)據(jù)unsigned short *probe; //存放不受*ignore影響的高7位設(shè)備地址數(shù)據(jù)unsigned short *ignore; //存放*ignore的高7位設(shè)備地址數(shù)據(jù)unsigned short **forces; //forces表示適配器匹配不了該設(shè)備,也要將其放入適配器中};

當(dāng)上面結(jié)構(gòu)體的數(shù)組成員以I2C_CLIENT_END結(jié)尾,則表示地址已結(jié)束,比如at24c02設(shè)備為例,看這個(gè)結(jié)構(gòu)體如何定義的:

#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,則表示該設(shè)備只支持指定的那個(gè)適配器static struct i2c_client_address_data addr_data = {.normal_i2c = normal_addr, //存放at24c02地址.probe = ignore, //表示無(wú)地址.ignore = ignore, //表示無(wú)地址. forces = forces, //存放強(qiáng)制的at24c02地址,表示強(qiáng)制支持};

一般而言,都不會(huì)設(shè)置.forces成員,這里只是打個(gè)比方

8.1接下來(lái)繼續(xù)進(jìn)入i2c_probe()函數(shù)繼續(xù)分析,如下所示:

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

里面調(diào)用了i2c_probe_address()函數(shù),從名稱上來(lái)看,顯然它就是用來(lái)發(fā)送起始信號(hào)+設(shè)備地址,來(lái)探測(cè)IIC設(shè)備地址用的

8.2進(jìn)入i2c_probe_address()函數(shù):

static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,int (*found_proc) (struct i2c_adapter *, int, int)) {/*判斷設(shè)備地址是否有效,addr里存放的是設(shè)備地址前7位,比如AT24C02=0xA0,那么addr=0x50*/if (addr < 0x03 || addr > 0x77) {dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",addr); //打印地址無(wú)效,并退出return -EINVAL;}/*查找鏈表中其它IIC設(shè)備的設(shè)備地址,若這個(gè)設(shè)備地址已經(jīng)被使用,則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) //進(jìn)入I2C傳輸函數(shù)return 0;... ... }

?

8.3 其中i2c_smbus_xfer()傳輸函數(shù)如下:

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這個(gè)函數(shù)mutex_lock(&adapter->bus_lock); //加互斥鎖res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,command,size,data); //調(diào)用adapter適配器里的傳輸函數(shù)mutex_unlock(&adapter->bus_lock); //解互斥鎖} else //否則使用默認(rèn)函數(shù)傳輸設(shè)備地址res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,command,size,data);return res; }

看了上面代碼后,顯然我們的s3c2410-i2c適配器沒(méi)有algo->smbus_xfer函數(shù),而是使用i2c_smbus_xfer_emulated()函數(shù),如下圖所示:

?

PS:通常適配器都是不支持的,使用默認(rèn)的i2c_smbus_xfer_emulated()函數(shù)

8.4 接下來(lái)看i2c_smbus_xfer_emulated()函數(shù)如何傳輸?shù)?

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,表示要執(zhí)行兩次數(shù)據(jù)傳輸struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },{ addr, flags | I2C_M_RD, 0, msgbuf1 }}; //定義兩個(gè)i2c_msg結(jié)構(gòu)體,msgbuf0[0] = command; //IIC設(shè)備地址最低位為讀寫(xiě)命令... ... if (i2c_transfer(adapter, msg, num) < 0)return -1;/*設(shè)置i2c_msg結(jié)構(gòu)體成員*/if (read_write == I2C_SMBUS_READ)switch(size) {... ...case I2C_SMBUS_BYTE_DATA: //如果是讀字節(jié)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結(jié)構(gòu)體的內(nèi)容發(fā)送給I2C設(shè)備return -1;... ... }

其中i2c_msg結(jié)構(gòu)體的結(jié)構(gòu),如下所示:

struct i2c_msg {__u16 addr;       //I2C從機(jī)的設(shè)備地址__u16 flags;    //當(dāng)flags=0表示寫(xiě), flags= I2C_M_RD表示讀__u16 len; //傳輸?shù)臄?shù)據(jù)長(zhǎng)度,等于buf數(shù)組里的字節(jié)數(shù)__u8 *buf; //存放數(shù)據(jù)的數(shù)組 };

上面代碼中之所以讀操作需要兩個(gè)i2c_msg,寫(xiě)操作需要一個(gè)i2c_msg,是因?yàn)樽xIIC設(shè)備是兩個(gè)流程

在上一節(jié)IIC接口下的24C02 驅(qū)動(dòng)分析:?http://www.cnblogs.com/lifexy/p/7793686.html里就已經(jīng)分析到了,

只要發(fā)送一個(gè)S起始信號(hào)則就是一個(gè)i2c_msg,如下兩個(gè)讀寫(xiě)操作圖所示:

?

而在i2c_transfer()函數(shù)中,最終又是調(diào)用了之前分析的i2c_adapter->algo->master_xfer()發(fā)送函數(shù),如下圖所示:

?

其中i2c_transfer()的參數(shù)*adap表示通過(guò)哪個(gè)適配器傳輸出去,msgs表示I2C消息,num表示msgs的數(shù)目

內(nèi)核每發(fā)送一個(gè)Msg都會(huì)先發(fā)出S開(kāi)始信號(hào)和設(shè)備地址.直到所有Msg傳輸完畢,最后發(fā)出P停止信號(hào)。

當(dāng)i2c_transfer()返回值為正數(shù),表示已經(jīng)傳輸正數(shù)個(gè)數(shù)據(jù),當(dāng)返回負(fù)數(shù),說(shuō)明I2C傳輸出錯(cuò)

?

8.5 所以在i2c_driver ->attach_adapter(adapter)函數(shù)里主要執(zhí)行以下幾步:

1)?調(diào)用?i2c_probe(adap,?i2c_client_address_data設(shè)備地址結(jié)構(gòu)體, 回調(diào)函數(shù));

2)?將要發(fā)的設(shè)備地址結(jié)構(gòu)體打包成i2c_msg,

3)?然后執(zhí)行i2c_transfer()來(lái)調(diào)用i2c_adapter->algo->master_xfer()將i2c_msg發(fā)出去

4)若收到ACK回應(yīng),便進(jìn)入回調(diào)函數(shù),注冊(cè)i2c_client從設(shè)備,使該設(shè)備與適配器聯(lián)系在一起

所以適配器和iic設(shè)備驅(qū)動(dòng)最終注冊(cè)框架圖如下所示:

?

?

9.接下來(lái)便來(lái)分析回調(diào)函數(shù)如何注冊(cè)i2c_client從設(shè)備的

先來(lái)看看i2c_client結(jié)構(gòu)體:

struct i2c_client { unsigned short flags;//標(biāo)志 unsigned short addr; //該i2c從設(shè)備的設(shè)備地址,存放地址高7位 char name[I2C_NAME_SIZE]; //設(shè)備名字struct i2c_adapter *adapter;//依附的i2c_adapter,表示該IIC設(shè)備支持哪個(gè)適配器 struct i2c_driver *driver;//依附的i2c_driver ,表示該IIC從設(shè)備的驅(qū)動(dòng)是哪個(gè)struct device dev;//設(shè)備結(jié)構(gòu)體 int irq;//設(shè)備所使用的結(jié)構(gòu)體 struct list_head detected;//鏈表頭 };

還是以driver/i2c/chips/eeprom.c為例,如下圖所示:

?

9.1這里的回調(diào)函數(shù)是eeprom_detect()函數(shù),代碼如下所示:

static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind) { struct i2c_client *new_client; //定義一個(gè)i2c_client結(jié)構(gòu)體局部變量new_client =kzalloc(sizeof(struct i2c_client), GFP_KERNEL); //分配i2c_client結(jié)構(gòu)體為全局變量/*設(shè)置i2c_client結(jié)構(gòu)體*/ new_client->addr = address; //設(shè)置設(shè)備地址 new_client->adapter = adapter; //設(shè)置依附的i2c_adapter new_client->driver = &eeprom_driver; //設(shè)置依附的i2c_driver new_client->flags = 0; //設(shè)置標(biāo)志位為初始值 strlcpy(new_client->name, "eeprom", I2C_NAME_SIZE); //設(shè)置名字/*注冊(cè)i2c_client*/if ((err = i2c_attach_client(new_client)))goto exit_kfree; //注冊(cè)失敗,便釋放i2c_client這個(gè)全局變量... ...exit_kfree:kfree(new_client); exit:return err; }

當(dāng)注冊(cè)了i2c_client從設(shè)備后,便可以使用i2c_transfer()來(lái)實(shí)現(xiàn)與設(shè)備傳輸數(shù)據(jù)了

?

10.接下來(lái),我們便參考driver/i2c/chips/eeprom.c驅(qū)動(dòng),來(lái)寫(xiě)出24C02驅(qū)動(dòng)以及測(cè)試程序

驅(qū)動(dòng)代碼步驟如下:

1.定義file_operations結(jié)構(gòu)體 ,設(shè)置字符設(shè)備的讀寫(xiě)函數(shù)(實(shí)現(xiàn)對(duì)24C02的讀寫(xiě)操作)
//構(gòu)造i2c_msg結(jié)構(gòu)體, 使用i2c_transfer()來(lái)實(shí)現(xiàn)與設(shè)備傳輸數(shù)據(jù)

2.定義i2c_client_address_data結(jié)構(gòu)體,里面保存24C02的設(shè)備地址
3.?定義一個(gè)i2c_driver驅(qū)動(dòng)結(jié)構(gòu)體
? ??? ?3.1?設(shè)置i2c_driver-> attach_adapter
    ?// 里面直接調(diào)用 i2c_probe(adap, i2c_client_address_data結(jié)構(gòu)體, 回調(diào)函數(shù));

 ?? 3.2?設(shè)置i2c_driver-> detach_client
  ??? ? ? ? //里面卸載i2c_client, 字符設(shè)備

4.寫(xiě)回調(diào)函數(shù),里面注冊(cè)i2c_client,字符設(shè)備( 字符設(shè)備用來(lái)實(shí)現(xiàn)讀寫(xiě)24C02里的數(shù)據(jù))
? ? ??4.1?分配并設(shè)置i2c_client

? ? ?4.2?使用i2c_attach_client()將i2c_client與適配器進(jìn)行連接

? ??4.3?注冊(cè)字符設(shè)備

5.?寫(xiě)init入口函數(shù),exit出口函數(shù)
init: 使用i2c_add_driver()注冊(cè)i2c_driver
exit: 使用i2c_del_driver ()卸載i2c_driver

?

具體驅(qū)動(dòng)代碼如下所示:

/** 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; //從設(shè)備結(jié)構(gòu)體 static struct class *at24c02_class; //類結(jié)構(gòu)體 static unsigned int at24c02_major; /*1.定義file_operations結(jié)構(gòu)體 ,* 設(shè)置字符設(shè)備的讀寫(xiě)函數(shù)(實(shí)現(xiàn)對(duì)24C02的讀寫(xiě)操作)*/ 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; //寫(xiě)標(biāo)志msg[0].len =1;msg[0].buf =&addr; //寫(xiě)入要讀的地址msg[1].addr=at24c02_client->addr;msg[1].flags=I2C_M_RD; //讀標(biāo)志msg[1].len =1;msg[1].buf =&data; //讀出數(shù)據(jù) ret=i2c_transfer(at24c02_client->adapter, msg, 2); if(ret==2) //表示2個(gè)msg傳輸成功{copy_to_user(buf,&data,1); //上傳數(shù)據(jù) return 0;}elsereturn -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) //地址 數(shù)據(jù)return -EINVAL;copy_from_user(val,buf,2); //獲取 地址 數(shù)據(jù)msg[0].addr=at24c02_client->addr;msg[0].flags=0; //寫(xiě)標(biāo)志msg[0].len =2;msg[0].buf =val; //寫(xiě)入要寫(xiě)的地址 數(shù)據(jù)ret=i2c_transfer(at24c02_client->adapter, msg, 1); if(ret==1) //表示1個(gè)msg傳輸成功{ return 0;}elsereturn -EAGAIN; }static struct file_operations at24c02_fops={.owner = THIS_MODULE,.read = at24c02_read,.write = at24c02_write, };/*2.定義i2c_client_address_data結(jié)構(gòu)體,保存24C02的設(shè)備地址*/ 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, // 強(qiáng)制地址 };/*3. 定義一個(gè)i2c_driver驅(qū)動(dòng)結(jié)構(gòu)體*/ 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, //綁定回調(diào)函數(shù).detach_client = at24c02_detach_client, //解綁回調(diào)函數(shù) };/*3.1 設(shè)置i2c_driver-> attach_adapter*/ static int at24c02_attach_adapter(struct i2c_adapter *adapter) {return i2c_probe(adapter,&at24c02_addr, at24c02_detect); }/*3.2 設(shè)置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.寫(xiě)回調(diào)函數(shù),里面注冊(cè)i2c_client,字符設(shè)備*/ static int at24c02_detect(struct i2c_adapter *adap, int addr, int kind) {printk("at24c02_detect\n");/* 4.1 分配并設(shè)置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與適配器進(jìn)行連接*/i2c_attach_client(at24c02_client) ;/*4.3 注冊(cè)字符設(shè)備*/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. 寫(xiě)init入口函數(shù),exit出口函數(shù)*/ 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.測(cè)試運(yùn)行

如下圖所示:

?

??

總結(jié)

以上是生活随笔為你收集整理的linux下IIC驱动解释的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。