How to write multiple slave i2c client device driver? How to write multiple slave i2c client device driver? linux linux

How to write multiple slave i2c client device driver?


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 versionYour 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.komake_dir /dev/foofind_major foomake_node /dev/foo/foo_0 c <major> 0make_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.


This is a workaround I am using right now, I will not accept this one as answer as this method doesn't feel right.

struct i2c_client *foo_i2c_client;static int foo_probe(struct i2c_client *client,                          const struct i2c_device_id *device_id){    foo_i2c_client = client;    // implementation}static int foo_remove(struct i2c_client *client){    // implementation}static const struct i2c_device_id foo_id[] = {    {"foo0", 0},    {},};static struct i2c_driver foo0_i2c_driver = {    .driver = {        .owner = THIS_MODULE,        .name = "foo0",    },    .probe = foo0_probe,    .remove = foo0_remove,    .id_table = foo_id,};static int send_cmd(u8 slv_addr, u8 *buffer, u16 size){    foo_i2c_client->address = slv_addr;    i2c_master_send(foo_i2c_client, buffer, size);    // rest of implementation} MODULE_DEVICE_TABLE(i2c, foo_id);module_i2c_driver(foo0_i2c_driver);