简体   繁体   中英

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.

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/

Thanks for this implementation of misc device to solve this problem!

For anyone referencing this in the future, I ran into a segmentation fault when inserting this into the kernel. If it helps:

instead of: my = kmalloc(sizeof(*my), GFP_KERNEL);

I used: my = (struct my_struct *) kmalloc(sizeof(struct my_struct), GFP_KERNEL);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM