i2c 驱动程序是否需要像任何其他字符设备驱动程序一样实现?

Does i2c driver need to be implemented just like any other character device driver?

我是 Linux 设备驱动程序的菜鸟,所以请多多包涵。我正在尝试实现一个 i2c 驱动程序(客户端)。这是我可以 insmod.probe 被调用(因为设备树条目)并且在 .probe 中我可以 read/write 到设备的地方。太好了。

但是我需要能够启动从用户空间到驱动程序的 read/writes。为了做到这一点,i2c 驱动程序的形状是否应该像任何其他字符设备驱动程序一样?意味着具有 file_operations 结构,因此用户空间可以 openclosereadwriteioctls?

我问是因为在我见过的所有 i2c 客户端示例中,没有人实现了我提到的这些东西。我想知道他们是如何在没有设置 file_operations 结构的情况下从用户空间发起调用的。也许它是如此明显以至于没有人提到它,我不知道......我想知道是否因为 i2c 被称为平台设备驱动程序它不需要这个?如果有人可以确认这将帮助我重新猜测自己。

如果您明白我在问什么,请忽略其余部分。如果您对我的问题感到困惑,这里是对我所问内容的更具体解释:

我现在拥有的是:

static int device_probe(struct i2c_client           *client,
                        const struct i2c_device_id  *id)
{
    struct device      *dev = &client->dev;
    struct device_data *data;

    /* Check the functionality of the i2c-adapter for smbus byte read/write */
    if (!i2c_check_functionality(client->adapter,
                                 I2C_FUNC_SMBUS_BYTE_DATA))
    {
        printk(KERN_ALERT "%s %d: device required i2c functionality is not supported\n", __func__, __LINE__);
        return -ENODEV;
    }

    /* Allocate memory to hold the device data
     * Using devm_kzalloc so do not have to worry about kfree */
    data = devm_kzalloc(dev, sizeof(struct device_data), GFP_KERNEL);
    if (dev == NULL)
    {
        printk(KERN_ALERT "%s %d: no memory\n", __func__, __LINE__);
        return -ENOMEM;
    }

    /* Record the pointer to current client */
    data->device_i2c_client = client;

    /* Set the client's data field to point to device-specific data struct */
    i2c_set_clientdata(client, data);

    /* Add the device-specific data struct to our collection of device client devices */
    device_data_tbl[id->driver_data] = data;

    /* Do a read, test the access */
    device_read();

    return 0;
}

static int device_remove(struct i2c_client *client)
{
    return 0;
}


int device_read(uint8_t             device_device_id,
                uint16_t const      dev_reg_addr,
                uint8_t *const      read_val)
{
    /* some read access */
}

static struct i2c_device_id device_idtable[] = {
    { "si5342",   0 },
    { },
};
MODULE_DEVICE_TABLE(i2c, device_idtable);

static struct i2c_driver device_driver = {
  .driver = {
    .name = device_DRIVER_NAME,
        .owner  = THIS_MODULE
  },
  .id_table = device_idtable,
    .probe      = device_probe,
    .remove     = device_remove,
};

static int __init device_driver_init(void)
{
    return i2c_add_driver(&device_driver);
}
module_init(device_driver_init);


static void __exit device_driver_exit(void)
{
    return i2c_del_driver(&device_driver);
}
module_exit(device_driver_exit);

想知道是否需要在中添加以下元素

static struct file_operations oxdrv_fops =
{
    .owner   = THIS_MODULE,
    .release = device_release,
    .open    = device_open,
    .unlocked_ioctl = device_ioctl
};

/* Associated function definitions: device_open, device_ioctl, etc */

alloc_chrdev_region();
cdev_init();

我想我现在通过@Alexandre Belloni 的评论和阅读这组演示幻灯片更好地理解了设备驱动程序模型:http://free-electrons.com/doc/training/linux-kernel/linux-kernel-slides.pdf。相关幻灯片从第 221 页到第 236 页。

有 3 种类型的设备驱动程序:

  1. 字符
  2. 网络
  3. 阻止

但是,有特定的 "frameworks" 作为字符设备驱动程序的子类存在,它实现了相同类型设备驱动程序的公共部分。

例如,主板上用于硬件监控的温度传感器将在 hwmon 框架下注册 (https://www.kernel.org/doc/Documentation/hwmon/hwmon-kernel-api.txt)。您将实现 i2c probereadwrite 功能,而不是将它塑造成具有 file_operations struct 的字符设备,您只需要将其注册为 hwmon 设备: hwmon_device_register_with_groups()。要暴露给用户空间,你需要使用 attributes 和你想要的暴露的 read/write 命令列表来构建你的 /sys/class/hwmon/hwmon* 目录(例如:从通道 1 读取温度,写入限制温度寄存器) .

构建内核时,select 您的设备 make menuconfig 以便与内核一起构建。有了这个,一旦你启动内核,设备就会出现在 /sys/class/hwmon/hwmon* 下,然后用户空间可以通过 sysfs 接口从设备 openread 。 在这里看到一个很好的例子:http://lxr.free-electrons.com/source/drivers/hwmon/tmp421.c。或 hwmon 目录中的任何设备。

这就是我的困惑所在。正如@Alexandre Belloni 指出的那样,这些设备是在一个框架下注册的,因此不需要显式的字符设备驱动程序代码。对我来说情况并非如此,我认为我正在做的设备(时钟 PLL)没有合适的框架。因此,我将需要走一般路线并将其实现为字符设备。这也将允许我 load/unload 作为一个模块,而不是在内核启动期间自动加载它。

请随时纠正我犯的任何错误。我希望这对其他对编写 i2c 客户端感到困惑的人有所帮助。