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

I2C子系統之ioctl()

根據前一篇的文章介紹 at24c02的讀寫方式有很多種,

寫有兩種1.寫一字節數據到word address處2.從指定的word address處開始寫一頁數據,此word address需要頁對齊!

讀有三種1.從at24c02當前的word address讀一字節數據2.從指定的word address 讀數據3.從當前的word address地址開始讀一串數據

根據驅動中write() read()的實現方法可以發現,當msg發送完畢時才發送stop信號,而msg之間是是連續發送的不會插入stop信號。

但是,write() read()每次都固定只能發送一則msg!這對at24c02的寫以及current read、sequential read來說沒問題,可以通過write()和read()函數直接實現這四種操作。

但是at24c02的random read就不能直接通過write() read()來實現了。因為random read需要先寫word address,寫完之後不能直接發送stop信號,

而是要接著發送start信號開始發送device address。而驅動中的write() read()只能一次發送一則msg,並且發送完畢就發送stop信號,所以這種時序不符合random read的操作。

不過系統通過ioctl操作,可以一次發送多則msg,而在msg之間是不會發送stop信號的。

所以at24c02的random read操作可以通過發送兩則msg的方式來實現,第一則msg是寫的,並且寫的內容是word address,第二則msg為讀。

下面在分析下ioctl 的驅動實現函數i2cdev_ioctl_rdrw()

  1. static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,  
  2.         unsigned long arg)  
  3. {  
  4.     struct i2c_rdwr_ioctl_data rdwr_arg;  
  5.     struct i2c_msg *rdwr_pa;  
  6.     u8 __user **data_ptrs;  
  7.     int i, res;  
  8.   
  9.     if (copy_from_user(&rdwr_arg,  
  10.                (struct i2c_rdwr_ioctl_data __user *)arg,  
  11.                sizeof(rdwr_arg)))  
  12.         return -EFAULT;  
  13.   
  14.     /* Put an arbitrary limit on the number of messages that can 
  15.      * be sent at once */  
  16.     if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)  
  17.         return -EINVAL;  
  18.   
  19.     rdwr_pa = kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL);  
  20.     if (!rdwr_pa)  
  21.         return -ENOMEM;  
  22.   
  23.     if (copy_from_user(rdwr_pa, rdwr_arg.msgs,  
  24.                rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {  
  25.         kfree(rdwr_pa);  
  26.         return -EFAULT;  
  27.     }  
  28.   
  29.     data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);  
  30.     if (data_ptrs == NULL) {  
  31.         kfree(rdwr_pa);  
  32.         return -ENOMEM;  
  33.     }  
  34.   
  35.     res = 0;  
  36.     for (i = 0; i < rdwr_arg.nmsgs; i++) {  
  37.         /* Limit the size of the message to a sane amount; 
  38.          * and don't let length change either. */  
  39.         if ((rdwr_pa[i].len > 8192) ||  
  40.             (rdwr_pa[i].flags & I2C_M_RECV_LEN)) {  
  41.             res = -EINVAL;  
  42.             break;  
  43.         }  
  44.         data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;  
  45.         rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len);  
  46.         if (IS_ERR(rdwr_pa[i].buf)) {  
  47.             res = PTR_ERR(rdwr_pa[i].buf);  
  48.             break;  
  49.         }  
  50.     }  
  51.     if (res < 0) {  
  52.         int j;  
  53.         for (j = 0; j < i; ++j)  
  54.             kfree(rdwr_pa[j].buf);  
  55.         kfree(data_ptrs);  
  56.         kfree(rdwr_pa);  
  57.         return res;  
  58.     }  
  59.   
  60.     res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);  
  61.     while (i-- > 0) {  
  62.         if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {  
  63.             if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,  
  64.                      rdwr_pa[i].len))  
  65.                 res = -EFAULT;  
  66.         }  
  67.         kfree(rdwr_pa[i].buf);  
  68.     }  
  69.     kfree(data_ptrs);  
  70.     kfree(rdwr_pa);  
  71.     return res;  
  72. }  
首先函數通過copy_from_user函數將用戶空間的數據拷貝到內核。data_ptrs這個變量相當於指針數組了,相當於數組裡面存放的是指針,並且這些指針指向的是需要發送的數據buf的首地址。

其中有一句比較難理解的是

  1. rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len);  
這句話其實實現一下內容:

1.在內核空間申請一塊buf,

2.將data_ptrs[i] (其值和目前的rdwr_pa[i].buf是一致的)指向的用戶空間的buf中的數據拷貝到剛申請的內核buf中。

3.將內核空間的buf地址返回,並且覆蓋rdwr_pa[i].buf中的數值,使其值由原來用戶空間的buf地址變為內核空間的buf地址。

然後調用函數i2c_transfer開始發送信息。發送時序可以通過

i2s_s3c_irq_nextbyte()函數中的流程來判斷,此處不具體分析了.

相關閱讀:

I2C子系統之at24c02讀寫測試 http://www.linuxidc.com/Linux/2012-08/68256.htm
I2C子系統之ioctl()  http://www.linuxidc.com/Linux/2012-08/68257.htm
I2C子系統之at24c02簡介 http://www.linuxidc.com/Linux/2012-08/68258.htm
I2C子系統之總結 http://www.linuxidc.com/Linux/2012-08/68259.htm
I2C子系統之內核中I2C子系統的結構 http://www.linuxidc.com/Linux/2012-08/68260.htm
I2C子系統之I2C bus初始化——I2C_init() http://www.linuxidc.com/Linux/2012-08/68261.htm
I2C子系統之platfor_device初始化——smdk2440_machine_init() http://www.linuxidc.com/Linux/2012-08/68262.htm
I2C子系統之platform_driver初始化——I2C_adap_s3c_init() http://www.linuxidc.com/Linux/2012-08/68263.htm
I2C子系統之I2C總線時鐘頻率設置 http://www.linuxidc.com/Linux/2012-08/68264.htm
I2C子系統之adapter device和client device注冊——I2C_add_number_adapter() http://www.linuxidc.com/Linux/2012-08/68265.htm
I2C子系統之__I2C_first_dynamic_bus_num變量的相關分析 http://www.linuxidc.com/Linux/2012-08/68266.htm
I2C子系統之 adapter driver注冊——I2C_dev_init() http://www.linuxidc.com/Linux/2012-08/68267.htm
I2C子系統之write() http://www.linuxidc.com/Linux/2012-08/68268.htm

Copyright © Linux教程網 All Rights Reserved