【问题标题】:Does i2c driver need to be implemented just like any other character device driver?是否需要像任何其他字符设备驱动程序一样实现 i2c 驱动程序?
【发布时间】:2017-01-05 19:11:25
【问题描述】:

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

但是,我需要能够启动从用户空间到驱动程序的读/写操作。为了做到这一点,i2c 驱动程序是否应该像任何其他 char 设备驱动程序一样设计?意味着有一个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();

【问题讨论】:

  • 是的,很明显。驱动程序不需要这个,因为 I2C hostclient 之间通过专门设计的 API 进行通信。只有一个驱动程序直接实现了与从属设备的用户空间通信,显然您不需要。
  • 重复什么?我并不是要争论,但老实说,我不知道您要传达的解决方案。无论如何,这整个 Linux i2c 客户端编程似乎都是一个废话。文档描述部分,需要完整的解决方案。我会相信我的直觉,并将其实现为字符驱动程序,就像这里发布的答案一样:stackoverflow.com/questions/23979129/i2c-device-linux-driver。你会注意到这个人也回答了他们自己的问题,就像其他与 i2c 客户端相关的问题一样
  • 好吧抱歉,我好像很懒。所以总结一下你是说:是的应该实现为用户空间的字符驱动程序 - > i2c-client驱动程序,并且可以从drivers/i2c/i2c-dev.c建模。我将 i2c 驱动程序作为 char 驱动程序的实现现在正在使用 ioctls 和 procfs。
  • 驱动程序没有自己注册字符设备,因为框架正在为他们做这件事。为您的设备选择正确的框架并在那里注册您的驱动程序。
  • @0andriy,我知道你说过你不会回应,只是为了说清楚。 a) 用户空间中的 i2c-dev,这是我当前的实现,但它不是并发安全的。我必须共享进程互斥锁,这似乎不对。 b)是的,当然在内核中做所有事情,但是一些用户空间程序需要能够触发内核代码,对吗?!我要问的是我们如何公开该内核用户空间接口。字符设备使用 file_operations (open/close/read/write/ioctl)。我看到的 i2c 客户端示例使用 sysfs,我不确定这是否是唯一“正确”的方式。

标签: linux linux-device-driver i2c


【解决方案1】:

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

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

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

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

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

构建内核时,请在make menuconfig 中选择您的设备,以便使用内核构建它。有了这个,一旦你启动内核,设备将出现在/sys/class/hwmon/hwmon*下,然后用户空间可以通过sysfs接口从设备上@9​​87654334@和read。 在此处查看一个很好的示例:http://lxr.free-electrons.com/source/drivers/hwmon/tmp421.c。或 hwmon 目录中的任何设备。

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

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

【讨论】:

  • 常用的时钟框架(驱动clk)是时钟和PLL的框架。但是,我不确定您希望从用户空间使用 PLL 做什么,所以这可能不太合适。
  • @AlexandreBelloni。谢谢你的建议。在阅读kernel.org/doc/Documentation/clk.txt 之后,我认为它不适合我正在尝试做的事情。感谢您的帮助。
  • IIO框架中有一些时钟芯片驱动。 clk 由为部分 SoC 提供时钟的驱动程序使用。其他内核驱动程序使用它来启用/禁用它们驱动的时钟。 IIO clockgen 允许用户空间控制,而不是为其他驱动程序提供时钟。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-24
  • 2016-05-28
  • 1970-01-01
  • 2013-03-16
  • 2017-12-13
  • 2017-05-04
  • 1970-01-01
相关资源
最近更新 更多