十九、IIC驱动框架介绍及驱动代码解析

一、IIC驱动框架简介

1、IIC物理总线

  • SCL:时钟线,数据收发同步。
  • SDL:数据线,具体数据

支持一主多从,各设备地址独立,标准模式传输速率为100kbit/s,快速模式为400kbit/s

2、常见IIC设备

  • EEPROM
  • 触摸芯片
  • 温湿度传感器
  • mpu6050

3、框架图

  • I2C核心

    提供I2C总线驱动和设备驱动的注册方法、注销方法、I2C通信硬件无关代码。

  • I2C总线驱动

    主要包含I2C硬件体系结构中适配器(IIC控制器)的控制,用于I2C读写时序。

    主要数据结构:I2C_adapter、 I2C_algorithm.

  • I2C设备驱动

    通过I2C适配器与CPU交换数据。

    主要数据结构:i2c_driver和i2c_client.

  • I2C适配器

    i2c adapter是软件上抽象出来的i2c总线控制器接口,物理上一条i2c总线可以挂接多个硬件设备(slave)一个CPU可以挂接多条i2c总线(想象一下PCI总线),i2c总线控制器就是CPU访问I2C总线的硬件接口,也就是你说的那几个寄存器  

    简单来说,你的开发板上有几个I2C接口,就有几个adapter , 也就是有几条I2C bus , I2C CLIENT 对应的就是你的外围I2C 设备,有几个就有几个CLIENT , 把这些设备插入开发板, 对应其中的一条BUS, 那么相应的就对应了其中的一个ADAPTER , 接下来的就是                 CLIENT 与 ADAPTER 勾搭成对了, 后面就是做该做的事了

二、I2C驱动文件分析

1、i2c-dev.c

(1)初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
static int __init i2c_dev_init(void)
{
    int res;
 
    printk(KERN_INFO "i2c /dev entries driver\n");
    //1、注册字符设备
    res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
    if (res)
        goto out;
    //2、创建类
    i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
    if (IS_ERR(i2c_dev_class)) {
        res = PTR_ERR(i2c_dev_class);
        goto out_unreg_chrdev;
    }
    i2c_dev_class->dev_groups = i2c_groups;
   //3、追踪哪个i2c适配器被添加或者移除   
    /* Keep track of adapters which will be added or removed later */
    res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
    if (res)
        goto out_unreg_class;
   //4、立即绑定已经存在的适配器
    /* Bind to already existing adapters right away */
    i2c_for_each_dev(NULL, i2cdev_attach_adapter);
 
    return 0;
 
out_unreg_class:
    class_destroy(i2c_dev_class);
out_unreg_chrdev:
    unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
out:
    printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
    return res;
}
(2)文件操作集合  

i2c设备驱动的文件操作集合中有两个ioctl:

1
2
3
4
5
6
7
8
9
10
static const struct file_operations i2cdev_fops = {
    .owner      = THIS_MODULE,
    .llseek     = no_llseek,
    .read       = i2cdev_read,   //读
    .write      = i2cdev_write,  //写
    .unlocked_ioctl = i2cdev_ioctl, 
    .compat_ioctl   = compat_i2cdev_ioctl,
    .open       = i2cdev_open,
    .release    = i2cdev_release,
}; 

两者的区别在于:

  • 64位的用户程序运行在64位的kernel上,调用的是compat_ioctl,
  • 32位的APP运行在32位的kernel上,调用的也是unlocked_ioctl。 
(3)读操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
        loff_t *offset)
{
    char *tmp;
    int ret;
 
    struct i2c_client *client = file->private_data;
        //最多读8192个字节
    if (count > 8192)
        count = 8192;
 
    tmp = kzalloc(count, GFP_KERNEL);
    if (tmp == NULL)
        return -ENOMEM;
 
    pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
        iminor(file_inode(file)), count);
        //接收到i2c传过来的数据
    ret = i2c_master_recv(client, tmp, count);
    if (ret >= 0)          //将数据拷贝到用户空间
        if (copy_to_user(buf, tmp, ret))
            ret = -EFAULT;
    kfree(tmp);
    return ret;
}
(4)写操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static ssize_t i2cdev_write(struct file *file, const char __user *buf,
        size_t count, loff_t *offset)
{
    int ret;
    char *tmp;
    struct i2c_client *client = file->private_data;
 
    if (count > 8192)
        count = 8192;
       //分配一块内存空间,将用户空间的数据拷贝进去
    tmp = memdup_user(buf, count);
    if (IS_ERR(tmp))
        return PTR_ERR(tmp);
 
    pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",
        iminor(file_inode(file)), count);
     //i2c发送 
    ret = i2c_master_send(client, tmp, count);
    kfree(tmp);
    return ret;
}  
1
memdup_user函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
 * memdup_user - duplicate memory region from user space
 *
 * @src: source address in user space
 * @len: number of bytes to copy
 *
 * Return: an ERR_PTR() on failure.  Result is physically
 * contiguous, to be freed by kfree().
 */
