【问题标题】:Kernel driver combining a platform_driver with a i2c_driver将 platform_driver 与 i2c_driver 组合在一起的内核驱动程序
【发布时间】:2021-03-29 01:21:20
【问题描述】:

我正在为 LCD 编写内核驱动程序。此 LCD 使用 8 条 GPIO 线 (d0...d7) 将数据发送到显示,一些 gpio 控制信号(开/关、启用背光和 r/w)和一个控制显示对比度的电位器,连接到 I2C 总线。

我编写了一个平台驱动程序,它使用“probe”和“remove”回调来注册/取消注册一个杂项设备,该设备创建一个 /dev/lcd 字符设备,可用于从用户空间发送一个要在屏幕上打印的缓冲区。我能够读取 DTS 上正确定义的 GPIOS,并管理这些 GPIOS 以在 LCD 上打印字符串。这是骨架:

#define MODULE_NAME     "lcd"
static void lcd_hw_setup(void)
{ ...  }
static int lcd_open(struct inode *inode, struct file *file)
{ ... }
static ssize_t lcd_write (struct file *file, const char *buf, size_t     count, loff_t *ppos)
{ ... }
static int lcd_close(struct inode *inode, struct file *file)
{ ... }
/* declare & initialize file_operations structure */
static const struct file_operations lcd_dev_fops = {
    .owner = THIS_MODULE,
    .open = lcd_open,
    .write = lcd_write,
    .release = lcd_close
};
/* declare & initialize miscdevice structure */
static struct miscdevice lcd_misc = {
    .minor = MISC_DYNAMIC_MINOR, /* major = 10 assigned by the misc framework */
    .name = MODULE_NAME, /* /dev/lcd */
    .fops = &lcd_dev_fops,
};
static int lcd_probe(struct platform_device *pdev)
{
    struct device *dev;
    pr_info(MODULE_NAME ": lcd_probe init\n");
    /* Register the misc device with the kernel */
    misc_register(&lcd_misc);
    dev = &pdev->dev;
    /* gpiod_get calls to get gpios from DTS */
    lcd_hw_setup();
    pr_info(MODULE_NAME ": lcd_probe ok\n");
    return 0;
}
static int lcd_remove(struct platform_device *pdev)
{
    pr_info(MODULE_NAME ": lcd_remove\n");
    /* Release gpio resources */
    ... 
    /* Unregister the device with the kernel */
    misc_deregister(&lcd_misc);
    return 0;
}
/* declare a list of devices supported by this driver */
static const struct of_device_id lcd_of_ids[] = {
    { .compatible = "my-lcd" },
    { /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, lcd_of_ids);
/* declare & initialize platform_driver structure */
static struct platform_driver lcd_pdrv = {
    .probe = lcd_probe,
    .remove = lcd_remove,
    .driver = {
        .name = "my-lcd", /* match with compatible */
        .of_match_table = lcd_of_ids,
        .owner = THIS_MODULE,
    },
};
/* register platform driver */
module_platform_driver(lcd_pdrv);

真的很好用。

现在我需要向 I2C 电位器发送一个初始化值来设置显示对比度。这需要调用 i2c_smbus_write_byte_data。为此,我需要访问 i2c_client 结构。

我发现了一些 I2C 示例,这些示例创建了一个 i2c_driver,它提供了探测和删除回调,并在探测函数中接收指向该 i2c_client 结构的指针。但是我找不到将 i2c_driver 与我的 platform_driver 关联起来的方法。他们似乎是完全独立的司机。

我的问题:

  • platform_driver 和 i2c_driver 可以组合在一个内核模块中吗?我的意思是,我可以在单个内核模块中添加 module_platform_driver 和 module_i2c_driver 调用吗?

  • 或者我必须创建第二个驱动程序来控制 I2C 电位器。在这种特殊情况下,两个内核模块之间存在依赖关系。应该如何管理这种依赖关系?

请对此提供一些帮助,这将非常有帮助。非常感谢!

【问题讨论】:

  • 首先,驱动很可能已经写好了(参见drivers/auxdisplay文件夹)。其次,在 Linux 中,具有对比度的部分通常是一个不同(或单独)驱动程序。
  • 我会检查指向的驱动程序是否符合我的需要。这就说得通了。您的反馈对于确认我的方法是正确的非常有用。非常感谢您的参与! :)

标签: linux kernel driver i2c platform


【解决方案1】:

作为初步工作,我开发了第二个驱动程序(i2c_driver),只是为了从探测回调中调用 i2c_smbus_write_byte_data 函数。它有效,但我想知道这是否是解决此问题的正确方法。谢谢!

【讨论】:

  • 是的,看我对你帖子的评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-17
  • 2021-12-04
  • 2020-07-10
  • 1970-01-01
  • 2013-02-23
  • 2022-06-25
相关资源
最近更新 更多