Does i2c driver need to be implemented just like a

2020-06-07 04:08发布

问题:

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:

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.