void *memdup_user(const void __user *src, size_t len)
{
    void *p;
        //分配内核态的空间
    p = kmalloc_track_caller(len, GFP_USER | __GFP_NOWARN);
    if (!p)
        return ERR_PTR(-ENOMEM);
     //从用户空间拷贝数据
    if (copy_from_user(p, src, len)) {
        kfree(p);
        return ERR_PTR(-EFAULT);
    }
 
    return p;
}
(5)i2cdev_ioctl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct i2c_client *client = file->private_data;
    unsigned long funcs;
 
    dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
        cmd, arg);
 
    switch (cmd) {      //设置从机地址      //I2C_SLAVE和I2C_SLAVE_FORCE的区别在于I2C_SLACE会检查设备地址,如果地址已经使用,就不能使用重复的地址,否则会返回-EBUSY,而I2C_SLACE_FORCE会跳过检查
    case I2C_SLAVE:
    case I2C_SLAVE_FORCE:
        if ((arg > 0x3ff) ||
            (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
            return -EINVAL;
        if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
            return -EBUSY;
        /* REVISIT: address could become busy later */
        client->addr = arg;
        return 0;    //设置10bit地址模式       //如果select不等于0选择10bit地址模式,如果等于0选择7bit模式,默认7bit。只有适配器支持I2C_FUNC_10BIT_ADDR,这个请求才是有效的
    case I2C_TENBIT:
        if (arg)
            client->flags |= I2C_M_TEN;
        else
            client->flags &= ~I2C_M_TEN;
        return 0;        //设置传输后增加PEC标志(用于数据校验)     //这个命令只对SMBus传输有效。这个请求只在适配器支持I2C_FUNC_SMBUS_PEC时有效;如果不支持这个命令也是安全的,它不做任何工作 
    case I2C_PEC:
        /*
         * Setting the PEC flag here won't affect kernel drivers,
         * which will be using the i2c_client node registered with
         * the driver model core.  Likewise, when that client has
         * the PEC flag already set, the i2c-dev driver won't see
         * (or use) this setting.
         */
        if (arg)
            client->flags |= I2C_CLIENT_PEC;
        else
            client->flags &= ~I2C_CLIENT_PEC;
        return 0;    //获取适配器支持的功能:SMbus或者普通的I2C
    case I2C_FUNCS:
        funcs = i2c_get_functionality(client->adapter);
        return put_user(funcs, (unsigned long __user *)arg);
       //i2c读写,和i2c_read、i2c_write的区别在于这两个函数一次只能处理一条消息,而i2c_RDWR一次可以处理多条消息
    case I2C_RDWR: {
        struct i2c_rdwr_ioctl_data rdwr_arg;
        struct i2c_msg *rdwr_pa;
 
        if (copy_from_user(&rdwr_arg,
                   (struct i2c_rdwr_ioctl_data __user *)arg,
                   sizeof(rdwr_arg)))
            return -EFAULT;
 
        if (!rdwr_arg.msgs || rdwr_arg.nmsgs == 0)
            return -EINVAL;
 
        /*
         * Put an arbitrary limit on the number of messages that can
         * be sent at once
         */        //最多处理42条message
        if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS)
            return -EINVAL;
 
        rdwr_pa = memdup_user(rdwr_arg.msgs,
                      rdwr_arg.nmsgs * sizeof(struct i2c_msg));
        if (IS_ERR(rdwr_pa))
            return PTR_ERR(rdwr_pa);
 
        return i2cdev_ioctl_rdwr(client, rdwr_arg.nmsgs, rdwr_pa);
    }
    //smbus协议
    case I2C_SMBUS: {
        struct i2c_smbus_ioctl_data data_arg;
        if (copy_from_user(&data_arg,
                   (struct i2c_smbus_ioctl_data __user *) arg,
                   sizeof(struct i2c_smbus_ioctl_data)))
            return -EFAULT;
        return i2cdev_ioctl_smbus(client, data_arg.read_write,
                      data_arg.command,
                      data_arg.size,
                      data_arg.data);
    }    //设置重试次数    //这句话设置适配器收不到ACK时重试的次数为m。默认的重试次数为1。
    case I2C_RETRIES:
        if (arg > INT_MAX)
            return -EINVAL;
 
        client->adapter->retries = arg;
        break;    //超时时间,时间单位为jiffes
    case I2C_TIMEOUT:
        if (arg > INT_MAX)
            return -EINVAL;
 
        /* For historical reasons, user-space sets the timeout
         * value in units of 10 ms.
         */
        client->adapter->timeout = msecs_to_jiffies(arg * 10);
        break;
    default:
        /* NOTE:  returning a fault code here could cause trouble
         * in buggy userspace code.  Some old kernel bugs returned
         * zero in this case, and userspace code might accidentally
         * have depended on that bug.
         */
        return -ENOTTY;
    }
    return 0;
}
1
I2C_RDWR应用实例:i2c驱动之调用ioctl函数进行读写at24c08

