diff options
| author | John Ogness <john.ogness@linutronix.de> | 2019-02-15 13:16:28 +0106 |
|---|---|---|
| committer | John Ogness <john.ogness@linutronix.de> | 2019-02-15 13:16:28 +0106 |
| commit | f19259fb36ffe74a11325c14ee2260a8e323b8ee (patch) | |
| tree | 6cf02a4fcedfa1451c164f6c3a44a0781f41a571 /schulung_tools/drivers/hellodriver/hello.c | |
| parent | 9fffe2a41d0aa2b47c589db2523707a9e14ab5a1 (diff) | |
schulung_tools: drivers: remove modules subdir
There is no need for the extra directory level. Move all the
example modules to the parent "drivers" directory and delete
the modules directory.
Signed-off-by: John Ogness <john.ogness@linutronix.de>
Diffstat (limited to 'schulung_tools/drivers/hellodriver/hello.c')
| -rw-r--r-- | schulung_tools/drivers/hellodriver/hello.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/schulung_tools/drivers/hellodriver/hello.c b/schulung_tools/drivers/hellodriver/hello.c new file mode 100644 index 0000000..7f78d06 --- /dev/null +++ b/schulung_tools/drivers/hellodriver/hello.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/init.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/cdev.h> +#include <linux/of.h> + +struct hello_dev { + struct device *dev; + struct cdev cdev; + unsigned int minor; +}; + +#define HELLO_MAX_DEVICES 10 + +static struct class *hello_class; +static dev_t hello_devt; + +static int hello_open(struct inode *node, struct file *f) +{ + struct cdev *cd = node->i_cdev; + struct hello_dev *hello = container_of(cd, struct hello_dev, cdev); + dev_info(hello->dev, "%s\n", __func__); + f->private_data = hello; + return 0; +} + +static int hello_release(struct inode *node, struct file *f) +{ + struct hello_dev *hello = f->private_data; + dev_info(hello->dev, "%s\n", __func__); + f->private_data = NULL; + return 0; +} + +static ssize_t hello_read(struct file *f, char __user *u, size_t s, loff_t *l) +{ + struct hello_dev *hello = f->private_data; + char buf[10]; + size_t len; + + dev_info(hello->dev, "%s: size=%zu offset=%llu\n", __func__, s, *l); + + if (*l >= 40) + return 0; + + len = snprintf(buf, sizeof(buf), "hello:%d\n", hello->minor); + + if (s < len) + len = s; + + if (copy_to_user(u, buf, len) != 0) + return -EFAULT; + + *l += len; + + return len; +} + +static ssize_t hello_write(struct file *f, const char __user *u, size_t s, + loff_t *l) +{ + struct hello_dev *hello = f->private_data; + dev_info(hello->dev, "%s: size=%zu offset=%llu\n", __func__, s, *l); + return s; +} + +static const struct file_operations hello_fops = { + .owner = THIS_MODULE, + .open = hello_open, + .release = hello_release, + .read = hello_read, + .write = hello_write, +}; + +static int hello_probe(struct platform_device *pdev) +{ + struct hello_dev *hello; + struct device *dev; + dev_t devt; + int ret; + + hello = devm_kzalloc(&pdev->dev, sizeof(*hello), GFP_KERNEL); + if (!hello) { + dev_err(&pdev->dev, "devm_kzalloc failed\n"); + return -ENOMEM; + } + +#ifdef CONFIG_OF + ret = of_property_read_u32(pdev->dev.of_node, "index", &hello->minor); + if (ret < 0) { + dev_err(&pdev->dev, "no index specified\n"); + goto err_out; + } +#else + hello->minor = (unsigned)pdev->id; +#endif + + if (hello->minor >= HELLO_MAX_DEVICES) { + dev_err(&pdev->dev, "invalid index: %u\n", hello->minor); + ret = -EINVAL; + goto err_out; + } + + hello->dev = &pdev->dev; + platform_set_drvdata(pdev, hello); + + devt = MKDEV(MAJOR(hello_devt), hello->minor); + + cdev_init(&hello->cdev, &hello_fops); + hello->cdev.owner = THIS_MODULE; + ret = cdev_add(&hello->cdev, devt, 1); + if (ret != 0) { + dev_err(&pdev->dev, "cdev_add failed\n"); + goto err_drvdata; + } + + dev = device_create(hello_class, &pdev->dev, devt, hello, + "hello%d", hello->minor); + if (IS_ERR(dev)) { + dev_err(&pdev->dev, "device_create failed\n"); + ret = PTR_ERR(dev); + goto err_cdev; + } + + dev_info(&pdev->dev, "HELLO! I am hello device %d!\n", hello->minor); + + return 0; + +err_cdev: + cdev_del(&hello->cdev); +err_drvdata: + platform_set_drvdata(pdev, NULL); +err_out: + return ret; +} + +static int hello_remove(struct platform_device *pdev) +{ + struct hello_dev *hello = platform_get_drvdata(pdev); + + device_destroy(hello_class, MKDEV(MAJOR(hello_devt), hello->minor)); + cdev_del(&hello->cdev); + platform_set_drvdata(pdev, NULL); + + dev_info(&pdev->dev, "GOODBYE! I was hello device %d!\n", hello->minor); + + return 0; +} + +static const struct of_device_id hello_match[] = { + { .compatible = "virtual,hello", }, + { /* end of table */ } +}; + +static struct platform_driver hello_driver = { + .driver = { + .name = "hello", + .of_match_table = hello_match, + }, + .probe = hello_probe, + .remove = hello_remove, +}; + +#ifndef CONFIG_OF +static struct platform_device *pdevs[3]; +#endif + +static int __init hello_init(void) +{ + int ret; + + printk(KERN_INFO "%s\n", __func__); + + ret = alloc_chrdev_region(&hello_devt, 0, HELLO_MAX_DEVICES, "hello"); + if (ret < 0) + return ret; + + hello_class = class_create(THIS_MODULE, "hello"); + if (IS_ERR(hello_class)) { + printk(KERN_ERR "%s: failed to create class\n", __func__); + ret = PTR_ERR(hello_class); + goto err_region; + } + + ret = platform_driver_register(&hello_driver); + if (ret != 0) + goto err_class; + +#ifndef CONFIG_OF + pdevs[0] = platform_device_register_simple("hello", 1, NULL, 0); + pdevs[1] = platform_device_register_simple("hello", 3, NULL, 0); + pdevs[2] = platform_device_register_simple("hello", 5, NULL, 0); +#endif + + return 0; + +err_class: + class_destroy(hello_class); +err_region: + unregister_chrdev_region(hello_devt, HELLO_MAX_DEVICES); + return ret; +} + +static void __exit hello_exit(void) +{ + printk(KERN_INFO "%s\n", __func__); + +#ifndef CONFIG_OF + platform_device_unregister(pdevs[0]); + platform_device_unregister(pdevs[1]); + platform_device_unregister(pdevs[2]); +#endif + + platform_driver_unregister(&hello_driver); + class_destroy(hello_class); + unregister_chrdev_region(hello_devt, HELLO_MAX_DEVICES); +} + +module_init(hello_init); +module_exit(hello_exit); + +MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>"); +MODULE_DESCRIPTION("a great module for hello-ing!"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("20190101"); |
