I2c_test.c + i2c_dev.c +i2c_s3c3410.c 构成的i2c设备的调用链

来源:互联网 发布:利拉德体测数据 编辑:程序博客网 时间:2024/05/17 08:52

2011.11.21                星期一

I2c_test.c + i2c_dev.c +i2c_s3c3410.c    构成的i2c设备的调用链。
一、从用户空间程序i2c_test.c开始进行流程
//i2c_test.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <stdio.h>
#include <linux/types.h>
#include <assert.h>
#include <string.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

#define SLAVE_ADDRESS  0x70

int main(void)
 {
    struct i2c_rdwr_ioctl_data TP_data;
   unsigned int i2c_fd;  
   int ret;
   i2c_fd = open("/dev/i2c/0", O_RDWR);
   if (!i2c_fd)
          {
             printf("Error on opening the device file\n");
             return 0;
           }
   while(1)
        {                 
         TP_data.nmsgs = 2;//因为读时序要两次,所以设为2,有几个start nmsgs就设置为几
         TP_data.msgs = (struct i2c_msg *)malloc(TP_data.nmsgs * sizeof(struct i2c_msg));
       //  printf("4");
         if (!TP_data.msgs)
           {
             printf("Memory alloc error\n");
             close(i2c_fd);
             return 0;
           }
         ioctl(i2c_fd, I2C_TIMEOUT, 2);//设置超时时间
         ioctl(i2c_fd, I2C_RETRIES, 1);//设置重发次数
          /*read data from register*/
      TP_data.nmsgs = 2;//读时序要两次过程,要发两次I2C消息
      TP_data.msgs[0].len = 1;//信息长度为1,第一次要写需要读入的寄存器地址
      TP_data.msgs[0].addr = 0x5c;//设备地址
      TP_data.msgs[0].flags = 0;//写命令,看读时序理解
      TP_data.msgs[0].buf = (unsigned char*)malloc(1);
      TP_data.msgs[0].buf[0] = 0x02;//信息值
       
      TP_data.msgs[1].len = 1;
      TP_data.msgs[1].addr = 0x5c;
      TP_data.msgs[1].flags = I2C_M_RD;//读命令
      TP_data.msgs[1].buf = (unsigned char*)malloc(1);
      TP_data.msgs[1].buf[0] = 0;//先清空要读的缓冲区
      ret = ioctl (i2c_fd, I2C_RDWR, (unsigned long)&TP_data);//好了,读吧
      if (ret < 0){
       printf ("ioctl read error\n");
      }
      
      //printf("read %02x from e2prom address %02x\n",e2prom_data.msgs[1].buf[0], reg_address);
      printf("touch num is %d\n",TP_data.msgs[1].buf[0]);
      
         
        }
       close(i2c_fd); 
       return(0);
 
 }
1、 在用户空间程序 i2c_test.c 中使用i2c_fd = open("/dev/i2c/0", O_RDWR);来打开设备文件,获得文件描述符来进一步对设备进行读写操作。这里的“/dev/i2c/0”据说是在i2c_dev.c中进行注册了,但是目前我没有发现是怎么注册的。这个需要确认一下。
2、 构造了两个结构体i2c_rdwr_ioctl_data和i2c_msg结构体定义如下:
 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 */
        };

这两个结构体包含了上层调用的参数信息,为操作设备做准备。


3、 ioctl (i2c_fd, I2C_RDWR, (unsigned long)&TP_data);利用ioctl这个函数,对设备进行读写,其中i2c_fd为open中打开的设备文件描述符,I2C_RDWR为命令,(unsigned long)&TP_data为向设备文件传输的信息,其中包含的地址信息,读写信息。


二、进入到设备驱动程序i2c_dev.c
这里有两个问题:1、不知道i2c_test.c中调用open会不会调用到此文件中设备结构体中的open相关函数。
2、 不知道设备文件“dev/i2c/0”在哪里注册的,参考资料上说是在这个文件上注册的但是没有找到。在i2cdev_attach_adapter中进行了注册
I2c_test.c中调用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;
}
这个函数的作用前面已经讲过了,这里的核心函数就是   res = i2c_transfer(client->adapter, rdwr_pa,rdwr_arg.nmsgs);关于这个函数如何往下挖前面也已经讲过了,关于参数client->adapter还是有疑惑。不明白适配器是怎么个调用关系,在i2c_s3c2410.c中挂载了适配器。在这个文件中设备驱动
//设备驱动

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,
};

这其中的几个函数什么时候调用还是不清楚。

原创粉丝点击