Search code examples
linuxlinux-kernellinux-device-driverkernel-modulei2c

Handling multiple i2c_clients in one driver module (with sysfs)


I want to create a kernel driver for tmp102 temperature sensors. In one project I have only 1 sensor, in the other - 2 sensors. I want my kernel module to be able to support N sensors, not a fixed number. I have a problem with managing more than 1 struct i2c_client and creating sysfs entries for each of them. Here's how I'm doing it:

  1. In the probe() function I get struct i2c_client* for each of the devices that I provide I2C_BOARD_INFO() for.

  2. I then kobject_create_and_add("tmp102", kernel_kobj) to get the main directory for the modules in sysfs.

  3. For each device I'm creating sysfs_create_group() which gets the pointer to statically created attributes. The attributes have the (*show)() and (*store)() pointers set to static functions, e.g.

    static ssize_t tmp102_sysfs_thigh_get_one(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
    

In this function I want to read via I2C. The problem is... I don't know how to get the struct i2c_client * for the device which should be somehow linked to this sysfs entry, and the I2C address is there!

How should I properly "link" i2c_client with sysfs entries, so that I can get the I2C address inside the functions?

Ideally, I would like to have just one set of functions (one for each of the attributes). The sysfs tree should look like that:

  /sysfs/kernel/tmp102
  |
  |-> <hex i2c address, e.g. /48>
  |   |
  |   |-> temperature   
  |
  |   
  |-> /49
      |
      |-> temperature

I want both 'temperature' attributes to use the same function, which is able to determine the I2C address, to which it should write.

Or maybe my architecture is plain wrong? If so, how should it look like for a driver that needs to handle multiple i2c_clients?

EDIT:

I decided to use struct device_attribute instead of regular attributes. To my understanding, it is not easy to get the struct device pointer when using the regular attributes, same with struct i2c_client. They are not easily "linked" with kobjects from /sys/kernel, where I need to have my attributes for this project. device_attributes can be found in /sys/devices - I used sysfs_create_group and linked my device's kobject with the device_attribute group. I used sysfs_create_link, and linked my device's kobject with the /sys/kernel/tmp102. This way I could create a folder (link) for each of the devices, which points to the original attributes folder in /sys/devices.


Solution

  • First, you should know that there is already a kernel driver for the tmp102 that has a sysfs interface. Have a look at drivers/hwmon/tmp102.c.

    Now, for your issue, you have a struct kobject that is passed to you sysfs callback. You can call kobj_to_dev() to get a pointer to the device. Then, for example, you could use dev_get_drvdata() to get a pointer to your own private structure that would contain a pointer to the i2c client. Don't forget to set it first with dev_set_drvdata() in your probe.

    You have can find an example in drivers/rtc/rtc-ds1343.c but it is using an spi_driver.