i2c-dev

obj-$(CONFIG_I2C_CHARDEV)    += i2c-dev.o
假如打开了CONFIG_I2C_CHARDEV的话,就可以将i2c 作为一个标准的字符设备来访问
static int __init i2c_dev_init(void)
{
    int res;

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

    res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
    if (res)
        goto out;
//注册i2c对应的class,之后就可以看到/sys/class/i2c-dev,这样如果有i2c总线设备的话,就会在/sys/class/i2c-dev/i2c-0等设备
    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对应的group ,会显示i2c-dev下每个字符设备的name
    i2c_dev_class->dev_groups = i2c_groups;
//bus_register_notifier和i2c_for_each_dev 都是为新的i2c 总线设备insmod 后调用i2cdev_attach_adapter。这不过i2c_for_each_dev 针对当前已经存在的i2c 总线设备,而bus_register_notifier是为未来可能insmod i2c设备调用i2cdev_attach_adapter
    /* 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;

    /* 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;
}

module_init(i2c_dev_init);
在i2c_dev_init 中i2c_dev_class->dev_groups = i2c_groups;中用到的i2c_groups 定义如下
static ssize_t name_show(struct device *dev,
             struct device_attribute *attr, char *buf)
{
    struct i2c_dev *i2c_dev = i2c_dev_get_by_minor(MINOR(dev->devt));

    if (!i2c_dev)
        return -ENODEV;
    return sprintf(buf, "%s\n", i2c_dev->adap->name);
}
static DEVICE_ATTR_RO(name);

static struct attribute *i2c_attrs[] = {
    &dev_attr_name.attr,
    NULL,
};
ATTRIBUTE_GROUPS(i2c);
可见这个通过cat name就key打印这个adapter对应的name.
int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
{
    int res;

    mutex_lock(&core_lock);
    res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
    mutex_unlock(&core_lock);

    return res;
}
为bus_type 为i2c_bus_type的设备调用i2cdev_attach_adapter
而bus_register_notifier(&i2c_bus_type, &i2cdev_notifier); 则是为未来肯能增加或减少的adapter调用i2cdev_notifier
static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
             void *data)
{
    struct device *dev = data;

    switch (action) {
    case BUS_NOTIFY_ADD_DEVICE:
        return i2cdev_attach_adapter(dev, NULL);
    case BUS_NOTIFY_DEL_DEVICE:
        return i2cdev_detach_adapter(dev, NULL);
    }

    return 0;
}

static struct notifier_block i2cdev_notifier = {
    .notifier_call = i2cdev_notifier_call,
};
可见如果是新增的adapter 同样调用的是i2cdev_attach_adapter,这是就和i2c_for_each_dev做的事情一样了
总结一下i2c_dev_init主要就是注册了一个i2c-dev,并为每个已经存在或者未来可能存在的adapter调用i2cdev_attach_adapter
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
    struct i2c_adapter *adap;
    struct i2c_dev *i2c_dev;
    int res;

    if (dev->type != &i2c_adapter_type)
        return 0;
    adap = to_i2c_adapter(dev);

    i2c_dev = get_free_i2c_dev(adap);
    if (IS_ERR(i2c_dev))
        return PTR_ERR(i2c_dev);

    cdev_init(&i2c_dev->cdev, &i2cdev_fops);
    i2c_dev->cdev.owner = THIS_MODULE;
    res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1);
    if (res)
        goto error_cdev;

    /* register this i2c device with the driver core */
    i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
                     MKDEV(I2C_MAJOR, adap->nr), NULL,
                     "i2c-%d", adap->nr);
    if (IS_ERR(i2c_dev->dev)) {
        res = PTR_ERR(i2c_dev->dev);
        goto error;
    }

    pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
         adap->name, adap->nr);
    return 0;
error:
    cdev_del(&i2c_dev->cdev);
error_cdev:
    put_i2c_dev(i2c_dev);
    return res;
}
而在i2cdev_attach_adapter 中会为每个i2c adapter生产一个字符设备,这样就可以在user space方位i2c 设备了
linux-zpo1:/sys/class/i2c-dev/i2c-0 # ls /dev |grep i2c
i2c-0
i2c-1

linux-zpo1:/sys/class/i2c-dev # ls
i2c-0  i2c-1

