<form id="bpvnp"></form>

            <address id="bpvnp"><listing id="bpvnp"><meter id="bpvnp"></meter></listing></address>

                <address id="bpvnp"><nobr id="bpvnp"><menuitem id="bpvnp"></menuitem></nobr></address>

                    技术文摘

                    Linux I2C驱动分析(三)——i2c_dev驱动和应用层分析












                    一、i2c-dev驱动分析

                    1.1、设备驱动注册

                    分析这个驱动,还是从module_init()和module_exit()开始,程序如下:

                    1.       static int __init i2c_dev_init(void)

                    2.       {

                    3.           int res;

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

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

                    6.           if (res)

                    7.               goto out;

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

                    9.           if (IS_ERR(i2c_dev_class)) {

                    10.              res = PTR_ERR(i2c_dev_class);

                    11.              goto out_unreg_chrdev;

                    12.          }

                    13.          /* Keep track of adapters which will be added or removed later */

                    14.          res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);

                    15.          if (res)

                    16.              goto out_unreg_class;

                    17.          /* Bind to already existing adapters right away */

                    18.          i2c_for_each_dev(NULL, i2cdev_attach_adapter);

                    19.          return 0;

                    20.      out_unreg_class:

                    21.          class_destroy(i2c_dev_class);

                    22.      out_unreg_chrdev:

                    23.          unregister_chrdev(I2C_MAJOR, "i2c");

                    24.      out:

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

                    26.          return res;

                    27.      }

                    28.      static void __exit i2c_dev_exit(void)

                    29.      {

                    30.          bus_unregister_notifier(&i2c_bus_type, &i2cdev_notifier);

                    31.          i2c_for_each_dev(NULL, i2cdev_detach_adapter);

                    32.          class_destroy(i2c_dev_class);

                    33.          unregister_chrdev(I2C_MAJOR, "i2c");

                    34.      }

                    35.      module_init(i2c_dev_init);

                    36.      module_exit(i2c_dev_exit);


                    首先注册了i2cdev_fops操作函数集,接着注册了一个名为”i2c-dev”的class,然后又注册了一个i2cdev_notifier,i2cdev_notifier如下

                    1.  static struct notifier_block i2cdev_notifier = {

                    2.      .notifier_call = i2cdev_notifier_call,

                    3.  };

                    4.  int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,

                    5.               void *data)

                    6.  {

                    7.      struct device *dev = data;

                    8.   

                    9.      switch (action) {

                    10.        case BUS_NOTIFY_ADD_DEVICE:

                    11.            return i2cdev_attach_adapter(dev, NULL);

                    12.        case BUS_NOTIFY_DEL_DEVICE:

                    13.            return i2cdev_detach_adapter(dev, NULL);

                    14.        }

                    15.     

                    16.        return 0;

                    17.    }


                    紧接着看下i2cdev_attach_adapter函数:

                    1.  static int i2cdev_attach_adapter(struct device *dev, void *dummy)

                    2.  {

                    3.      struct i2c_adapter *adap;

                    4.      struct i2c_dev *i2c_dev;

                    5.      int res;

                    6.      if (dev->type != &i2c_adapter_type)

                    7.          return 0;

                    8.      adap = to_i2c_adapter(dev);

                    9.      i2c_dev = get_free_i2c_dev(adap);

                    10.          if (IS_ERR(i2c_dev))

                    11.              return PTR_ERR(i2c_dev);

                    12.          /* register this i2c device with the driver core */

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

                    14.                       MKDEV(I2C_MAJOR, adap->nr), NULL,

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

                    16.          if (IS_ERR(i2c_dev->dev)) {

                    17.              res = PTR_ERR(i2c_dev->dev);

                    18.              goto error;

                    19.          }

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

                    21.          if (res)

                    22.              goto error_destroy;

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

                    24.               adap->name, adap->nr);

                    25.          return 0;

                    26.      error_destroy:

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

                    28.      error:

                    29.          return_i2c_dev(i2c_dev);

                    30.          return res;

                    31.      }


                    这个函数也很简单,首先调用get_free_i2c_dev()分配并初始化了一个struct i2c_dev结构,使i2c_dev->adap指向操作的adapter.之后,该i2c_dev会被链入链表i2c_dev_list中。再分别以I2C_MAJOR,、adap->nr为主次设备号创建了一个device。如果此时系统配置了udev或者是hotplug,那么就在/dev下自动创建相关的设备节点了。

                    i2cdev_detach_adapter函数完成相反的操作,在此省略。

                    所有主设备号为I2C_MAJOR的设备节点的操作函数是i2cdev_fops,它的定义如下所示:

                    1.  static const struct file_operations i2cdev_fops = {

                    2.      .owner        = THIS_MODULE,

                    3.      .llseek        = no_llseek,

                    4.      .read        = i2cdev_read,

                    5.      .write        = i2cdev_write,

                    6.      .unlocked_ioctl    = i2cdev_ioctl,

                    7.      .open        = i2cdev_open,

                    8.      .release    = i2cdev_release,

                    9.  };


                    接下来一一分析i2cdev_fops结构体的成员。







                    1.2、设备打开函数--i2cdev_open

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

                    2.  {

                    3.      unsigned int minor = iminor(inode);

                    4.      struct i2c_client *client;

                    5.      struct i2c_adapter *adap;

                    6.      struct i2c_dev *i2c_dev;

                    7.      i2c_dev = i2c_dev_get_by_minor(minor);     //以次设备号从i2c_dev_list链表中取得i2c_dev

                    8.      if (!i2c_dev)

                    9.          return -ENODEV;

                    10.          adap = i2c_get_adapter(i2c_dev->adap->nr);    //以apapter的总线号从i2c_adapter_idr中找到adapter

                    11.          if (!adap)

                    12.              return -ENODEV;

                    13.          /* This creates an anonymous i2c_client, which may later be

                    14.           * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.

                    15.           *

                    16.           * This client is ** NEVER REGISTERED ** with the driver model

                    17.           * or I2C core It just holds private copies of addressing

                    18.           * information and maybe a PEC flag.

                    19.           */

                    20.          client = kzalloc(sizeof(*client), GFP_KERNEL);    //分配并初始化一个i2c_client结构

                    21.          if (!client) {

                    22.              i2c_put_adapter(adap);

                    23.              return -ENOMEM;

                    24.          }

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

                    26.          client->adapter = adap;    //clinet->adapter指向操作的adapter

                    27.          file->private_data = client;     //关联到file

                    28.          return 0;

                    29.      }


                    注意这里分配并初始化了一个struct i2c_client结构,但是没有注册这个clinet。此外,这个函数中还有一个比较奇怪的操作,不是在前面已经将i2c_dev->adap 指向要操作的adapter么?为什么还要以adapter->nr为关键字从i2c_adapter_idr去找这个操作的adapter呢?因为调用i2c_get_adapter()从总线号nr找到操作的adapter的时候,还会增加module的引用计数,这样可以防止??橐馔獗皇头诺?,那i2c_dev->adap->nr操作,如果i2c_dev->adap被释放掉的话,不是会引起系统崩溃么?这里因为在i2cdev_attach_adapter()间接的增加了一次adapter的一次引用计数.如下:

                    1.  static int i2cdev_attach_adapter(struct i2c_adapter *adap)

                    2.  {

                    3.  ......

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

                    5.                      MKDEV(I2C_MAJOR, adap->nr),

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

                    7.  ......

                    8.  }


                    i2c_dev内嵌的device是以adap->dev为父结点,在device_create()中会增次adap->dev的一次引用计数。




                    1.3、read操作

                    Read操作对应的操作函数如下示:
                    1.   static ssize_t i2cdev_read (struct file *file, char __user *buf, size_t count,

                    2.                               loff_t *offset)

                    3.   {

                    4.       char *tmp;

                    5.       int ret;

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

                    7.       if (count > 8192)

                    8.           count = 8192;

                    9.       tmp = kmalloc(count,GFP_KERNEL);

                    10.      if (tmp==NULL)

                    11.          return -ENOMEM;

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

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

                    14.      ret = i2c_master_recv(client,tmp,count);

                    15.      if (ret >= 0)

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

                    17.      kfree(tmp);

                    18.      return ret;

                    19.  }


                    首先从file结构中取得struct i2c_clinet,然后在kernel分配相同长度的缓存区,随之调用i2c_master_recv()从设备中读取数据.再将读取出来的数据copy到用户空间中。I2c_master_recv()在Linux I2C驱动分析(二)----I2C板级设备扫描和数据传输已经讲述。







                    1.4、write操作

                    1.  static ssize_t i2cdev_write(struct file *file, const char __user *buf,

                    2.          size_t count, loff_t *offset)

                    3.  {

                    4.      int ret;

                    5.      char *tmp;

                    6.      struct i2c_client *client = file->private_data;

                    7.   

                    8.      if (count > 8192)

                    9.          count = 8192;

                    10.     

                    11.        tmp = memdup_user(buf, count);

                    12.        if (IS_ERR(tmp))

                    13.            return PTR_ERR(tmp);

                    14.     

                    15.        pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",

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

                    17.     

                    18.        ret = i2c_master_send(client, tmp, count);

                    19.        kfree(tmp);

                    20.        return ret;

                    21.    }


                    该操作比较简单,就是将用户空间的数据通过使用函数i2c_master_send发送到i2c 设备。i2c_master_send函数在《Linux I2C驱动分析(二)----I2C板级设备扫描和数据传输》已经讲述。

                    memdup_user函数完成分配存储区,把应用层的数据复制到刚分配的存储区中,具体程序如下:
                    1.     void *memdup_user(const void __user *src, size_t len)

                    2.     {

                    3.         void *p;

                    4.         /*

                    5.          * Always use GFP_KERNEL, since copy_from_user() can sleep and

                    6.          * cause pagefault, which makes it pointless to use GFP_NOFS

                    7.          * or GFP_ATOMIC.

                    8.          */

                    9.         p = kmalloc_track_caller(len, GFP_KERNEL);

                    10.        if (!p)

                    11.            return ERR_PTR(-ENOMEM);

                    12.        if (copy_from_user(p, src, len)) {

                    13.            kfree(p);

                    14.            return ERR_PTR(-EFAULT);

                    15.        }

                    16.        return p;

                    17.    }




                    1.5、ioctl函数

                    有人可能看出了一个问题,clinet->addr是从哪来的呢?对,在read之前应该还要有一步操作来设置clinet->addr的值。这个过程是ioctl的操作,ioctl可以设置PEC标志,重试次数,超时时间和发送接收数据等。具体程序如下:
                    1.  static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

                    2.  {

                    3.      struct i2c_client *client = file->private_data;

                    4.      unsigned long funcs;

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

                    6.          cmd, arg);

                    7.      switch (cmd) {

                    8.      case I2C_SLAVE:

                    9.      case I2C_SLAVE_FORCE:

                    10.          /* NOTE: devices set up to work with "new style" drivers

                    11.           * can't use I2C_SLAVE, even when the device node is not

                    12.           * bound to a driver. Only I2C_SLAVE_FORCE will work.

                    13.           *

                    14.           * Setting the PEC flag here won't affect kernel drivers,

                    15.           * which will be using the i2c_client node registered with

                    16.           * the driver model core. Likewise, when that client has

                    17.           * the PEC flag already set, the i2c-dev driver won't see

                    18.           * (or use) this setting.

                    19.           */

                    20.          if ((arg > 0x3ff) ||

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

                    22.              return -EINVAL;

                    23.          if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))

                    24.              return -EBUSY;

                    25.          /* REVISIT: address could become busy later */

                    26.          client->addr = arg;    //设置addr

                    27.          return 0;

                    28.      case I2C_TENBIT:

                    29.          //设置10 bit地址模式

                    30.          if (arg)

                    31.              client->flags |= I2C_M_TEN;

                    32.          else

                    33.              client->flags &= ~I2C_M_TEN;

                    34.          return 0;

                    35.      case I2C_PEC:

                    36.          //设置传输后增加PEC标志

                    37.          if (arg)

                    38.              client->flags |= I2C_CLIENT_PEC;

                    39.          else

                    40.              client->flags &= ~I2C_CLIENT_PEC;

                    41.          return 0;

                    42.      case I2C_FUNCS:

                    43.          //获取函数支持

                    44.          funcs = i2c_get_functionality(client->adapter);

                    45.          return put_user(funcs, (unsigned long __user *)arg);

                    46.      case I2C_RDWR:

                    47.          //读取和接收数据,后面讲述

                    48.          return i2cdev_ioctl_rdrw(client, arg);

                    49.      case I2C_SMBUS:

                    50.          //smbus协议数据传输,后面讲述

                    51.          return i2cdev_ioctl_smbus(client, arg);

                    52.      case I2C_RETRIES:

                    53.          //设置重试次数

                    54.          client->adapter->retries = arg;

                    55.          break;

                    56.      case I2C_TIMEOUT:

                    57.          /* For historical reasons, user-space sets the timeout

                    58.           * value in units of 10 ms.

                    59.           */

                    60.          //设置超时时间

                    61.          client->adapter->timeout = msecs_to_jiffies(arg * 10);

                    62.          break;

                    63.      default:

                    64.          /* NOTE: returning a fault code here could cause trouble

                    65.           * in buggy userspace code. Some old kernel bugs returned

                    66.           * zero in this case, and userspace code might accidentally

                    67.           * have depended on that bug.

                    68.           */

                    69.          return -ENOTTY;

                    70.      }

                    71.      return 0;

                    72.  }


                    读取和接收数据函数i2cdev_ioctl_rdrw(client, arg);如下:

                    1.  static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,

                    2.          unsigned long arg)

                    3.  {

                    4.      struct i2c_rdwr_ioctl_data rdwr_arg;    //包括i2c_msg和它的个数

                    5.      struct i2c_msg *rdwr_pa;

                    6.      u8 __user **data_ptrs;

                    7.      int i, res;

                    8.      if (copy_from_user(&rdwr_arg,

                    9.               (struct i2c_rdwr_ioctl_data __user *)arg,

                    10.                 sizeof(rdwr_arg)))

                    11.            return -EFAULT;

                    12.        /* Put an arbitrary limit on the number of messages that can

                    13.         * be sent at once */

                    14.        if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)

                    15.            return -EINVAL;

                    16.        rdwr_pa = kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL);    //创建存储i2c_msg的内存

                    17.        if (!rdwr_pa)

                    18.            return -ENOMEM;

                    19.        if (copy_from_user(rdwr_pa, rdwr_arg.msgs,

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

                    21.            kfree(rdwr_pa);

                    22.            return -EFAULT;

                    23.        }

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

                    25.        if (data_ptrs == NULL) {

                    26.            kfree(rdwr_pa);

                    27.            return -ENOMEM;

                    28.        }

                    29.        res = 0;

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

                    31.            /* Limit the size of the message to a sane amount;

                    32.             * and don't let length change either. */

                    33.            if ((rdwr_pa[i].len > 8192) ||

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

                    35.                res = -EINVAL;

                    36.                break;

                    37.            }

                    38.            data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;

                    39.            rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len);

                    40.            if (IS_ERR(rdwr_pa[i].buf)) {

                    41.                res = PTR_ERR(rdwr_pa[i].buf);

                    42.                break;

                    43.            }

                    44.        }

                    45.        if (res < 0) {

                    46.            int j;

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

                    48.                kfree(rdwr_pa[j].buf);

                    49.            kfree(data_ptrs);

                    50.            kfree(rdwr_pa);

                    51.            return res;

                    52.        }

                    53.        res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);    //传输数据

                    54.        while (i-- > 0) {

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

                    56.                if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,

                    57.                         rdwr_pa[i].len))    //将接收到的数据发送到应用层

                    58.                    res = -EFAULT;

                    59.            }

                    60.            kfree(rdwr_pa[i].buf);

                    61.        }

                    62.        kfree(data_ptrs);

                    63.        kfree(rdwr_pa);

                    64.        return res;

                    65.    }


                    smbus协议数据传输函数i2cdev_ioctl_smbus(client, arg);如下:

                    1.  static noinline int i2cdev_ioctl_smbus(struct i2c_client *client,

                    2.          unsigned long arg)

                    3.  {

                    4.      struct i2c_smbus_ioctl_data data_arg;

                    5.      union i2c_smbus_data temp;

                    6.      int datasize, res;

                    7.      //从应用层复制数据

                    8.      if (copy_from_user(&data_arg,

                    9.               (struct i2c_smbus_ioctl_data __user *) arg,

                    10.                 sizeof(struct i2c_smbus_ioctl_data)))

                    11.            return -EFAULT;

                    12.        if ((data_arg.size != I2C_SMBUS_BYTE) &&

                    13.         (data_arg.size != I2C_SMBUS_QUICK) &&

                    14.         (data_arg.size != I2C_SMBUS_BYTE_DATA) &&

                    15.         (data_arg.size != I2C_SMBUS_WORD_DATA) &&

                    16.         (data_arg.size != I2C_SMBUS_PROC_CALL) &&

                    17.         (data_arg.size != I2C_SMBUS_BLOCK_DATA) &&

                    18.         (data_arg.size != I2C_SMBUS_I2C_BLOCK_BROKEN) &&

                    19.         (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) &&

                    20.         (data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) {

                    21.            dev_dbg(&client->adapter->dev,

                    22.                "size out of range (%x) in ioctl I2C_SMBUS.\n",

                    23.                data_arg.size);

                    24.            return -EINVAL;    //不符合协议,直接退出

                    25.        }

                    26.        /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1,

                    27.         so the check is valid if size==I2C_SMBUS_QUICK too. */

                    28.        if ((data_arg.read_write != I2C_SMBUS_READ) &&

                    29.         (data_arg.read_write != I2C_SMBUS_WRITE)) {

                    30.            dev_dbg(&client->adapter->dev,

                    31.                "read_write out of range (%x) in ioctl I2C_SMBUS.\n",

                    32.                data_arg.read_write);

                    33.            return -EINVAL;    //既不是读,也不是写

                    34.        }

                    35.        /* Note that command values are always */

                    36.        if ((data_arg.size == I2C_SMBUS_QUICK) ||

                    37.         ((data_arg.size == I2C_SMBUS_BYTE) &&

                    38.         (data_arg.read_write == I2C_SMBUS_WRITE)))

                    39.            /* These are special: we do not use data */

                    40.            return i2c_smbus_xfer(client->adapter, client->addr,

                    41.                     client->flags, data_arg.read_write,

                    42.                     data_arg.command, data_arg.size, NULL);     //不需要读

                    43.        if (data_arg.data == NULL) {

                    44.            dev_dbg(&client->adapter->dev,

                    45.                "data is NULL pointer in ioctl I2C_SMBUS.\n");

                    46.            return -EINVAL;

                    47.        }

                    48.        //判断数据大小

                    49.        if ((data_arg.size == I2C_SMBUS_BYTE_DATA) ||

                    50.         (data_arg.size == I2C_SMBUS_BYTE))

                    51.            datasize = sizeof(data_arg.data->byte);

                    52.        else if ((data_arg.size == I2C_SMBUS_WORD_DATA) ||

                    53.             (data_arg.size == I2C_SMBUS_PROC_CALL))

                    54.            datasize = sizeof(data_arg.data->word);

                    55.        else /* size == smbus block, i2c block, or block proc. call */

                    56.            datasize = sizeof(data_arg.data->block);

                    57.        if ((data_arg.size == I2C_SMBUS_PROC_CALL) ||

                    58.         (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||

                    59.         (data_arg.size == I2C_SMBUS_I2C_BLOCK_DATA) ||

                    60.         (data_arg.read_write == I2C_SMBUS_WRITE)) {

                    61.            if (copy_from_user(&temp, data_arg.data, datasize))

                    62.                return -EFAULT;

                    63.        }

                    64.        if (data_arg.size == I2C_SMBUS_I2C_BLOCK_BROKEN) {

                    65.            /* Convert old I2C block commands to the new

                    66.             convention. This preserves binary compatibility. */

                    67.            data_arg.size = I2C_SMBUS_I2C_BLOCK_DATA;

                    68.            if (data_arg.read_write == I2C_SMBUS_READ)

                    69.                temp.block[0] = I2C_SMBUS_BLOCK_MAX;

                    70.        }

                    71.        //smbus数据传输,2中已经讲述

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

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

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

                    75.             (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) ||

                    76.             (data_arg.read_write == I2C_SMBUS_READ))) {

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

                    78.                return -EFAULT;

                    79.        }

                    80.        return res;

                    81.    }







                    二、用户空间使用i2c_dev

                    对于注册的i2c适配器,用户空间可以使用它们。上面的驱动对每个适配器生成一个主设备号为89的设备节点,实现了文件操作接口,用户空间可以通过i2c设备节点访问i2c适配器。适配器的编号从0开始,和适配器的设备节点的次设备号相同。i2c适配器的设备节点是/dev/i2c-x,其中x是数字,代表适配器的编号。由于适配器编号是动态分配的(和注册次序有关),所以想了解哪一个适配器对应什么编号,可以查看/sys/class/i2c-dev/目录下的文件内容。







                    2.1 前期准备

                    为了在用户空间的程序当中操作i2c适配器,必须在程序中包含以下两句:

                    1.  #include<linux/i2c-dev.h>

                    2.  #include<linux/i2c.h>

                    这两个头文件中定义了之后需要用到的结构体和宏。然后就可以打开设备节点了。但是打开哪一个呢?因为适配器的编号并不固定。为此我们在终端中运行以下命令:

                    [root@hdw /]# cat /sys/class/i2c-dev/i2c-0/name

                    BLX GSC3280 I2C adapter

                    如果我们想打开第二个适配器,刚好它的编号是1,对应的设备节点是/dev/i2c-1。那么可以用下面的方法打开它:

                    1.  int fd;

                    2.  if ((fd = open("/dev/i2c-1",O_RDWR))< 0)

                    3.  {

                    4.      /* 错误处理 */        

                    5.      exit(1);

                    6.  }

                    打开适配器对应的设备节点,i2c-dev为打开的线程建立一个i2c_client,但是这个i2c_client并不加到i2c_adapter的client链表当中。当用户关闭设备节点时,它自动被释放。







                    2.2 IOCTL控制

                    查看include/linux/i2c-dev.h文件,可以看到i2c-dev支持的IOCTL命令。如下:

                    1.  #define I2C_RETRIES 0x0701 /* 设置收不到ACK时的重试次数 */

                    2.  #define I2C_TIMEOUT 0x0702 /* 设置超时时限的jiffies */

                    3.  #define I2C_SLAVE 0x0703 /* 设置从机地址 */

                    4.  #define I2C_SLAVE_FORCE 0x0706 /* 强制设置从机地址 */

                    5.  #define I2C_TENBIT 0x0704 /* 选择地址位长:=0 for 7bit , != 0 for 10 bit */

                    6.  #define I2C_FUNCS 0x0705 /* 获取适配器支持的功能 */

                    7.  #define I2C_RDWR 0x0707 /* Combined R/W transfer (one STOP only) */

                    8.  #define I2C_PEC 0x0708 /* != 0 to use PEC with SMBus */

                    9.  #define I2C_SMBUS 0x0720 /* SMBus transfer */


                    下面进行一一解释。

                    1.设置重试次数      

                          1.   ioctl(fd, I2C_RETRIES, m); //这句话设置适配器收不到ACK时重试的次数为m。默认的重试次数为1。


                    2.设置超时

                          1.   ioctl(fd, I2C_TIMEOUT, m); //设置SMBus的超时时间为m,单位为jiffies。


                    3.设置从机地址

                          1.   ioctl(fd, I2C_SLAVE,addr);

                          2.   ioctl(fd, I2C_SLAVE_FORCE, addr);


                    在调用read()和write()函数之前必须设置从机地址。这两行都可以设置从机的地址,区别是第二行无论内核中是否已有驱动在使用这个地址都会成功, 第一行则只在该地址空闲的情况下成功。由于i2c-dev创建的i2c_client不加入i2c_adapter的client列表,所以不能防止其它线程使用同一地址,也不能防止驱动??檎加猛坏刂?。

                    4.设置地址模式
                          1.   ioctl(file, I2C_TENBIT, select); //如果select不等于0选择10比特地址模式,如果等于0选择7比特模式,默认7比特。只有适配器支持I2C_FUNC_10BIT_ADDR,这个请求才是有效的。

                    5.获取适配器功能
                          1.     ioctl(file, I2C_FUNCS, (unsignedlong *)funcs); //获取的适配器功能保存在funcs中。各比特的含义如:
                          2.     /* include/linux/i2c.h */
                          3.     #define I2C_FUNC_I2C 0x00000001
                          4.     #define I2C_FUNC_10BIT_ADDR 0x00000002
                          5.     #define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */
                          6.     #define I2C_FUNC_SMBUS_PEC 0x00000008
                          7.     #define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 */
                          8.     #define I2C_FUNC_SMBUS_QUICK 0x00010000
                          9.     #define I2C_FUNC_SMBUS_READ_BYTE 0x00020000
                         10.    #define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000
                         11.    #define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000
                         12.    #define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000
                         13.    #define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000
                         14.    #define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000
                         15.    #define I2C_FUNC_SMBUS_PROC_CALL 0x00800000
                         16.    #define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000
                         17.    #define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
                         18.    #define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */
                         19.    #define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */
                         20.    #define I2C_FUNC_SMBUS_READ_I2C_BLOCK_2 0x10000000 /* I2C-like block xfer */
                         21.    #define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2 0x20000000 /* w/ 2-byte reg. addr. */

                    6.I2C层通信
                          1.     ioctl(file, I2C_RDWR, (structi2c_rdwr_ioctl_data *)msgset);

                    这一行代码可以使用I2C协议和设备进行通信。它进行连续的读写,中间没有间歇。只有当适配器支持I2C_FUNC_I2C此命令才有效。参数是一个指针,指向一个结构体,它的定义如:
                          1.     struct i2c_rdwr_ioctl_data {
                          2.         struct i2c_msg __user *msgs; /* 指向i2c_msgs数组 */
                          3.         __u32nmsgs; /* 消息的个数 */
                          4.     };

                    msgs[] 数组成员包含了指向各自缓冲区的指针。这个函数会根据是否在消息中的flags置位I2C_M_RD来对缓冲区进行读写。从机的地址以及是否使用10比特地址模式记录在每个消息中,忽略之前ioctl设置的结果。

                    7.设置SMBus PEC 
                          1.     ioctl(file, I2C_PEC, (long )select);

                    如果select不等于0选择SMBus PEC (packet error checking),等于零则关闭这个功能,默认是关闭的。

                    这个命令只对SMBus传输有效。这个请求只在适配器支持I2C_FUNC_SMBUS_PEC时有效;如果不支持这个命令也是安全的,它不做任何工作。

                    8.SMBus通信
                          1.   ioctl(file, I2C_SMBUS, (i2c_smbus_ioctl_data*)msgset);

                    这个函数和I2C_RDWR类似,参数的指针指向i2c_smbus_ioctl_data类型的变量,它的定义如:

                          1.   struct i2c_smbus_ioctl_data {

                          2.       __u8read_write;

                          3.       __u8command;

                          4.       __u32size;

                          5.       unioni2c_smbus_data __user *data;

                          6.   };







                    2.3 i2c_dev使用例程

                    要想在用户空间使用i2c适配器,首先要选择某个适配器的设备节点打开,然后才能进行通信。

                    2.3.1 read()/write()

                    通信的方式有两种,一种是使用操作普通文件的接口read()和write()。这两个函数间接调用了i2c_master_recv和i2c_master_send。但是在使用之前需要使用I2C_SLAVE设置从机地址,设置可能失败,需要检查返回值。这种通信过程进行I2C层的通信,一次只能进行一个方向的传输。

                    下面的程序是ARM与E2PROM芯片通信的例子,使用read()/write()与i2c设备通信:

                    1.       #include <stdio.h>

                    2.       #include <sys/ioctl.h>

                    3.       #include <fcntl.h>

                    4.       #include <linux/i2c-dev.h>

                    5.       #include <linux/i2c.h>

                    6.        

                    7.       #define CHIP "/dev/i2c-0"

                    #define CHIP_ADDR 0x50

                    8.        

                    9.        

                    10.      int main()

                    11.       

                    12.      {

                    13.          printf("this is i2c test/n");

                    14.          int fd =open(CHIP, O_RDWR);

                    15.          if (fd< 0) {

                    16.              printf("open"CHIP"failed/n");        

                    17.              gotoexit;

                    18.       

                    19.          }

                    20.          if (ioctl(fd, I2C_SLAVE_FORCE, CHIP_ADDR) < 0) { 

                    21.              /* 设置芯片地址 */

                    22.              printf("oictl:setslave address failed/n");

                    23.              goto close;

                    24.       

                    25.          }

                    26.          struct i2c_msg msg;    

                    27.          unsigned char rddata;

                    28.          unsigned char rdaddr[2] = {0, 0}; /* 将要读取的数据在芯片中的偏移量 */

                    29.          unsigned char wrbuf[3] = {0, 0, 0x3c}; /* 要写的数据,头两字节为偏移量 */

                    30.          printf("inputa char you want to write to E2PROM/n");

                    31.          wrbuf[2]= getchar();

                    32.          printf("writereturn:%d, write data:%x/n", write(fd, wrbuf, 3), wrbuf[2]);

                    33.          sleep(1);

                    34.          printf("writeaddress return: %d/n",write(fd, rdaddr, 2)); /* 读取之前首先设置读取的偏移量 */

                    35.          printf("readdata return:%d/n", read(fd, &rddata, 1));

                    36.          printf("rddata:%c/n", rddata);

                    37.      close:

                    38.          close(fd);

                    39.      exit:  

                    40.          return0;

                    41.      }







                    2.3.2  I2C_RDWR

                    还可以使用I2C_RDWR实现同样的功能,使用I2C_RDWR与I2C设备通信

                    1.     #include <stdio.h>

                    2.     #include <sys/ioctl.h>

                    3.     #include <fcntl.h>

                    4.     #include <linux/i2c-dev.h>

                    5.     #include <linux/i2c.h>

                    1.     #define CHIP "/dev/i2c-0"

                    2.     #define CHIP_ADDR 0x50

                    3.     int main()

                    4.     {

                    5.         printf("hello,this is i2c tester/n");

                    6.         int fd =open(CHIP, O_RDWR);

                    7.         if (fd< 0) {

                    8.             printf("open"CHIP"failed/n");

                    9.             gotoexit;

                    10.        }

                    11.        struct i2c_msg msg;

                    12.        unsigned char rddata;

                    13.        unsigned char rdaddr[2] = {0, 0}; 

                    14.        unsigned char wrbuf[3] = {0, 0, 0x3c};

                    15.        printf("inputa char you want to write to E2PROM/n");

                    16.        wrbuf[2]= getchar();

                    17.        struct i2c_rdwr_ioctl_data ioctl_data;

                    18.        struct i2c_msg msgs[2];

                    19.        msgs[0].addr= CHIP_ADDR;

                    20.        msgs[0].len= 3;

                    21.        msgs[0].buf= wrbuf;

                    22.        ioctl_data.nmsgs= 1;

                    23.        ioctl_data.msgs= &msgs[0];

                    24.        printf("ioctlwrite,return :%d/n", ioctl(fd, I2C_RDWR, &ioctl_data));

                    25.        sleep(1);

                    26.        msgs[0].addr= CHIP_ADDR;  

                    27.        msgs[0].len= 2;

                    28.        msgs[0].buf= rdaddr;

                    29.        msgs[1].addr= CHIP_ADDR;

                    30.        msgs[1].flags|= I2C_M_RD;

                    31.        msgs[1].len= 1;

                    32.        msgs[1].buf= &rddata;

                    33.        ioctl_data.nmsgs= 1;

                    34.        ioctl_data.msgs= msgs;

                    35.        printf("ioctlwrite address, return :%d/n", ioctl(fd, I2C_RDWR, &ioctl_data));

                    36.        ioctl_data.msgs= &msgs[1];

                    37.        printf("ioctlread, return :%d/n", ioctl(fd, I2C_RDWR, &ioctl_data));

                    38.        printf("rddata:%c/n", rddata);

                    39.        close:

                    40.        close(fd);

                    41.     exit:

                    42.        return0;

                    43.          }







                    原文请参见:http://blog.chinaunix.net/uid-25445243-id-3862576.html















                     
                    彩神ll【中国】股份有限公司