OFN鼠標驅動(五) -- i2c-dev.c文件的分析

 

分析完i2c-core後,其實還遺留了不少問題,比如algo的掛載,I2C的ioctl命令的響應等,帶着這些問題,我們繼續分析i2c-dev.c的代碼,看看能否找到一些答案

 

自旋鎖和互斥鎖:這兩個鎖很相似,只是自旋鎖在等待的時候不會進入睡眠,而會一直佔用CPU,這樣會使得資料浪費嚴重,所以要慎用

 

小結:如果之前有接觸過字符型驅動程序的朋友都知道,字符型驅動設備都會爲設備關聯上open,read等操作函數來給應用層使用,而我們在前兩章內容分析的時候,卻沒有發現I2C設備有關聯這些操作函數,原來這些操作函數的實現是在這裏。

系統是將I2C總線看成了一個設備,而I2C總線上掛載的設備則需要分享這些命令。

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

同樣的,先是準備工作,看下變量的定義等:

 

struct      i2c_dev {

       struct list_head             list;

       struct i2c_adapter         *adap;            //適配器

       struct device                 *dev;             //設備

};

 

#define   I2C_MINORS        256                //設備號

static      LIST_HEAD(i2c_dev_list);          //設備鏈表

static      DEFINE_SPINLOCK(i2c_dev_list_lock);   //自旋鎖

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//這個函數名已經註釋了這個函數的作用了:根據index獲取設備信息

static struct i2c_dev      *i2c_dev_get_by_minor(unsigned index)

{

       struct i2c_dev        *i2c_dev;

 

       spin_lock(&i2c_dev_list_lock);

       //遍歷i2c_dev_list鏈表,取出結構體i2c_dev

       list_for_each_entry(i2c_dev,        &i2c_dev_list,       list) {

              if (i2c_dev->adap->nr == index)          //適配器的nr號爲指定的索引

                     goto found;

       }

       i2c_dev = NULL;

 

found:

       spin_unlock(&i2c_dev_list_lock);

       return     i2c_dev;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//獲取一個空的i2c_dev宿主結構體掛載adapter,並將該設備掛載到i2c_dev_list上

static       struct i2c_dev      *get_free_i2c_dev(struct i2c_adapter   *adap)

{

       struct i2c_dev        *i2c_dev;

 

       //索引號過大,出錯

       if (adap->nr >= I2C_MINORS) {

              printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n",

                     adap->nr);

              return ERR_PTR(-ENODEV);

       }

 

       //申請一塊內存,並關聯上輸入的adap信息

       i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL);

       if (!i2c_dev)

              return ERR_PTR(-ENOMEM);

       i2c_dev->adap = adap;

 

       spin_lock(&i2c_dev_list_lock);

       list_add_tail(&i2c_dev->list, &i2c_dev_list);      //將設備鏈表掛載到i2c_dev_list上

       spin_unlock(&i2c_dev_list_lock);

       return i2c_dev;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//刪除一個設備

static void      return_i2c_dev(struct i2c_dev *i2c_dev)

{

       spin_lock(&i2c_dev_list_lock);

       list_del(&i2c_dev->list);                            //將設備從鏈表中斷開

       spin_unlock(&i2c_dev_list_lock);

       kfree(i2c_dev);                                  //釋放設備所佔用的內存

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//顯示適配器的名字

static ssize_t          show_adapter_name(

       struct device   *dev,                     //設備指針

       struct device_attribute   *attr,      //設備屬性

       char              *buf)                    //緩存

{

       //通過device結構體的註解我們可以看到,devt成員是在建立文件系統的時候創建的

       struct i2c_dev        *i2c_dev = i2c_dev_get_by_minor(MINOR(dev->devt));

 

       if (!i2c_dev)

              return -ENODEV;

       return sprintf(buf, "%s\n", i2c_dev->adap->name);

}

 

static      DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL);

#define   DEVICE_ATTR(_name, _mode, _show, _store) \

       struct device_attribute   dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

 

可見,這裏是創建了一個dev_attr_name的設備屬性結構體

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

這裏開始是對I2C設備的操作代碼,應該是應用層的操作了

 

//I2C設備讀

static ssize_t          i2cdev_read (

       struct file       *file,             //文件句柄

       char __user    *buf,             //操作緩存

       size_t            count,            //操作字節數量

       loff_t            *offset)          //偏移地址

