【发布时间】:2016-09-07 12:49:06
【问题描述】:
我正在尝试为嵌入式板开发驱动程序。驱动程序应该为 v4l2 打开一个接口,并使用 i2c 与 2 个设备进行通信。司机将充当主人。
我似乎无法理解 i2c_device_id 数组和 i2c_add_driver 函数的工作原理。我阅读了内核源代码中的文档,但它在多个从客户端上对我没有帮助。
- 我必须有两个单独的探测功能吗?
- 我必须拨打
i2c_add_driver两次吗? - 如果不是,我将如何保存两个不同的客户端,以便能够将不同的字节发送到不同的地址。
我在这里粘贴我的代码。我尝试实例化两个i2c_drivers,两次调用i2c_driver_add,分别实现i2c probe。当第二次调用i2c_add_driver 时,代码无法告诉我foo1 已经注册。
我在我的 dts 文件中的 i2c1 下定义了两个块,例如:
&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>;
};
...
除了名称之外,两个块完全相同。
在驱动文件中我实例化了以下结构:
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,
};
下面是我的init 和exit 函数:
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;
}
【问题讨论】:
-
我认为您混淆了“驱动程序”和“设备”。您注册了 1 个驱动程序(每个设备类型),它将处理多个设备,具体取决于它们的 I2C 从地址。当然,必须编写驱动程序以支持 >1 设备,但任何体面的驱动程序都应该。
-
@domen 是的,我可以理解,但是寄存器被硬编码到设备树中。所以实例化一个只允许我读取/写入一个奴隶。
-
我不认为你这样做。您只需要一个具有一种探测功能的驱动程序。然后,设备驱动程序系统将为每个匹配的 DT 定义调用该探测器。我手头没有很好的参考资料,抱歉,你检查过 ldd3 吗? kernel.org/doc/Documentation/driver-model/platform.txt 也包含一些信息。
-
您可以在设备树中将一个 I2C 设备作为“主要”设备来实例化驱动程序,然后将第二个设备的信息(i2c 总线、从地址、引脚)作为子节点放入主设备的节点并在主设备探测功能中读取这些信息。基本上,您将辅助设备的 i2c 信息作为附加信息添加到主设备的设备树节点。
-
我猜您正在尝试为 imx6 进行双摄像头设置。通常就像您只有 2 个硬件设备和两个驱动程序实例,它们将两个 v4l2 设备导出到用户空间,就像您将两个 USB 网络摄像头连接到计算机一样。但是imx6 v4l2“捕获”驱动程序静态分配驱动程序数据的方式使得不可能被实例化两次。因此,IMO 试图在一个驱动程序实例中管理两个摄像头时,您走错了路,而是应该修复驱动程序,使其能够被实例化两次,并让每个摄像头都有自己的驱动程序实例,让用户空间决定如何使用它们。