i2c数据传输主要通过i2c_transfer函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
 * i2c_transfer - execute a single or combined I2C message
 * @adap: Handle to I2C bus
 * @msgs: One or more messages to execute before STOP is issued to
 *  terminate the operation; each message begins with a START.
 * @num: Number of messages to be executed.
 *
 * Returns negative errno, else the number of messages executed.
 *
 * Note that there is no requirement that each message be sent to
 * the same slave address, although that is the most common model.
 */
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
    int ret;
 
    if (!adap->algo->master_xfer) {
        dev_dbg(&adap->dev, "I2C level transfers not supported\n");
        return -EOPNOTSUPP;
    }
 
    /* REVISIT the fault reporting model here is weak:
     *
     *  - When we get an error after receiving N bytes from a slave,
     *    there is no way to report "N".
     *
     *  - When we get a NAK after transmitting N bytes to a slave,
     *    there is no way to report "N" ... or to let the master
     *    continue executing the rest of this combined message, if
     *    that's the appropriate response.
     *
     *  - When for example "num" is two and we successfully complete
     *    the first message but get an error part way through the
     *    second, it's unclear whether that should be reported as
     *    one (discarding status on the second message) or errno
     *    (discarding status on the first one).
     */
    ret = __i2c_lock_bus_helper(adap);
    if (ret)
        return ret;
 
    ret = __i2c_transfer(adap, msgs, num);
    i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
 
    return ret;
}
(6)退出函数
1
2
3
4
5
6
7
static void __exit i2c_dev_exit(void)
{
    bus_unregister_notifier(&i2c_bus_type, &i2cdev_notifier);
    i2c_for_each_dev(NULL, i2cdev_detach_adapter);
    class_destroy(i2c_dev_class);
    unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
}

2、i2c_algorithm

两种i2c算法,一种是普通的I2c数据通讯协议,一种是SMbus协议,是两种不同的通信方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
 * struct i2c_algorithm - represent I2C transfer method
 * @master_xfer: Issue a set of i2c transactions to the given I2C adapter
 *   defined by the msgs array, with num messages available to transfer via
 *   the adapter specified by adap.
 * @master_xfer_atomic: same as @master_xfer. Yet, only using atomic context
 *   so e.g. PMICs can be accessed very late before shutdown. Optional.
 * @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this
 *   is not present, then the bus layer will try and convert the SMBus calls
 *   into I2C transfers instead.
 * @smbus_xfer_atomic: same as @smbus_xfer. Yet, only using atomic context
 *   so e.g. PMICs can be accessed very late before shutdown. Optional.
 * @functionality: Return the flags that this algorithm/adapter pair supports
 *   from the I2C_FUNC_* flags.
 * @reg_slave: Register given client to I2C slave mode of this adapter
 * @unreg_slave: Unregister given client from I2C slave mode of this adapter
 *
 * The following structs are for those who like to implement new bus drivers:
 * i2c_algorithm is the interface to a class of hardware solutions which can
 * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
 * to name two of the most common.
 *
 * The return codes from the @master_xfer{_atomic} fields should indicate the
 * type of error code that occurred during the transfer, as documented in the
 * Kernel Documentation file Documentation/i2c/fault-codes.rst.
 */
struct i2c_algorithm {
    /*
     * If an adapter algorithm can't do I2C-level access, set master_xfer
     * to NULL. If an adapter algorithm can do SMBus access, set
     * smbus_xfer. If set to NULL, the SMBus protocol is simulated
     * using common I2C messages.
     *
     * master_xfer should return the number of messages successfully
     * processed, or a negative value on error
     */
    int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
               int num);
    int (*master_xfer_atomic)(struct i2c_adapter *adap,
                   struct i2c_msg *msgs, int num);
    int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr,
              unsigned short flags, char read_write,
              u8 command, int size, union i2c_smbus_data *data);
    int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u16 addr,
                 unsigned short flags, char read_write,
                 u8 command, int size, union i2c_smbus_data *data);
 
    /* To determine what the adapter supports */
    u32 (*functionality)(struct i2c_adapter *adap);
 