{

       char       *tmp;

       int          ret;

 

       //I2C設備的私有數據爲client結構體

       struct i2c_client     *client = (struct i2c_client *)file->private_data;

 

       //操作字節不能大於8K

       if (count > 8192)

              count = 8192;

 

       //申請一塊內存放操作數據

       tmp = kmalloc(count,    GFP_KERNEL);

       if (tmp==NULL)

              return -ENOMEM;

 

       pr_debug("i2c-dev: i2c-%d reading %zd bytes.\n",

              iminor(file->f_path.dentry->d_inode), count);

 

       //調用i2c-core.c中的函數實現讀操作

       ret = i2c_master_recv(client,tmp,count);

       if (ret >= 0)           //讀成功,將數據複製到用戶buf中

              ret = copy_to_user(buf,  tmp, count)?-EFAULT:ret;

       kfree(tmp);            //釋放內存

       return ret;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//I2C設備的寫操作

//代碼基本和讀操作類似,只是調用的實現函數是i2c_master_send

//因爲代碼類似,所以這裏就刪去了些不重要的代碼

static ssize_t          i2cdev_write (

       struct file       *file,

       const char __user   *buf,

       size_t            count,

      loff_t            *offset)

{

       struct i2c_client     *client = (struct i2c_client *)file->private_data;

 

       tmp = kmalloc(count,GFP_KERNEL);

       if (copy_from_user(tmp,buf,count)) {

              kfree(tmp);

              return -EFAULT;

       }

 

       ret = i2c_master_send(client,tmp,count);

       kfree(tmp);

       return ret;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//檢查addr地址是否已經存在於adapter中,不存在則返回0

//在分析i2c-core.c時就已經有相同功能的函數了,所以這裏就不再詳細分析了

static int        i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr)

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

static int               i2cdev_ioctl(

       struct inode           *inode,

       struct file              *file,

       unsigned int          cmd,

       unsigned long        arg)

{

       struct i2c_client     *client = (struct i2c_client *)file->private_data;

       struct i2c_rdwr_ioctl_data    rdwr_arg;

       struct i2c_smbus_ioctl_data data_arg;

       union i2c_smbus_data          temp;

       struct i2c_msg                     *rdwr_pa;              //SMBUS的數據結構體

 

       u8 __user       **data_ptrs;

       int                 i,datasize,res;

       unsigned long funcs;

 

       //打印信息到設備的LOG中

       dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",

              cmd, arg);

 

       //根據不同的命令做響應

       switch ( cmd ) {

       //設置要操作的I2C設備地址

       //由於在這個命令下,arg代表着I2C設備的地址,一般I2C設備地址爲7位,所以arg值不應該大於0x7f。如果設備帶有I2C_M_TEN參數,則說明I2C設備地址是10位,所以此時arg也不應該大於0x3ff

       case        I2C_SLAVE:

       case        I2C_SLAVE_FORCE:         

              if ((arg > 0x3ff) ||                       //參數 >0x3ff

                                                               //設備屬性不帶I2C_M_TEN時,參數>0x7f

                  (((client->flags & I2C_M_TEN) == 0)       &&        arg > 0x7f))

                     return -EINVAL;                  //出錯

 

              if (cmd == I2C_SLAVE

                     && i2cdev_check_addr(client->adapter, arg))     //設備地址(arg)不存在

                     return -EBUSY;

 

              //將client的地址設置爲傳入的設備地址

              //由於函數的一開始client是指向file->private_data的,所以這一步操作就相當於把file的地址設置爲了arg地址了

              client->addr = arg;

              return 0;

 

       //設置設備地址是7位還是10位(I2C_M_TEN)

       case        I2C_TENBIT:       

              if (arg)

                     client->flags |= I2C_M_TEN;

              else

                     client->flags &= ~I2C_M_TEN;

              return 0;

 

       //設置I2C傳輸的數據是否需要帶CRC校驗

       //關於這裏的CRC校驗,實際就是在分析i2c-core.c文件中最後幾個函數所用的crc算法,用來保護傳輸的數據是否正確

       case        I2C_PEC:

              if (arg)

                     client->flags |= I2C_CLIENT_PEC;

              else

                     client->flags &= ~I2C_CLIENT_PEC;

              return 0;

 

       //獲取適配器支持的操作

       case I2C_FUNCS:

              //獲取adapter支持的操作

              funcs = i2c_get_functionality(client->adapter);

              return     put_user(funcs, (unsigned long __user *)arg);     //將數據傳給arg

 

       //設備的讀寫操作,注意傳入參數的類型

       case I2C_RDWR:

              if (copy_from_user(&rdwr_arg,

                               (struct i2c_rdwr_ioctl_data   __user *)arg,

                               sizeof(rdwr_arg)))

                     return -EFAULT;

 

              //要操作的數據包(msg包)超過最大限制

              if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)

                     return -EINVAL;

 

              //申請內存

              rdwr_pa = (struct   i2c_msg *)

                     kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),

                     GFP_KERNEL);

 

              if (rdwr_pa == NULL) return -ENOMEM;        //申請失敗

 

              //將要操作的數據用用戶層複製到內核層

              if (copy_from_user(rdwr_pa,       rdwr_arg.msgs,

                               rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {

                     kfree(rdwr_pa);

                     return -EFAULT;

              }

 

              //申請一組指針,每一個元素指向一個msg包

              data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);

              if (data_ptrs == NULL) {

                     kfree(rdwr_pa);

                     return -ENOMEM;

              }

 

              res = 0;

              for( i=0; i<rdwr_arg.nmsgs; i++ ) {

                     if ((rdwr_pa[i].len > 8192)   ||                    //要操作的數據不能大於8K

                         (rdwr_pa[i].flags & I2C_M_RECV_LEN)) {

                            res = -EINVAL;

                            break;

                     }

 

                     data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;         //指針和用戶層數據緩存關聯

                     //根據長度申請內存,注意,此時buf地址關聯上的是新申請的內存的地址

                     rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);      

                     if(rdwr_pa[i].buf == NULL) {

                            res = -ENOMEM;

                            break;

                     }

 

                     //將要操作的用戶數據COPY到內核緩存中

                     //如果是讀操作,那麼這裏複製的數據是沒有意義的

                     //如果是寫操作,那麼這裏就將要寫入的數據給複製進了緩存中了

                     if(copy_from_user(rdwr_pa[i].buf,

                            data_ptrs[i],

                            rdwr_pa[i].len)) {

                                   ++i;

                                   res = -EFAULT;

                            break;

                     }

              }            //for結束

 

              //如果以上操作失敗(一般是申請內存失敗),則釋放內存並返回

              if (res < 0) {

                     int j;

                     for (j = 0; j < i; ++j)

                            kfree(rdwr_pa[j].buf);

                     kfree(data_ptrs);

                     kfree(rdwr_pa);

                     return res;

              }

 

              //傳輸數據

              res = i2c_transfer(client->adapter,

                     rdwr_pa,

                     rdwr_arg.nmsgs);

 

              while(i-- > 0) {

                     //如果是讀操作,則把數據複製出來

                     if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) {

                            if(copy_to_user(

                                   data_ptrs[i],

                                   rdwr_pa[i].buf,

                                   rdwr_pa[i].len)) {

                                   res = -EFAULT;

                            }

                     }

                     kfree(rdwr_pa[i].buf);          //釋放內存

              }

 

              kfree(data_ptrs);

              kfree(rdwr_pa);

              return res;

 

       //SMBUS格式的讀寫操作

       //實際上就是直接調用i2c-core.c中的i2c_smbus_xfer函數來實現I2C通訊

       case I2C_SMBUS:

              if (copy_from_user(&data_arg,

                                 (struct i2c_smbus_ioctl_data        __user *) arg,

                                 sizeof(struct i2c_smbus_ioctl_data)))

                     return -EFAULT;

 

              //這裏省略了很多參數合法性判斷的代碼

              //當所有參數都判斷合法之後,則調用這個核心函數來實現對應的功能

              res = i2c_smbus_xfer(client->adapter,client->addr,client->flags,

                    data_arg.read_write,

                    data_arg.command,data_arg.size,&temp);

 

              if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) ||

                            (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||

                           (data_arg.read_write == I2C_SMBUS_READ))) {

                     if (copy_to_user(data_arg.data, &temp, datasize))

                            return     -EFAULT;

              }

              return res;

 

       //設置兩個參數

       case I2C_RETRIES:

              client->adapter->retries = arg;

              break;

       case I2C_TIMEOUT:

              client->adapter->timeout = arg;

              break;

       default:

              return -ENOTTY;

       }

 

       return 0;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//打開設備

