Does I2C Driver Need to Be Implemented Just Like Any Other Character Device Driver

Does i2c driver need to be implemented just like any other character device driver?

I think I understand the device driver model better now with @Alexandre Belloni's comment and reading this set of presentation slides: http://free-electrons.com/doc/training/linux-kernel/linux-kernel-slides.pdf. The relevant slides are from page 221 to 236.

There are 3 types of device drivers:

  1. Character
  2. Network
  3. Block

However, there are specific "frameworks" that exist as a subclass of character device drivers which implements the common parts of drivers for the same type of devices.

For example a temperature sensor on the motherboard used for hardware monitoring would be registered under the hwmon framework (https://www.kernel.org/doc/Documentation/hwmon/hwmon-kernel-api.txt). You would implement the i2c probe, read, write functionality but instead of shaping it into a character device with file_operations struct, you just need to register it as a hwmon device: hwmon_device_register_with_groups(). To expose to userspace you need to build up your /sys/class/hwmon/hwmon* directory using attributes with the list of exposed read/write commands you want (ex: read temp from channel 1, write to limit temp register).

When you build the kernel, select your device in make menuconfig in order for it to be built with the kernel. With this, once you bootup the kernel, the device will appear under /sys/class/hwmon/hwmon*, and userspace can then open and read from the device through the sysfs interface.
See a good example here: http://lxr.free-electrons.com/source/drivers/hwmon/tmp421.c. Or any device in the hwmon directory.

So that's where my confusion was. As @Alexandre Belloni pointed out, these devices are registered under a framework so the explicit character device driver code is not necessary. For me this is not the case, I don't think there is a suitable framework for the device I'm doing (clocking PLL). Thus I will need to go the general route and implement as a character device. This will also allow me to load/unload as a module rather than it automatically being loaded during kernel bootup.

Please feel free to correct any mistakes I made. I hope this is helpful for anyone else confused about writing i2c-clients.

When loading a platform driver for an I2C device does the device need to be present?

The device doesn't have to be pysically plugged in to call the probe function. Most of the implemented probe functions even check if the device is plugged in or not.
The warning with the tainted kernel can also be ignored, your driver still should be able to run.

I think you missed to add your driver to the device tree.

You can find a good explanaition here LWN.net

Without more informations it is hard for us to guess some other possible problems.

I2C device linux driver

Thank you, guys. I've found what I want a couple hours ago. So as I see there are three types of drivers. One is supposed to be inserted into the kernel, and you need to rebuild your kernel to use them. There are also drivers that you can use through the sysfs using device attributes (they appear in /sys/bus/i2c/driver/your_driver/). And, third - my type - drivers that look like character devices. Actually, you can combine them.

So, if you want to use last type of devices it'll be a bit difficult to find correct examples, because almost all examples are devoted to the first two types of drivers. Anyway, if you want to create a character device, you need to describe functions from the file_operations structure. But all functions like i2c_transfer, i2c_smbus_read_byte. Etc. (full list) required eitherstruct i2c_adapter or struct i2c_client. And there two questions, how to get there structures, and how to attach driver to the appropriate i2c bus, such as i2c-2?

So there is a function that is not described here: i2c_get_adapter. As a parameter, you need to pass the i2c bus number. It returns a link to a i2c_adapter structure. The i2c_client structure you can get with i2c_new_dummy function passing i2c_adapter and slave address to it as parameters.

After that you can use functions such as i2c_transfer, i2c_smbus_read_byte etc. In the end you can describe file_operations structure functions, and release driver, representing your i2c device, without using sysfs and rebuilding your kernel.

Result code looks like:

u8 ret; 
struct i2c_client * my_client;
struct i2c_adapter * my_adap = i2c_get_adapter(1); // 1 means i2c-1 bus
my_client = i2c_new_dummy (my_adap, 0x69); // 0x69 - slave address on i2c bus
i2c_smbus_write_byte(my_client, 0x0f);
ret = i2c_smbus_read_byte(my_client);

You can use this code directly in your functions for the file_operations structure.

Hope this info will be useful for beginners like me.

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 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.



Related Topics



Leave a reply



Submit