如何编写多个从i2c客户端设备驱动程序? [英] How to write multiple slave i2c client device driver?

查看:148
本文介绍了如何编写多个从i2c客户端设备驱动程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为嵌入式板开发驱动程序.该驱动程序应该打开 v4l2 的界面,并使用 i2c 与2个设备进行通信.驾驶员将充当主人.

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

  • 我必须具有两个单独的探针功能吗?
  • 我必须打两次i2c_add_driver吗?
  • 如果不是这样,我将如何保存两个不同的客户端,以便能够将不同的字节发送到不同的地址.

我将代码粘贴到这里.我试图实例化两个名为i2c_driver_addi2c_drivers两次,并分别实现了i2c探针.该代码无法正常运行,告诉我第二次调用i2c_add_driverfoo1已被注册.

我在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,
};

以下是我的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个月,但希望它将对遇到相同问题(与我一样)并且找不到合适答案的其他人有所帮助. /p>

简而言之,解决方案是使用次要数字表示每个从站.您的驱动程序将在您存储的客户列表中查找该次要编号,以获得适当的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指针.全局表的索引是次设备号.次要编号是同时传入的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! */
}

希望您现在正在追赶. 当您对.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无法相互之间传递参数.如果有人有更好的解决方案,请告诉我.

I am trying to develop a driver for an embedded board. The driver is supposed to open up an interface for v4l2 and communicate with 2 devices using i2c. the driver will act as a master.

I can't seem to understand how i2c_device_id array and i2c_add_driver functions work. I read documentation in kernel source but it won't help me on multiple slave clients.

  • Do I have to have two seperate probe functions?
  • Do i have to call i2c_add_driver two times?
  • If not how am I going to be able to save two different clients to be able to send different bytes to different addresses.

I am pasting my code here. I tried to instantiate two i2c_drivers, called i2c_driver_add two times and implemented i2c probe seperately. The code doesn't work telling me that foo1 is already registered when it calls i2c_add_driver for the second time.

I defined two blocks under i2c1 in my dts file like:

&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>;
    };

...

Two blocks are exactly the same except their names.

In the driver file I instantiated following structs:

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,
};

Below are my init and exit functions:

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);

Below is my probe function. I have two copies for it for both slaves.

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;
}

解决方案

This answer is late by 5 months but hopefully it will help someone else who had the same issue (as I did) and could not find a suitable answer.

In short the solution is to use a minor number to represent each slave. Your driver will look up that minor number in your stored list of clients to obtain the proper i2c_client.

Long version Your I2C driver will eventually probably be a character device driver for such an unique device. Otherwise a framework (such as hwmon) may already be implemented and handling multiple slaves is already done by the framework so you need not worry about it. See http://lxr.free-electrons.com/source/drivers/hwmon/ for examples.

Now assuming it is a character device driver, in your driver __init you need to allocate as many minor numbers as slave devices:

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

Now onto your MODULE_DEVICE_TABLE. Enter the entries for each slave remembering that the string must match the device tree compatible entry. The 2nd field is a number, we will use it as both an unique identifier and a minor number (this is the trick):

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

Ok with that in place, your .probe function will be called for each matching device-tree entry. When the .probe function is called, Linux passes in the i2c_client pointer it has instantiated for you. Here's the other part of the trick, have a global table that stores these individual i2c_client pointers. The index of the global table is the minor number. The minor number is the id->driver_data that is also passed in, which was the number you assigned in the foo_idtable before.

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! */
}

Hopefully you are catching on now. When you insmod your .ko, you want to make multiple nodes, one for each minor number:

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

Now when your userspace code tries to use your driver, it will open the file that corresponds to the correct minor number (slave device). In userspace you would do something like:

/* 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);

Your .open implementation will get called.

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];
}

Then for example your userspace code makes a call to the driver's ioctl:

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

In your driver's ioctl implementation:

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 */
}

That's it :). I hope that makes sense. It's a long explanation but I hope I was thorough but not too confusing. The biggest problem is that we have a global table that stores the i2c_cient pointers, but I can't think of a way not to have it since .probe and .open have no way to pass parameters between each other. If anyone has a better solution let me know.

这篇关于如何编写多个从i2c客户端设备驱动程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