static int        i2cdev_open(struct inode *inode,        struct file *file)

{

       unsigned int   minor = iminor(inode);         //獲取設備的次版本號

       struct i2c_client     *client;

       struct i2c_adapter *adap;

       struct i2c_dev        *i2c_dev;

 

       i2c_dev = i2c_dev_get_by_minor(minor);    //根據次版本號在i2c_dev_list中獲取設備

       if (!i2c_dev)

              return -ENODEV;

 

       //根據關聯的適配器號獲取適配器,獲取的同時,該適配器的引用計數加1

       adap = i2c_get_adapter(i2c_dev->adap->nr);             

       if (!adap)

              return -ENODEV;

 

       //申請一塊內存,準備掛載設備到文件的私有數據上

       client = kzalloc(sizeof(*client), GFP_KERNEL);       

       if (!client) {

              i2c_put_adapter(adap);          //適配器的引用計數減1

              return     -ENOMEM;

       }

       snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);

       client->driver = &i2cdev_driver;  //將i2cdev_driver掛載到設備驅動上

       client->adapter = adap;                //將適配器掛載上去

       file->private_data = client;           //文件的私有數據指向這個設備

 

       return 0;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//釋放設備

static int               i2cdev_release(struct inode *inode,      struct file *file)

{

       struct i2c_client     *client = file->private_data;

 

       i2c_put_adapter(client->adapter);         //適配器的引用計數減1

       kfree(client);                                      //釋放設備client內存

       file->private_data = NULL;                 //文件的私有數據指向空

 

       return 0;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//這個結構就比較熟悉了,字符型驅動的操作函數

static const struct file_operations         i2cdev_fops = {

       .owner           = THIS_MODULE,              //所有者

       .llseek            = no_llseek,                         //操作位置定位

       .read                     = i2cdev_read,                            //讀操作

       .write             = i2cdev_write,                    //寫操作

       .ioctl              = i2cdev_ioctl,                     //IOCTL操作

       .open             = i2cdev_open,                    //打開文件

       .release           = i2cdev_release,                  //釋放文件

};

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

static struct class    *i2c_dev_class;

 

//適配器探測

static int               i2cdev_attach_adapter(struct i2c_adapter *adap)

{

       struct i2c_dev        *i2c_dev;

       int                        res;

 

       i2c_dev = get_free_i2c_dev(adap);              //申請一塊內存做i2c_dev並關聯上adapter

       if (IS_ERR(i2c_dev))

              return     PTR_ERR(i2c_dev);

 

       //創建設備並掛載到結構體上

       i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,

                                 MKDEV(I2C_MAJOR, adap->nr),

                                 "i2c-%d", adap->nr);

       if (IS_ERR(i2c_dev->dev)) {

              res = PTR_ERR(i2c_dev->dev);

              goto error;

       }

 

       //根據設備的屬性創建文件

       //這一步應該就是將設備變成文件放到根目錄中了

       res = device_create_file(i2c_dev->dev,        &dev_attr_name);

       if (res)

              goto error_destroy;

 

       pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",

               adap->name, adap->nr);

       return 0;

 

