2011.11.17 星期四 I2c-dev.c 参考代码 TP驱动分析(五)

程序运行流程:


1、 module_init(i2c_dev_init); 加载初始化函数。
2、 运行i2c_dev_init函数,注册设备,注册设备驱动,以_init为头的函数,在运行过后系统将回收其内存
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;
}
其中注册设备时用的主设备号I2C_MAJOR是什么,不清楚,不知道是不是头文件里面的宏定义,register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);中的i2c是在/proc/devices中体现的,
其中注册的结构体&i2cdev_fops,给用户空间提供了调用接口,就是个字符型驱动
目前不清楚/dev/i2c/0这个设备文件是在哪里注册的????。
这里面注册的设备驱动&i2cdev_driver,不知道是什么时候用


3、 结构体static const struct file_operations  i2cdev_fops 提供了用户空间调用函数的接口,在程序i2c-test.c中使用的就是ioctl函数来对I2C设备进行读写的。
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,                  //释放文件
};


用户程序中只用到了ioctl,所以其他的接口没有看。


3、对ioctl函数i2cdev_ioctl进行分析,在用户空间中是使用 ioctl(i2c_fd, I2C_TIMEOUT, 2);//设置超时时间;ioctl(i2c_fd, I2C_RETRIES, 1) //设置重发次数;以及ret = ioctl (i2c_fd, I2C_RDWR, (unsigned long)&TP_data);//读取  ,来对设备进行操作的,其中i2c_fd为打开/dev/i2c/0这个设备文件后返回的文件描述符,第二个参数是命令,第三个参数随命令不同表示的意义也不一样,其中TP_data为i2c_rdwr_ioctl_data结构体:
struct i2c_rdwr_ioctl_data {
            struct i2c_msg __user *msgs; /* pointers to i2c_msgs */
            __u32 nmsgs; /* number of i2c_msgs */
        };
struct i2c_msg {
            _ _u16 addr; /* slave address */
            _ _u16 flags; /* 标志(读、写) */ 
            _ _u16 len; /* msg length */
            _ _u8 *buf; /* pointer to msg data */
        };
这其中nmsgs为需要发送的消息数量,msgs为指向i2c_msg结构体的指针。接下来看驱动程序中是怎么样定义ioctl的吧。


static int      i2cdev_ioctl (
       struct inode           *inode,
       struct file              *file,  //文件描述符,也就是打开/dev/下创建的设备文件所返回的文件描述符
       unsigned int          cmd,//命令
       unsigned long        arg )//从上层传输过来的参数,视cmd的不同含义有所不同
{
       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 ) {
        //设备的读写操作,注意传入参数的类型
       case I2C_RDWR:
              if (copy_from_user(&rdwr_arg,
                               (struct i2c_rdwr_ioctl_data   __user *)arg,
                               sizeof(rdwr_arg))) //arg是从用户空间传过来的TP_data,rdwr_arg是用户空间,但&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);//申请了结构体i2c_msg大小的内核内存,这里要注意一点,申请的内存是装载i2c_msg这个结构体的,其成员buf为指针,申请的内核内存只是装载指针变量的,所以此指针所指向的空间还是用户空间内存
              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;
}


这个函数的其他命令在用户空间没有调用所以没有列出来,这其中有许多情况都要进行内核空间的内存申请,以及从用户空间拷贝数据到内核空间,以及从内核空间拷贝数据到用户空间;这其中要注意一点就是,要看清申请的是装载数据的空间,还是只是装载指针的空间,如果只是装载指针的空间的话,这个指针指向的空间还是在用户空间。




总结:今天没有将这个程序完全看完,其中还不知道设备文件是怎么创建的(也就是用户空间所打开的/dev/i2c/0),同i2c-s3c2440.c的关联关系也没有搞清楚,明天要将这个情况搞清楚。 

 与i2c-s3c2440.c的关联关系为:i2c-s3c2440.c提供了传输函数 i2c_transfer的具体实现方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值