zoukankan      html  css  js  c++  java
  • Linux I2C驱动分析(三)----i2c_dev驱动和应用层分析 【转】

    本文转载自:http://blog.chinaunix.net/uid-21558711-id-3959287.html

    一、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 ");
    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 ", __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.     switch (action) {
    9.     case BUS_NOTIFY_ADD_DEVICE:
    10.         return i2cdev_attach_adapter(dev, NULL);
    11.     case BUS_NOTIFY_DEL_DEVICE:
    12.         return i2cdev_detach_adapter(dev, NULL);
    13.     }
    14.     return 0;
    15. }

            紧接着看下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 ",
    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.     if (count > 8192)
    8.         count = 8192;
    9.     tmp = memdup_user(buf, count);
    10.     if (IS_ERR(tmp))
    11.         return PTR_ERR(tmp);
    12.     pr_debug("i2c-dev: i2c-%d writing %zu bytes. ",
    13.         iminor(file->f_path.dentry->d_inode), count);
    14.     ret = i2c_master_send(client, tmp, count);
    15.     kfree(tmp);
    16.     return ret;
    17. }

            该操作比较简单,就是将用户空间的数据通过使用函数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 ",
    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. ",
    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. ",
    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. ");
    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. #define CHIP "/dev/i2c-0"
      #define CHIP_ADDR 0x50
    7. int main()
    8. {
    9.     printf("this is i2c test/n");
    10.     int fd =open(CHIP, O_RDWR);
    11.     if (fd< 0) {
    12.         printf("open"CHIP"failed/n");        
    13.         gotoexit;
    14.     }
    15.     if (ioctl(fd, I2C_SLAVE_FORCE, CHIP_ADDR) < 0) { 
    16.         /* 设置芯片地址 */
    17.         printf("oictl:setslave address failed/n");
    18.         goto close;
    19.     }
    20.     struct i2c_msg msg;    
    21.     unsigned char rddata;
    22.     unsigned char rdaddr[2] = {0, 0}; /* 将要读取的数据在芯片中的偏移量 */
    23.     unsigned char wrbuf[3] = {0, 0, 0x3c}; /* 要写的数据,头两字节为偏移量 */
    24.     printf("inputa char you want to write to E2PROM/n");
    25.     wrbuf[2]= getchar();
    26.     printf("writereturn:%d, write data:%x/n", write(fd, wrbuf, 3), wrbuf[2]);
    27.     sleep(1);
    28.     printf("writeaddress return: %d/n",write(fd, rdaddr, 2)); /* 读取之前首先设置读取的偏移量 */
    29.     printf("readdata return:%d/n", read(fd, &rddata, 1));
    30.     printf("rddata:%c/n", rddata);
    31. close:
    32.     close(fd);
    33. exit:  
    34.     return0;
    35. }

    1.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. }
     
  • 相关阅读:
    数据库01
    并发编程6
    并发编程5
    并发编程4
    并发编程3
    并发编程2
    并发编程1
    kali 下文件操作
    kali driftnet
    2017.1.1
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/7605473.html
Copyright © 2011-2022 走看看