//創建文件出錯

error_destroy:

       //銷燬設備

       device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));

 

//創建設備出錯

error:

       return_i2c_dev(i2c_dev);             //將函數一開始創建的i2c_dev結構體釋放

       return res;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//adapter斷開時執行的函數

static int               i2cdev_detach_adapter(struct i2c_adapter *adap)

{

       struct i2c_dev        *i2c_dev;

 

       i2c_dev = i2c_dev_get_by_minor(adap->nr);      //根據次版本號獲取設備指針

       if (!i2c_dev)

              return 0;

 

       device_remove_file(i2c_dev->dev, &dev_attr_name);        //刪除文件

       return_i2c_dev(i2c_dev);                                                //將i2c_dev佔的內存釋放

       device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));    //設備銷燬

 

       pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name);

       return 0;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//設備client卸載時調用的函數(什麼也沒幹)

static int               i2cdev_detach_client(struct i2c_client *client)

{

       return 0;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//設備驅動

static struct i2c_driver   i2cdev_driver = {

       .driver = {

              .name      = "dev_driver",

       },

       .id                        = I2C_DRIVERID_I2CDEV,

       .attach_adapter       = i2cdev_attach_adapter,

       .detach_adapter      = i2cdev_detach_adapter,

       .detach_client         = i2cdev_detach_client,

};

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//模塊的加載函數

module_init(i2c_dev_init);

module_exit(i2c_dev_exit);

 

//代碼在初始化段中,將在開機時自動執行

static int __init      i2c_dev_init(void)

{

       int res;

 

//代碼看到這裏的時候,順便看了一眼我的Linux啓動時打印出來的LOG,果然找到了這句

       printk(KERN_INFO "i2c /dev entries driver\n");

 

       //用主設備號註冊設備

       res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);

       if (res)

              goto out;

      

       //創建設備類

       i2c_dev_class = class_create(THIS_MODULE,   "i2c-dev");

       if (IS_ERR(i2c_dev_class))

              goto out_unreg_chrdev;

 

       res = i2c_add_driver(&i2cdev_driver);        //註冊設備驅動

       if (res)

              goto       out_unreg_class;

 

       return 0;

 

//註冊失敗

out_unreg_class:

       class_destroy(i2c_dev_class);                      //銷燬設備類

out_unreg_chrdev:

       unregister_chrdev(I2C_MAJOR, "i2c");      //根據主設備號卸載設備

out:

       printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);

       return res;

}

 

static void      __exit i2c_dev_exit(void)

{

       i2c_del_driver(&i2cdev_driver);                 //刪除驅動

       class_destroy(i2c_dev_class);                      //銷燬設備類

       unregister_chrdev(I2C_MAJOR,"i2c");              //根據主設備號卸載設備

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章