本文转载自: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\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. 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\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. 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.\n",
  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\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. #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. }
 

Linux I2C驱动分析(三)----i2c_dev驱动和应用层分析 【转】的更多相关文章

  1. Linux内核设计第三周学习总结 跟踪分析Linux内核的启动过程

    陈巧然 原创作品 转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验步骤 登陆实验楼虚 ...

  2. Spring5深度源码分析(三)之AnnotationConfigApplicationContext启动原理分析

    代码地址:https://github.com/showkawa/spring-annotation/tree/master/src/main/java/com/brian AnnotationCon ...

  3. Linux I2C设备驱动编写(二)

    在(一)中简述了Linux I2C子系统的三个主要成员i2c_adapter.i2c_driver.i2c_client.三者的关系也在上一节进行了描述.应该已经算是对Linux I2C子系统有了初步 ...

  4. 【转】Linux I2C设备驱动编写(二)

    原文网址:http://www.cnblogs.com/biglucky/p/4059582.html 在(一)中简述了Linux I2C子系统的三个主要成员i2c_adapter.i2c_drive ...

  5. linux i2c驱动架构-dm368 i2c驱动分析

      linux i2c驱动架构-dm368 i2c驱动分析   在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i2c-core.c以及芯片提供商的提供的i2c总线驱动(i2c-davinc ...

  6. linux驱动基础系列--Linux I2c驱动分析

    前言 主要是想对Linux I2c驱动框架有一个整体的把控,因此会忽略协议上的某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.设备模型.sysfs等也不进行详细说明原理,涉及到i2c协议部分也只 ...

  7. Linux I2C总线设备驱动模型分析(ov7740)

    1. 框架1.1 硬件协议简介1.2 驱动框架1.3 bus-drv-dev模型及写程序a. 设备的4种构建方法a.1 定义一个i2c_board_info, 里面有:名字, 设备地址 然后i2c_r ...

  8. Linux I2C核心、总线和设备驱动

    目录 更新记录 一.Linux I2C 体系结构 1.1 Linux I2C 体系结构的组成部分 1.2 内核源码文件 1.3 重要的数据结构 二.Linux I2C 核心 2.1 流程 2.2 主要 ...

  9. Linux i2c子系统(一) _动手写一个i2c设备驱动

    i2c总线是一种十分常见的板级总线,本文以linux3.14.0为参考, 讨论Linux中的i2c驱动模型并利用这个模型写一个mpu6050的驱动, 最后在应用层将mpu6050中的原始数据读取出来 ...

随机推荐

  1. vue-clickoutside d

    js文件 export default { bind(el, binding, vnode) { function documentHandler(e) { if (el.contains(e.tar ...

  2. 笔记《精通css》第4章 背景图像,平铺方式,背景定位,圆角框,投影,不透明

    第4章  背景图像,平铺方式,背景定位,圆角框,投影,不透明 1.背景图像 background-image:url() 2.平铺方式 background-repeat:repeat-x repea ...

  3. SQLSERVER SQL性能优化技巧

    这篇文章主要介绍了SQLSERVER SQL性能优化技巧,需要的朋友可以参考下 1.选择最有效率的表名顺序(只在基于规则的优化器中有效)       SQLSERVER的解析器按照从右到左的顺序处理F ...

  4. JS——思维拓展

    1.阶乘求和:4的阶乘是1*2*3*4 <script> function jiechen(value) { var n = 1; for (var i = 1; i <= valu ...

  5. Django中 基于form的注册,基于ajax的登录

    1 form.py中写register的的form组件 from django import forms class Register(forms.Form): # 注册的form username ...

  6. centos添加永久的环境变量

    cd /etc/profile.d/ 创建一个sh文件 vi dotnetpath.sh 内容如下: export PATH=$PATH:/opt/dotnet 保存,重启,这就有了一个永久的环境变量

  7. [pytorch学习]1.pytorch ubuntu安装

    看完了Deep Learning with Python,尝试了部分Keras的demo代码. 感觉Keras虽然容易上手,能够快速搭建出一个通用的模型,但是缺乏对底层的控制. 同时,在使用了自己编译 ...

  8. 65.dynamic mapping

    主要知识点: 理解dynamic mapping 定制dynamic mapping 更改default dynamic mapping     一.理解dynamic mapping 1.基本概念 ...

  9. MySql 日志查看与设置

    错误日志log-errol 开启方式:在my.ini的[mysqld]选项下:添加代码:log-error=E:\log-error.txt 记录内容:主要是记录启动.运行或停止mysqld时出现的致 ...

  10. 团体程序设计天梯赛-练习集L1-006. *连续因子

    L1-006. 连续因子 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 一个正整数N的因子中可能存在若干连续的数字.例如630 ...