#if IS_ENABLED(CONFIG_I2C_SLAVE)
    int (*reg_slave)(struct i2c_client *client);
    int (*unreg_slave)(struct i2c_client *client);
#endif
};

3、i2c总线驱动分析(i2c-core-base.c)

(1)i2c总线定义
1
2
3
4
5
6
7
struct bus_type i2c_bus_type = {
    .name       = "i2c",
    .match      = i2c_device_match,
    .probe      = i2c_device_probe,
    .remove     = i2c_device_remove,
    .shutdown   = i2c_device_shutdown,
};
(2)总线注册
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
static int __init i2c_init(void)
{
    int retval;
 
    retval = of_alias_get_highest_id("i2c");
 
    down_write(&__i2c_board_lock);
    if (retval >= __i2c_first_dynamic_bus_num)
        __i2c_first_dynamic_bus_num = retval + 1;
    up_write(&__i2c_board_lock);
 
    retval = bus_register(&i2c_bus_type);
    if (retval)
        return retval;
 
    is_registered = true;
 
#ifdef CONFIG_I2C_COMPAT
    i2c_adapter_compat_class = class_compat_register("i2c-adapter");
    if (!i2c_adapter_compat_class) {
        retval = -ENOMEM;
        goto bus_err;
    }
#endif
    retval = i2c_add_driver(&dummy_driver);
    if (retval)
        goto class_err;
 
    if (IS_ENABLED(CONFIG_OF_DYNAMIC))
        WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
    if (IS_ENABLED(CONFIG_ACPI))
        WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier));
 
    return 0;
 
class_err:
#ifdef CONFIG_I2C_COMPAT
    class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
    is_registered = false;
    bus_unregister(&i2c_bus_type);
    return retval;
}
(3)i2c设备和驱动匹配规则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
    struct i2c_client   *client = i2c_verify_client(dev);
    struct i2c_driver   *driver;
 
 
    /* Attempt an OF style match */
    if (i2c_of_match_device(drv->of_match_table, client))
        return 1;
 
    /* Then ACPI style match */
    if (acpi_driver_match_device(dev, drv))
        return 1;
 
    driver = to_i2c_driver(drv);
 
    /* Finally an I2C match */
    if (i2c_match_id(driver->id_table, client))
        return 1;
 
    return 0;
}
  • i2c_of_match_device:设备树匹配方式

    比较I2C设备节点的compatible属性和of_device_id中的compatible属性

  • acpi_driver_match_device: ACPI匹配方式
  • i2c_match_id: i2c总线传统匹配方式

    比较i2c设备名字和i2c驱动的id_table->name字段是否相等

三、SMbus介绍 

1、介绍

  • 系统管理总线(SMBus)是一个两线接口。通过它,各设备之间以及设备与系统的其他部分之间可以互相通信。
  • 它基于I2C操作原理。SMBus为系统和电源管理相关的任务提供一条控制总线。一个系统利用SMBus可以和多个设备互传信息,而不需使用独立的控制线路。
  • 系统管理总线(SMBus)标准涉及三类设备。从设备-接收或响应命令的设备。主设备-用来发布命令,产生时钟和终止发送的设备。主机,是一种专用的主设备,它提供与系统CPU的主接口。主机必须具有主-从机功能,并且必须支持SMBus通报协议。
  • 在一个系统里只允许有一个主机

2、SMBus和I2C之间的相似点

  • 2条线的总线协议(1个时钟,1个数据) + 可选的SMBus提醒线
  • 主-从通信,主设备提供时钟
  • 多主机功能
  • SMBus数据格式类似于I2C的7位地址格式

3、SMBus和I2C之间的不同点

SMbus i2c
传输速度 10khz~100khz 最小传输速度 10kHz 无最小传输速度 
最小传输速度 35ms时钟低超时 无时钟超时 
固定的逻辑电平 逻辑电平由VDD决定
不同的地址类型(保留、动态等) 7位、10位和广播呼叫从地址类型
不同的总线协议(快速命令、处理呼叫等) 无总线协议

 

4、SMBus应用用途

  利用系统管理总线,设备可提供制造商信息,告诉系统它的型号/部件号,保存暂停事件的状态,报告不同类型的错误,接收控制参数,和返回它的状态。SMBus为系统和电源管理相关的任务提供控制总线。

 

  

  

  

  

  

  

  

posted @   轻轻的吻  阅读(1406)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
历史上的今天:
2019-01-09 C语言可重入函数和不可重入函数
点击右上角即可分享
微信分享提示