summaryrefslogtreecommitdiff
path: root/schulung_tools/drivers/hellodriver/hello.c
diff options
context:
space:
mode:
authorJohn Ogness <john.ogness@linutronix.de>2019-02-15 13:16:28 +0106
committerJohn Ogness <john.ogness@linutronix.de>2019-02-15 13:16:28 +0106
commitf19259fb36ffe74a11325c14ee2260a8e323b8ee (patch)
tree6cf02a4fcedfa1451c164f6c3a44a0781f41a571 /schulung_tools/drivers/hellodriver/hello.c
parent9fffe2a41d0aa2b47c589db2523707a9e14ab5a1 (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.c230
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");