下面的name就是通过i2c_dev_class->dev_groups = i2c_groups;产生的
linux-zpo1:/sys/class/i2c-dev/i2c-0 # cat name
Synopsys DesignWare I2C adapter


<think>我们正在讨论在Linux用户空间进行I2C通信的问题。用户之前已经了解了通过/dev/i2c-*设备节点进行I2C通信的方法,现在他遇到了在/dev目录下找不到I2C设备节点的情况,并询问是否可以使用/sys/class/i2c-dev中的接口进行用户态I2C通信。 首先,我们需要明确: 1. 在Linux系统中,用户空间访问I2C总线通常是通过/dev/i2c-*字符设备节点进行的。 2. 如果/dev目录下没有这些节点,可能是由于内核没有加载相应的驱动(如i2c-dev模块)或者系统没有可用的I2C总线。 3. /sys/class/i2c-dev目录是sysfs虚拟文件系统的一部分,它提供了I2C总线的信息,但并不直接提供用于读写的设备节点。 然而,用户提到使用/sys/class/i2c-dev接口。实际上,/sys/class/i2c-dev目录下的每个条目(如i2c-0, i2c-1等)对应一个I2C总线,但这些是目录,而不是字符设备。它们包含一些属性文件(如name, device等),但并没有提供直接读写I2C设备的接口。 那么,如何在没有/dev/i2c-*节点的情况下进行用户态I2C通信呢? 解决方案: 1. 确保i2c-dev模块已加载:加载该模块后,系统会在/dev下创建相应的设备节点。 ```bash sudo modprobe i2c-dev ``` 然后检查/dev目录下是否出现了i2c-*设备节点。 2. 如果加载了i2c-dev模块后仍然没有设备节点,可能是由于系统没有检测到I2C控制器或者没有注册总线。此时需要检查硬件和内核配置。 3. 如果确实无法创建/dev/i2c-*节点,那么用户空间程序无法通过标准的I2C接口(如ioctl和read/write)进行通信。在这种情况下,替代方案包括: a) 编写内核驱动:这通常是最标准的方法,但需要内核编程知识。 b) 使用其他用户空间接口:例如,通过GPIO模拟I2C(bit-banging),但这需要可用的GPIO引脚并且性能较低。 4. 关于/sys/class/i2c-dev:这个目录主要用于提供信息,并不支持直接的数据传输。但是,我们可以通过它来获取总线的编号,然后结合/dev下的设备节点使用(如果节点存在)。如果/dev下没有节点,那么/sys/class/i2c-dev并不能直接用于读写。 因此,结论是:不能直接使用/sys/class/i2c-dev中的接口进行用户态I2C通信。必须确保/dev目录下有对应的I2C设备节点。 如果用户无法在/dev下看到设备节点,我们可以按照以下步骤排查: 步骤1:检查i2c-dev模块是否加载 ```bash lsmod | grep i2c_dev ``` 如果没有,则加载: ```bash sudo modprobe i2c-dev ``` 步骤2:检查系统是否识别了I2C总线控制器 ```bash sudo i2cdetect -l ``` 如果这个命令没有输出,说明没有可用的I2C总线,需要检查硬件连接和内核驱动。 步骤3:检查/sys/class/i2c-dev目录 ```bash ls /sys/class/i2c-dev/ ``` 如果有类似i2c-0, i2c-1的目录,说明内核已经注册了I2C总线。此时,应该可以在/dev下看到对应的设备节点(如/dev/i2c-0, /dev/i2c-1)。如果没有,可能是udev规则的问题,可以尝试手动创建设备节点(不推荐,通常由udev自动创建)。 步骤4:手动创建设备节点(如果必要) 例如,如果/sys/class/i2c-dev/i2c-0存在,那么可以手动创建设备节点: ```bash sudo mknod /dev/i2c-0 c 89 0 ``` 其中,89是主设备号(可以通过`cat /sys/class/i2c-dev/i2c-0/dev`得到,输出格式为major:minor),0是次设备号(这里以i2c-0为例,次设备号为0)。但是,这种方法只是临时解决方案,重启后会失效。正确的做法是修复udev规则或确保i2c-dev模块正确加载。 综上所述,用户应该首先确保i2c-dev模块加载,并检查系统是否识别了I2C总线。如果一切正常而/dev下没有节点,可能是权限问题(需要检查udev规则)或者系统配置问题。如果用户确实无法在/dev下创建设备节点,那么用户空间的I2C通信将无法使用标准接口,只能考虑其他替代方案(如内核驱动或GPIO模拟)。 最后,生成相关问题:</think>在 Linux 系统中,当 `/dev` 目录下找不到 I2C 设备节点时,**可以使用 `/sys/class/i2c-dev` 接口作为替代方案进行用户态 I2C 通信**,但实现方式与 `/dev` 接口不同。以下是具体方法和注意事项: --- ### 1. `/sys/class/i2c-dev` 接口的作用 - **信息查询**:提供 I2C 总线信息(如总线编号、适配器名称) - **节点创建**:通过该接口可手动创建设备节点 - **非直接通信**:本身 **不提供直接读写 I2C 数据的接口**,需结合其他方法 --- ### 2. 解决方案:手动创建设备节点 #### 步骤 1:确认 I2C 总线存在 ```bash ls /sys/class/i2c-dev/ # 输出示例: i2c-0 i2c-1 i2c-2 ``` #### 步骤 2:获取设备主次设备号 ```bash cat /sys/class/i2c-dev/i2c-1/dev # 输出示例: 89 1 # 格式: <major>:<minor> ``` #### 步骤 3:手动创建设备节点 ```bash sudo mknod /dev/i2c-1 c 89 1 # 格式: mknod <路径> c <major> <minor> sudo chmod 666 /dev/i2c-1 # 设置权限 ``` #### 步骤 4:验证节点 ```bash ls -l /dev/i2c-1 # 输出示例: crw-rw-rw- 1 root root 89, 1 Jun 10 10:00 /dev/i2c-1 ``` --- ### 3. 替代方案:通过 sysfs 直接访问设备 若设备驱动已导出 sysfs 接口,可直接读写设备属性: ```bash # 示例:读取地址为 0x50 的 EEPROM 设备 echo 0-0050 > /sys/bus/i2c/devices/i2c-1/new_device # 注册设备 # 读取数据(假设驱动导出 eeprom 文件) hexdump -C /sys/bus/i2c/devices/1-0050/eeprom ``` --- ### 4. 根本原因分析与排查 #### 为何 `/dev/i2c-*` 不存在? | 原因 | 检查方法 | 解决方案 | |------|----------|----------| | `i2c-dev` 模块未加载 | `lsmod \| grep i2c_dev` | `sudo modprobe i2c-dev` | | 内核未启用 I2C 支持 | `grep CONFIG_I2C /boot/config-$(uname -r)` | 重新编译内核启用 `CONFIG_I2C` 和 `CONFIG_I2C_CHARDEV` | | 设备权限问题 | `dmesg \| grep i2c` | 配置 udev 规则或使用 `chmod` | | 硬件无 I2C 控制器 | `sudo i2cdetect -l` | 检查硬件连接 | --- ### 5. 完整用户态通信示例(使用创建的节点) ```c #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/i2c-dev.h> #define I2C_NODE "/dev/i2c-1" // 手动创建的节点 #define DEV_ADDR 0x50 int main() { int fd = open(I2C_NODE, O_RDWR); if (fd < 0) { perror("Open failed"); return 1; } if (ioctl(fd, I2C_SLAVE, DEV_ADDR) < 0) { perror("Address set failed"); close(fd); return 1; } // 写入寄存器 0x01 uint8_t reg = 0x01; uint8_t data = 0xAB; if (write(fd, &reg, 1) != 1 || write(fd, &data, 1) != 1) { perror("Write failed"); } close(fd); return 0; } ``` 编译命令:`gcc -o i2c_app i2c_app.c` --- ### 关键结论 1. `/sys/class/i2c-dev` **不能直接用于数据传输**,但可通过它创建设备节点 2. 优先方案:确保 `i2c-dev` 模块加载,自动生成 `/dev/i2c-*` 3. 备选方案: - 手动创建设备节点(临时方案) - 使用驱动导出的 sysfs 接口(需驱动支持) - 通过 GPIO bit-banging 模拟 I2C(性能较低) > **提示**:生产环境中建议通过 udev 规则自动创建设备节点并设置权限[^1]: > ```bash > # /etc/udev/rules.d/99-i2c.rules > SUBSYSTEM=="i2c-dev", MODE="0666" > ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值