Search code examples
linux-kernellinux-device-driveri2cread-write

Am i using the hardware driver while using system call read/write?


i have connected a hardware to an embedded linux board on i2c lines. I can see the device at /dev/i2c-1

    filename = "/dev/i2c-1"
    filehandle = open(filename,O_RDWR);
    write(filehandle, <buffer to be written>, <number of Bytes>)

(similiarly for  read =   read(filehandle, <buffer to be read in an array>, <number of Bytes>)

Now my question here is am I using the Linux's i2c-drivers ( read/write) when I am invoking write system calls (and read like above using filehandle).

Also is this implementation independent of i2c module?I verified only after I do modprobe i2c_dev I can see my code running. Is modprobe i2c_dev loading the i2c module and forming the /dev/i2c-1 in the /dev directory since I have connected the i2c device to it.


Solution

  • User-space interface to I2C

    User-space interface to I2C subsystem is provided via /dev/i2c-* files and documented at Documentation/i2c/dev-interface. There are two ways to send I2C message:

    • send plain buffer via write(); you need to include linux/i2c-dev.h for this
    • send i2c_msg structure via ioctl() with I2C_RDWR request; you need to include linux/i2c.h for this

    See this question for examples.

    How /dev/i2c-1 is associated with I2C subsystem

    /dev/i2c-1 file is just an interface to I2C subsystem. You can send I2C message, receive I2C message and configure I2C using correspondingly write(), read() and ioctl() syscalls. Once you perform one of these operations over /dev/i2c-1 file, it's being passed through Virtual file system to I2C layer, where those operations are implemented. Actual callbacks for those operations are implemented in drivers/i2c/i2c-dev.c file, more specifically -- in i2cdev_fops structure.

    For example, when you perform open() syscall on /dev/i2c-1 file, the i2cdev_open() function is called in kernel, which creates i2c_client structure for further send/receive operations, and that structure is being assigned to file's private data field:

    /* This creates an anonymous i2c_client, which may later be
     * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
     *
     * This client is ** NEVER REGISTERED ** with the driver model
     * or I2C core code!!  It just holds private copies of addressing
     * information and maybe a PEC flag.
     */
    client = kzalloc(sizeof(*client), GFP_KERNEL);
    ...
    file->private_data = client;
    

    When you perform some operations on that /dev/i2c-1 file next, i2c_client structure will be extracted from file->private_data field, and corresponding function will be called for that structure.

    For write() syscall the i2cdev_write() function will be called, which leads to i2c_master_send() function call:

    struct i2c_client *client = file->private_data;
    ...
    ret = i2c_master_send(client, tmp, count);
    

    The same way read() leads to i2cdev_read(), which leads to i2c_master_recv(). And ioctl() leads to i2cdev_ioctl(), which just assigns corresponding flags to i2c_client structure.

    How /dev/i2c-1 is associated with hardware I2C driver

    Operations performed on /dev/i2c-* file lead eventually to execution of I2C hardware driver functions. Let's see one example of it. When we are doing write() syscall, the whole chain will be:

    write() -> i2cdev_write() -> i2c_master_send() -> i2c_transfer() -> __i2c_transfer() -> adap->algo->master_xfer(), where adap is i2c_adapter structure, which stores hardware specific data about I2C controller, such as callbacks of I2C hardware driver. That .master_xfer callback is implemented in I2C hardware driver. For example, for OMAP platforms it's implemented in drivers/i2c/busses/i2c-omap.c file, see omap_i2c_xfer() function.