IIC接口下的24C02 驅動分析: http://www.cnblogs.com/lifexy/p/7793686.htmlhtml
接下來本節, 學習Linux下如何利用linux下I2C驅動體系結構來操做24C02linux
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設備驅動相關的文件,以下圖所示,好比m41t00,就是RTC實時鐘
4) i2c-core.c
這個文件實現了I2C核心的功能(I2C總線的初始化、註冊和適配器添加和註銷等相關工做)以及/proc/bus/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關聯起來.
在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入口函數,以下圖所示:
在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下,以下圖所示:
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入口函數:
其中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
顯然,在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設備驅動註冊框架以下所示:
這裏調用了i2c_driver ->attach_adapter(adapter),咱們看看裏面是否是經過發送IIC設備地址,等待ACK響應來匹配的
8.以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_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()函數,以下圖所示:
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,以下兩個讀寫操做圖所示:
而在i2c_transfer()函數中,最終又是調用了以前分析的i2c_adapter->algo->master_xfer()發送函數,以下圖所示:
其中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設備驅動最終註冊框架圖以下所示:
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爲例,以下圖所示:
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.測試運行
以下圖所示: