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");              //根据主设备号卸载设备

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值