c - How to write multiple slave i2c client device driver? -


i trying develop driver embedded board. driver supposed open interface v4l2 , communicate 2 devices using i2c. driver act master.

i can't seem understand how i2c_device_id array , i2c_add_driver functions work. read documentation in kernel source won't me on multiple slave clients.

  • do have have 2 seperate probe functions?
  • do have call i2c_add_driver 2 times?
  • if not how going able save 2 different clients able send different bytes different addresses.

i pasting code here. tried instantiate 2 i2c_drivers, called i2c_driver_add 2 times , implemented i2c probe seperately. code doesn't work telling me foo1 registered when calls i2c_add_driver second time.

i defined 2 blocks under i2c1 in 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 vgen3,                         on rev b board 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 vgen3,                         on rev b board 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 same except names.

in driver file 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 init , 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 probe function. have 2 copies 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 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 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 set here */         foo_reset();      ret = foo_get_id();      if(ret < 0 /* || ret != foo_id */)     {         clk_disable_unprepare(foo_data.sensor_clk);         pr_warning("foo 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 found\n");     i2c0initialized = 1;     return ret; } 

this answer late 5 months else had same issue (as did) , not find suitable answer.

in short solution use minor number represent each slave. driver minor number in stored list of clients obtain proper i2c_client.

long version i2c driver character device driver such unique device. otherwise framework (such hwmon) may implemented , handling multiple slaves done framework need not worry it. see http://lxr.free-electrons.com/source/drivers/hwmon/ examples.

now assuming character device driver, in driver __init need allocate many minor numbers slave devices:

alloc_chrdev_region(&dev, *minor_start*, *num_devices*, name)     /* each minor or slave device, cdev_init , cdev_add */ 

now onto module_device_table. enter entries each slave remembering string must match device tree compatible entry. 2nd field number, use both unique identifier , minor number (this trick):

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

ok in place, .probe function called each matching device-tree entry. when .probe function called, linux passes in i2c_client pointer has instantiated you. here's other part of trick, have global table stores these individual i2c_client pointers. index of global table minor number. minor number id->driver_data passed in, number assigned in foo_idtable before.

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

hopefully catching on now. when insmod .ko, want make multiple nodes, 1 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 userspace code tries use driver, open file corresponds correct minor number (slave device). in userspace 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 called.

static int foo_driver_open(struct inode *inode, struct file *filep) {     int minor_num = minor(inode->i_rdev);     /* remember global table had before indexed using minor number?      * let's i2c_client stored there , filep->private_data      * point i2c_client. subsequent read/write/ioctl can      * obtain i2c_client filep->private_data */     filep->private_data = that_table[minor_num]; } 

then example userspace code makes call driver's ioctl:

ioctl(fd, foo_ioctl_do_read, &msg); ioctl(fd_1, foo_ioctl_do_write, &msg); 

in driver's ioctl implementation:

long foo_driver_ioctl(struct file *filep, unsinged int cmd, unsigned long arg) {     /* filep->private_data has i2c_client pointer! yay! */     struct i2c_client *client = filep->private_data;      /* can talk slave device i2c_client knowing      * correct i2c_client */ } 

that's :). hope makes sense. it's long explanation hope thorough not confusing. biggest problem have global table stores i2c_cient pointers, can't think of way not have since .probe , .open have no way pass parameters between each other. if has better solution let me know.


Comments

Popular posts from this blog

serialization - Convert Any type in scala to Array[Byte] and back -

matplotlib support failed in PyCharm on OSX -

python - Matplotlib: TypeError: 'AxesSubplot' object is not callable -