歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

I2C子系統之write()

應用層調用write()函數後首先進入的是i2c類設備的write函數,即i2cdev_fops中的write方法。

此處的i2cdev_fops對應的是系統中所有i2c類設備的操作。也就是說系統中所有i2c adapter 的read()

write() open() close() ioctl()等操作,首先調用的是i2c類i2cdev_fops中的方法,通過i2c類中的方法再去尋找adapter 對應的算法i2c_algorithm,此處s3c2440對應的為s3c24xx_i2c_algorithm。

對i2c的操作方法

1.首先open

2.ioctl設置at24c02的地址

3.write()


1.open設備/dev/i2c-0

open通過系統調用最後調用到fops的i2cdev_open函數。

  1. static int i2cdev_open(struct inode *inode, struct file *file)  
  2. {  
  3.     。。。 。。。  
  4.     adap = i2c_get_adapter(i2c_dev->adap->nr);  
  5.     if (!adap)  
  6.         return -ENODEV;  
  7.   
  8.     。。。 。。。  
  9.     client = kzalloc(sizeof(*client), GFP_KERNEL);  
  10.     if (!client) {  
  11.         i2c_put_adapter(adap);  
  12.         return -ENOMEM;  
  13.     }  
  14.     snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);  
  15.     client->driver = &i2cdev_driver;  
  16.   
  17.     client->adapter = adap;  
  18.     file->private_data = client;  
  19.   
  20.     return 0;  
  21. }  

可以發現此函數的作用是根據/dev/i2c-0的設備號找到對應的adapter,然後將其保存到新建的client中。

需要注意的是,此處的client與驅動中的client不同,這裡的client並不會注冊到總線上,和i2c驅動模型的代碼無關。

此處的client只是用來保存client地址信息等。

最後將這個clietn保存到file->private_data中,供ioctl() write() open()等操作使用。

2. ioctl

應用層調用ioctl後會調用到i2cdev_ioctl()函數,此處使用的是I2C_SLAVE_FORCE,用於設置at24c02的地址。

3.write

write通過系統調用最後執行fops這中的i2cdev_write函數

  1. static ssize_t i2cdev_write(struct file *file, const char __user *buf,  
  2.         size_t count, loff_t *offset)  
  3. {  
  4.     。。。 。。。  
  5.     struct i2c_client *client = file->private_data;  
  6.         。。。 。。。  
  7.     tmp = memdup_user(buf, count);  
  8.     。。。 。。。  
  9.     ret = i2c_master_send(client, tmp, count);  
  10.     。。。 。。。  
  11. }  

可以發現,在write函數中首先做的就是將在open操作中保存到file->private_data中的client取出

然後通過memdup_user函數將用戶空間的緩沖區拷貝到內核空間。最後調用函數i2c_master_send()

  1. int i2c_master_send(struct i2c_client *client, const char *buf, int count)  
  2. {  
  3.     。。。 。。。   
  4.     ret = i2c_transfer(adap, &msg, 1);  
  5.         。。。 。。。  
  6. }  

在i2c_mastr_send函數中首先初始化msg結構體,將client的地址、當前需要拷貝的數據長度等信息填充到msg中。最後將此msg作為形參傳遞給i2c_transfer函數。i2c的讀寫過程中,發送的信息都是通過msg來完成的,除了device address之外。device address信息單獨發送,其余的通過msg.buf來完成

並且可以發現,此處i2c_transfer中的第三個參數為1,這個參數是告訴驅動每次發送的msg個數,這裡設置為1

表示每次只能發送一則msg。

i2c_transfer()函數如下:

  1. int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)  
  2. {  
  3.                  。。。 。。。  
  4.                orig_jiffies = jiffies;  
  5.                for (ret = 0, try = 0; try <= adap->retries; try++) {  
  6.             ret = adap->algo->master_xfer(adap, msgs, num);  
  7.             if (ret != -EAGAIN)  
  8.                 break;  
  9.             if (time_after(jiffies, orig_jiffies + adap->timeout))  
  10.                 break;  
  11.         }  
  12.          。。。 。。。  
  13. }  

在此函數做完相關處理後直接調用adapter的algorithm來發送數據,此處即i2c-s3c2440文件中

s3c24xx_i2c_probe總注冊的算法

  1. i2c->adap.algo    = &s3c24xx_i2c_algorithm;  

s3c24xx_i2c_algorithm算法具體如下:

  1. static const struct i2c_algorithm s3c24xx_i2c_algorithm = {  
  2.     .master_xfer        = s3c24xx_i2c_xfer,  
  3.     .functionality      = s3c24xx_i2c_func,  
  4. };  

因此相當於直接調用了函數s3c24xx_i2c_xfer

s3c24xx_i2c_xfer只是對s3c24xx_i2c_doxfer的簡單封裝,實際的處理都在函數s3c24xx_i2c_doxfer中。下面重點分析這個s3c24xx_i2c_doxfer函數

  1. static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,  
  2.                   struct i2c_msg *msgs, int num)  
  3. {  
  4.     。。。 。。。  
  5.     ret = s3c24xx_i2c_set_master(i2c);  
  6.     。。。 。。。  
  7.         i2c->msg     = msgs;  
  8.         i2c->msg_num = num;  
  9.         i2c->msg_ptr = 0;  
  10.         i2c->msg_idx = 0;  
  11.         i2c->state   = STATE_START;  
  12.           
  13.         s3c24xx_i2c_enable_irq(i2c);  
  14.         s3c24xx_i2c_message_start(i2c, msgs);  
  15.     spin_unlock_irq(&i2c->lock);  
  16.   
  17.     timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);  
  18.         。。。 。。。  
  19.   
  20. }  

在此函數中首先調用的是s3c24xx_i2c_set_master函數,查詢master(即adapter)是否處於忙的狀態。忙則休眠1ms後再次查詢,總共查詢400次,相當於在400ms之後i2c還處於忙狀態則放棄。

master空閒後,做些相關初始化的操作。初始化操作中需要注意的是i2c->state = STATE_START,通過這個狀態位來標記i2c當前是起始狀態、寫狀態還是讀狀態。

接著通關函數

  1. s3c24xx_i2c_enable_irq(i2c);  
打開中斷。然後調用函數
  1. s3c24xx_i2c_message_start(i2c, msgs);  
來發送第一個字節,即device address。當第一個字節發送完畢後,s3c2440的i2c控制器會產生中斷。

s3c2440的i2c中斷發生在1.完成1字節的發送或者接收2.廣播呼叫或者從地址匹配時3.總線仲裁失敗。

並且當第一個字節device address發送完畢後,函數通過

  1. timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);  
Copyright © Linux教程網 All Rights Reserved