草庐IT

c - 如何编写多个从属 i2c 客户端设备驱动程序?

coder 2023-06-18 原文

我正在尝试为嵌入式板开发驱动程序。驱动程序应该为 v4l2 打开一个接口(interface)并使用 i2c 与 2 个设备通信。司机将充当主人。

我似乎无法理解i2c_device_id 数组和i2c_add_driver 函数是如何工作的。我阅读了内核源代码中的文档,但它对我在多个从属客户端上没有帮助。

  • 我必须有两个独立的探测函数吗?
  • 我必须调用 i2c_add_driver 两次吗?
  • 如果不是,我将如何保存两个不同的客户端,以便能够将不同的字节发送到不同的地址。

我在这里粘贴我的代码。我尝试实例化了两个i2c_drivers,分别调用了两次i2c_driver_add并分别实现了i2c probe。第二次调用 i2c_add_driver 时,代码无法告诉我 foo1 已经注册。

我在我的 dts 文件中的 i2c1 下定义了两个 block ,例如:

&i2c1 {

...
    foo0: foo0@00 {
        compatible = "bar,foo0";
        reg = <0x00>;
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_ipu1_2>;
        clocks = <&clks IMX6QDL_CLK_CKO>;
        clock-names = "csi_mclk";
        DOVDD-supply = <&vgen4_reg>; /* 1.8v */
        AVDD-supply = <&vgen3_reg>;  /* 2.8v, on rev C board is VGEN3,
                        on rev B board is VGEN5 */
        DVDD-supply = <&vgen2_reg>;  /* 1.5v*/
        pwn-gpios = <&gpio1 16 1>;   /* active low: SD1_DAT0 */
        rst-gpios = <&gpio1 17 0>;   /* active high: SD1_DAT1 */
        csi_id = <0>;
        mclk = <24000000>;
        mclk_source = <0>;
    };

    foo1: foo1@02 {
        compatible = "bar, foo1";
        reg = <0x02>;
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_ipu1_2>;
        clocks = <&clks IMX6QDL_CLK_CKO>;
        clock-names = "csi_mclk";
        DOVDD-supply = <&vgen4_reg>; /* 1.8v */
        AVDD-supply = <&vgen3_reg>;  /* 2.8v, on rev C board is VGEN3,
                        on rev B board is VGEN5 */
        DVDD-supply = <&vgen2_reg>;  /* 1.5v*/
        pwn-gpios = <&gpio1 16 1>;   /* active low: SD1_DAT0 */
        rst-gpios = <&gpio1 17 0>;   /* active high: SD1_DAT1 */
        csi_id = <0>;
        mclk = <24000000>;
        mclk_source = <0>;
    };

...

两个 block 完全一样,除了它们的名字。

在驱动程序文件中,我实例化了以下结构:

static const struct i2c_device_id foo_id[] = {
    {"foo0", 0},
    {"foo1", 1},
    {},
};

static struct i2c_driver foo0_i2c_driver = {
    .driver = {
        .owner = THIS_MODULE,
        .name = "foo0",
    },
    .probe = foo0_probe,
    .remove = foo0_remove,
    .id_table = foo_id,
};

static struct i2c_driver foo1_i2c_driver = {
    .driver = {
        .owner = THIS_MODULE,
        .name = "foo1",
    },
    .probe = foo1_probe,
    .remove = foo1_remove,
    .id_table = foo_id,
};

下面是我的initexit 函数:

MODULE_DEVICE_TABLE(i2c, foo_id);

static __init int foo_init(void)
{
    u8 err;

    err = i2c_add_driver(&foo0_i2c_driver);
    if (err != 0)
        pr_err("%s:driver registration failed i2c-slave0, error=%d\n",
            __func__, err);

    err = i2c_add_driver(&foo1_i2c_driver);
    if (err != 0)
        pr_err("%s:driver registration failed i2c-slave1, error=%d\n",
            __func__, err);

    return err;
}
static void __exit foo_clean(void)
{
    if((&foo0_i2c_driver) != NULL && i2c0initialized)
    {
        i2c_del_driver(&foo0_i2c_driver);
        i2c0initialized = 0;
    }
    if((&foo1_i2c_driver) != NULL && i2c1initialized)
    {
        i2c_del_driver(&foo1_i2c_driver);
        i2c1initialized = 0;
    }
}

module_init(foo_init);
module_exit(foo_clean);

下面是我的 probe 函数。我为两个奴隶准备了两份副本。

static int foo_probe(struct i2c_client *client,
                          const struct i2c_device_id *device_id)
{
    struct pinctrl *pinctrls;
    struct device *dev = &client->dev;

    int ret = 0;

    pinctrls = devm_pinctrl_get_select_default(dev);
    if(IS_ERR(pinctrls))
    {
        dev_err(dev, "pinctrl setup failed\n");
        return PTR_ERR(pinctrls);
    }

    memset(&foo_data, 0, sizeof(foo_data));
    foo_data.sensor_clk = devm_clk_get(dev, "csi_mclk");
    if(IS_ERR(foo_data.sensor_clk))
    {
        dev_err(dev, "get mclk failed\n");
        return PTR_ERR(foo_data.sensor_clk);
    }

    ret = of_property_read_u32(dev->of_node, "mclk", &(foo_data.mclk));
    if(ret < 0)
    {
        dev_err(dev, "mclk frequency is invalid\n");
        return ret;
    }

    ret = of_property_read_u32(dev->of_node, "mclk_source",
                               (u32 *)&(foo_data.mclk_source));
    if(ret < 0)
    {
        dev_err(dev, "mclk source is invalid\n");
        return ret;
    }

    ret = of_property_read_u32(dev->of_node, "csi_id", &(foo_data.csi));
    if(ret < 0)
    {
        dev_err(dev, "csi_id invalid\n");
        return ret;
    }

    clk_prepare_enable(foo_data.sensor_clk);
    i2c_client0 = client;

    /* custom data structures are set here */   

    foo_reset();

    ret = foo_get_id();

    if(ret < 0 /* || ret != foo_ID */)
    {
        clk_disable_unprepare(foo_data.sensor_clk);
        pr_warning("foo is not found\n");
        return -ENODEV;
    }

    clk_disable_unprepare(foo_data.sensor_clk);
    foo_int_device.priv = &foo_data;
    ret = v4l2_int_device_register(&foo_int_device);

    pr_info("foo is found\n");
    i2c0initialized = 1;
    return ret;
}

最佳答案

这个答案晚了 5 个月,但希望它能帮助遇到同样问题(和我一样)但找不到合适答案的其他人。

简而言之解决方案就是用一个minor number来代表每个slave。您的驱动程序将在您存储的客户端列表中查找该次要编号以获得正确的 i2c_client。

长版 您的 I2C 驱动程序最终可能是这种独特设备的字符设备驱动程序。否则,框架(例如 hwmon)可能已经实现,并且框架已经完成了处理多个从站的工作,因此您不必担心。参见 http://lxr.free-electrons.com/source/drivers/hwmon/例如。

现在假设它是一个字符设备驱动程序,在您的驱动程序 __init 中,您需要分配与从设备一样多的次设备号:

alloc_chrdev_region(&dev, *MINOR_START*, *NUM_DEVICES*, name)    
/* for each minor or slave device, do cdev_init and cdev_add */

现在进入您的MODULE_DEVICE_TABLE。为每个从站输入条目,记住字符串必须与设备树兼容条目相匹配。第二个字段是一个数字,我们将把它用作唯一标识符和次要数字(这就是诀窍):

struct i2c_device_id foo_idtable[] = {
    { "foo_1", 0 },
    { "foo_2", 1 },
    { },
};
MODULE_DEVICE_TABLE(i2c, foo_idtable);

好的,您的 .probe 函数将为每个匹配的设备树条目调用。当调用 .probe 函数时,Linux 会传入它为您实例化的 i2c_client 指针。这是技巧的另一部分,有一个全局表来存储这些单独的 i2c_client 指针。全局表的索引是次要编号。 minor number就是同样传入的id->driver_data,也就是你之前在foo_idtable中分配的编号。

static int foo_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    /* Store the i2c_client pointer in a global table using minor number as index 
     * make sure to allocate the global table dynamically */
    that_table[id->driver_data] = client;
    /* The id->driver_data is the minor number! */
}

