Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Device Specific Data Structure with Platform Driver and Character Device Interface

I'm struggling with understanding the linkup between a platform device driver with a character device interface and storing data in a device specific data structure.

I created a struct keeping track of data related to my device, and then add it to the devices struct at the probe function:

dev_set_drvdata(dev, data_struct);

I also keep a global copy of data_struct.

I register a misc device so that I can mmap() and access the device through ioctl() commands. If I want to access this device's data_struct, and currently I access it through the global copy. Is there another way through the inode or file pointers to access the data I stored in the devices struct?

I currently only allow one instance of the device, but I want to make sure I implement this correctly for future implementations where there might be multiple devices using the same driver.

like image 731
gavenant Avatar asked Sep 13 '16 07:09

gavenant


People also ask

What is a platform device driver?

Platform drivers are dedicated to devices not based on conventional buses. I2C devices or SPI devices are platform devices, but respectively rely on I2C or SPI buses not on the platform bus. Everything needs to be done manually with the platform driver.

What is a character device driver?

Character device drivers normally perform I/O in a byte stream. Examples of devices using character drivers include tape drives and serial ports. Character device drivers can also provide additional interfaces not present in block drivers, such as I/O control (ioctl) commands, memory mapping, and device polling.

What is difference between platform driver and device driver?

Platform driver is for those devices that are on chip. Normal device driver are for those that are interfaced to the processor chip.

What is an example of a character device?

Examples for Character Devices: serial ports, parallel ports, sounds cards. Examples for Block Devices: hard disks, USB cameras, Disk-On-Key. For the user, the type of the Device (block or character) does not matter - you just care that this is a hard disk partition or a sound card.

What is a character-type device?

Next, we'll refer to character devices as drivers. In the kernel, a character-type device is represented by struct cdev, a structure used to register it in the system. Most driver operations use three important structures: struct file_operations, struct file and struct inode.

What is the implementation of a character device driver?

Consequently, implementation of a character device driver means implementing the system calls specific to files: open , close, read, write, lseek, mmap, etc. These operations are described in the fields of the struct file_operations structure:

What is the use of device-specific method in device drivers?

This method provides for individual, device-specific data and control functions that can be called by a device driver without conflicting with other such device-specific methods.

What are the early platform interfaces?

The early platform interfaces provide platform data to platform device drivers early on during the system boot. The code is built on top of the early_param () command line parsing and can be executed very early on. 1. Registering early platform device data ¶


1 Answers

When your miscdevice is being open for the first time, miscdevice framework will set file->private_data to your struct miscdevice (see misc_open() function and also comment to misc_register() function). You can rely on this and use file->private_data in your file operations to obtain your custom structure, using container_of() macro. Of course, your custom structure must contain your struct miscdevice for that. The neat and commonly used way to do so is to create helper function called to_*(), which will figure out and return your custom struct by file pointer provided. So if you called your custom struct my_struct, you should call that helper function as to_my_struct().

Also, if you are writing platform driver, you can use platform_set_drvdata() instead of dev_set_drvdata(). This is needed so you can obtain your custom structure in remove() function of your platform driver.

Here is an example for everything explained above:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>

struct my_struct {
    struct platform_device *pdev;
    struct miscdevice mdev;
};

static inline struct my_struct *to_my_struct(struct file *file)
{
    struct miscdevice *miscdev = file->private_data;

    return container_of(miscdev, struct my_struct, mdev);
}

static ssize_t my_read(struct file *file, char __user *buf, size_t count,
                       loff_t *pos)
{
    struct my_struct *my = to_my_struct(file); /* just for example */

    (void)my; /* unused */
    return simple_read_from_buffer(buf, count, pos, "my text", 7);
}

static const struct file_operations my_fops = {
    .owner  = THIS_MODULE,
    .read   = my_read,
};

static int my_probe(struct platform_device *pdev)
{
    struct my_struct *my;
    int ret;

    my = kmalloc(sizeof(*my), GFP_KERNEL);
    if (!my)
        return -ENOMEM;

    platform_set_drvdata(pdev, my);
    my->pdev = pdev;

    my->mdev.minor  = MISC_DYNAMIC_MINOR;
    my->mdev.name   = "my";
    my->mdev.fops   = &my_fops;
    my->mdev.parent = NULL;

    ret = misc_register(&my->mdev);
    if (ret) {
        dev_err(&pdev->dev, "Failed to register miscdev\n");
        return ret;
    }

    dev_info(&pdev->dev, "Registered\n");

    return 0;
}

static int my_remove(struct platform_device *pdev)
{
    struct my_struct *my = platform_get_drvdata(pdev);

    misc_deregister(&my->mdev);
    kfree(my);
    dev_info(&pdev->dev, "Unregistered\n");

    return 0;
}

static struct platform_driver my_driver = {
    .probe      = my_probe,
    .remove     = my_remove,
    .driver = {
        .name = "my",
    },
};

module_platform_driver(my_driver);

MODULE_AUTHOR("Sam Protsenko");
MODULE_DESCRIPTION("Platform device driver using char device example");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:my");

By the way, you can look for examples in kernel code, just by using keywords, like this:

$ git grep -l --all-match -e 'misc_register(' -e 'platform_device' -e 'file->private_data' -- drivers/
like image 143
Sam Protsenko Avatar answered Sep 19 '22 22:09

Sam Protsenko