1

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();
Sam Protsenko
  • 14,045
  • 4
  • 59
  • 75
Splaty
  • 604
  • 1
  • 8
  • 21
  • 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 Jan 05 '17 at 20:26
  • Sorry, I don't quite understand what you mean. I need to be able to initiate read/write to my driver from userspace. So I think I do need ioctls, I'm not aware of another way for userspace to trigger device driver action. Not sure what you mean by "communication is happened between I2C host and client through specificially designed API". Which specifically designed APIs are you referring to? – Splaty Jan 05 '17 at 20:31
  • Just use existing I2C_DEV driver. It's designed specifically to raw communication from user space to I2C *client*. – 0andriy Jan 05 '17 at 20:45
  • Cannot, I need to be able to handle concurrent access to same device. The device requires 2 i2c operations per read/write so a mutex is needed. I think it's unnecessarily complex (shared memory, shared process mutex), if not just plain wrong, to do concurrent access protection in userspace when it should be in kernel space. – Splaty Jan 05 '17 at 21:00
  • I'm just wondering how other people have triggered their i2c driver code from userspace? If none of them implemented the driver as a char device with ioctls, how can they initiate basic read/write access from userspace? – Splaty Jan 05 '17 at 21:01
  • I'm not going to repeat what I told you above. Just read documentation, google it, etc. – 0andriy Jan 05 '17 at 22:27
  • 1
    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: http://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 clients – Splaty Jan 06 '17 at 14:20
  • People are too lazy to check existing code: *drivers/i2c/i2c-dev.c* – 0andriy Jan 06 '17 at 18:17
  • 1
    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 Jan 06 '17 at 20:35
  • No, I see two options: a) use i2c-dev and do actual driver in user space, b) forget about user space and do everything in kernel. Character device is needed only for a) and it already had been implemented. (I think it will be last comment from me on the topic) – 0andriy Jan 06 '17 at 23:52
  • 2
    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 Jan 08 '17 at 09:56
  • @AlexandreBelloni, by frameworks do you mean like a hwmon device? They (lm-sensors) seem to use sysfs which I'm not familiar with. Not sure about the limitations on read/write/trigger device action from userspace to sysfs. The device I'm working with is not a hwmon device, it is a clocking device (PLL). I wanted to use character device because ioctls have much more flexibility I think. I can define and trigger any ioctl I want. Just wasn't sure if this is correct way to do it, or if there was a different standard way for i2c-clients. – Splaty Jan 08 '17 at 20:22
  • 1
    @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 Jan 08 '17 at 20:26

1 Answers1

3

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.

Splaty
  • 604
  • 1
  • 8
  • 21
  • 2
    The common clock framework (driver clk) is the framework for clocks and PLL. However, I'm not sure what you expect to be able to do with your PLL from userspace so this may not be a good fit. – Alexandre Belloni Jan 14 '17 at 03:10
  • @AlexandreBelloni. Thanks for the suggestion. After reading https://www.kernel.org/doc/Documentation/clk.txt, I don't think it's a good fit for what I'm trying to do. Appreciate the help. – Splaty Jan 15 '17 at 02:22
  • There are some clock chip drivers in the IIO framework. clk is used by drivers that provide clocks to parts of a SoC. It used by other kernel drivers to enable/disable a clock to what they drive. The IIO clockgen allows for userspace control and is not for providing clocks to other drivers. – TrentP Jan 22 '17 at 18:01