希望你现在 catch 了。 当你 insmod 你的 .ko 时,你想要制作多个节点,每个节点对应一个次要编号:

insmod foo.ko
make_dir /dev/foo
find_major foo
make_node /dev/foo/foo_0 c <major> 0
make_node /dev/foo/foo_1 c <major> 1

现在,当您的用户空间代码尝试使用您的驱动程序时,它将打开与正确的次要编号(从属设备)相对应的文件。在用户空间你会做这样的事情:

/* Accessing slave #0 */
int fd = open("/dev/foo/foo_0", O_RDWR);

/* Accessing slave #1 */
int fd_1 = open("/dev/foo/foo_1", O_RDWR);

您的 .open 实现将被调用。

static int foo_driver_open(struct inode *inode, struct file *filep)
{
    int minor_num = MINOR(inode->i_rdev);
    /* remember the global table we had before that was indexed using the minor number?
     * Let's get the i2c_client stored there and get filep->private_data
     * to point to that i2c_client. Then subsequent read/write/ioctl can just
     * obtain the i2c_client from filep->private_data */
    filep->private_data = that_table[minor_num];
}

例如,您的用户空间代码调用驱动程序的 ioctl:

ioctl(fd, FOO_IOCTL_DO_READ, &msg);
ioctl(fd_1, FOO_IOCTL_DO_WRITE, &msg);

在您的驱动程序的 ioctl 实现中:

long foo_driver_ioctl(struct file *filep, unsinged int cmd, unsigned long arg)
{
    /* the filep->private_data has the i2c_client pointer! yay! */
    struct i2c_client *client = filep->private_data;

    /* now you can talk to your slave device with the i2c_client knowing
     * it is the correct i2c_client */
}

就是这样:)。 我希望这是有道理的。这是一个很长的解释,但我希望我是彻底的,但不要太困惑。最大的问题是我们有一个存储 i2c_cient 指针的全局表,但我想不出没有它的方法,因为 .probe.open 有没有办法在彼此之间传递参数。如果有人有更好的解决方案,请告诉我。

关于c - 如何编写多个从属 i2c 客户端设备驱动程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39370398/

有关c - 如何编写多个从属 i2c 客户端设备驱动程序?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  6. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  7. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  8. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  9. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  10. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

随机推荐