1
votes

I'm a noob to Linux device drivers so please bear with me. I'm trying to implement a i2c driver (client). It's at the point where I can insmod, .probe gets called (because of device-tree entries) and in the .probe I can read/write to the device. Great.

However I need to be able to initiate read/writes from userspace to the driver. In order to do this, should an i2c driver be shaped like any other char device driver? Meaning having a file_operations struct so userspace can open, close, read, write, and ioctls?

I'm asking because in all the i2c client examples I've seen, nobody has implemented these things I've mentioned. I'm wondering how the heck they initiated calls from userspace without the file_operations struct set up. Maybe it was so obvious that nobody mentioned it, I don't know... I'm wondering if it's because i2c is referred to as a platform device driver it doesn't need this? If someone can confirm that would help me second guessing myself.

If you understand what I'm asking please ignore the rest. If you are confused about my question here is a more concrete explanation of what I'm asking:

What I have right now:

static int device_probe(struct i2c_client           *client,
                        const struct i2c_device_id  *id)
{
    struct device      *dev = &client->dev;
    struct device_data *data;

    /* Check the functionality of the i2c-adapter for smbus byte read/write */
    if (!i2c_check_functionality(client->adapter,
                                 I2C_FUNC_SMBUS_BYTE_DATA))
    {
        printk(KERN_ALERT "%s %d: device required i2c functionality is not supported\n", __func__, __LINE__);
        return -ENODEV;
    }

    /* Allocate memory to hold the device data
     * Using devm_kzalloc so do not have to worry about kfree */
    data = devm_kzalloc(dev, sizeof(struct device_data), GFP_KERNEL);
    if (dev == NULL)
    {
        printk(KERN_ALERT "%s %d: no memory\n", __func__, __LINE__);
        return -ENOMEM;
    }

    /* Record the pointer to current client */
    data->device_i2c_client = client;

    /* Set the client's data field to point to device-specific data struct */
    i2c_set_clientdata(client, data);

    /* Add the device-specific data struct to our collection of device client devices */
    device_data_tbl[id->driver_data] = data;

    /* Do a read, test the access */
    device_read();

    return 0;
}

static int device_remove(struct i2c_client *client)
{
    return 0;
}


int device_read(uint8_t             device_device_id,
                uint16_t const      dev_reg_addr,
                uint8_t *const      read_val)
{
    /* some read access */
}

static struct i2c_device_id device_idtable[] = {
    { "si5342",   0 },
    { },
};
MODULE_DEVICE_TABLE(i2c, device_idtable);

static struct i2c_driver device_driver = {
  .driver = {
    .name = device_DRIVER_NAME,
        .owner  = THIS_MODULE
  },
  .id_table = device_idtable,
    .probe      = device_probe,
    .remove     = device_remove,
};

static int __init device_driver_init(void)
{
    return i2c_add_driver(&device_driver);
}
module_init(device_driver_init);


static void __exit device_driver_exit(void)
{
    return i2c_del_driver(&device_driver);
}
module_exit(device_driver_exit);

Wondering if the following elements needs to be added in

static struct file_operations oxdrv_fops =
{
    .owner   = THIS_MODULE,
    .release = device_release,
    .open    = device_open,
    .unlocked_ioctl = device_ioctl
};

/* Associated function definitions: device_open, device_ioctl, etc */

alloc_chrdev_region();
cdev_init();
1
Yes, it's so obvious. The driver doesn't need this because communication is happened between I2C host and client through specifically designed API. Only one driver implements such for user space communication to the slaves directly, and apparently you do not need that.0andriy
Repeat what? I'm not trying to be argumentative but I honestly have no idea about the solution you are trying to convey. Anyways this whole Linux i2c client programming seems like a crapshoot. Documentation describes parts, need a full solution. I'm going to trust my instinct and just implement it as char driver like the answer posted here: stackoverflow.com/questions/23979129/i2c-device-linux-driver. You'll notice that person also answered their own question, much like every other question related to i2c clientsSplaty
Ok sorry for seeming to be lazy. So to summarize you are saying: yes should implement as char driver for userspace -> i2c-client driver, and can model from drivers/i2c/i2c-dev.c. My implementation of i2c driver as a char driver is working now with ioctls and procfs.Splaty
The drivers are not registering the character device themselves because a framework is doing that for them. choose the correct framework for your device and register your driver there.Alexandre Belloni
@0andriy, I know you said you won't respond but just to make it clear. a) i2c-dev in userspace, this is my current implementation but it's not concurrent safe. I'd have to have shared process mutex, doesn't seem right. b) Yes of course do everything in kernel but some userspace program needs to be able to trigger the kernel code right?! How can we expose that kernel-userspace interface is what I'm asking. Character devices uses file_operations (open/close/read/write/ioctl). The examples I've seen for i2c-clients use sysfs, I'm not sure if that's the only "correct" way.Splaty

1 Answers

2
votes

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.