diff options
| author | John Ogness <john.ogness@linutronix.de> | 2017-12-19 10:53:07 +0100 |
|---|---|---|
| committer | John Ogness <john.ogness@linutronix.de> | 2017-12-19 10:53:07 +0100 |
| commit | 27209bb802048f4803d9cd9a5c2f99d613986446 (patch) | |
| tree | 54f36042b79f63ce0a19be18e82a7d189a41686b | |
| parent | 0f172d0b022f1a2ec3d7465eb7272828ee083f6e (diff) | |
import drivers from devel/jogness
Simple drivers for use in the Linux Advanced schulung were only
available in the devel/jogness branch. Push them to master so
they are easily accessible to any trainer.
Signed-off-by: John Ogness <john.ogness@linutronix.de>
7 files changed, 613 insertions, 0 deletions
diff --git a/schulung_tools/drivers/modules/hellodriver/Makefile b/schulung_tools/drivers/modules/hellodriver/Makefile new file mode 100644 index 0000000..a0878d9 --- /dev/null +++ b/schulung_tools/drivers/modules/hellodriver/Makefile @@ -0,0 +1,19 @@ +MNAME = hello +BDIR = /home/devel/work/build-linux + +ifeq ($(KERNELRELEASE),) +# called from shell + +.PHONY: default clean + +default: + make -C $(BDIR) M=$(shell pwd) modules + +clean: + rm -rf $(MNAME).mod.* $(MNAME).*o .$(MNAME)* .tmp* Module.symvers modules.order + +else +# called from kernel Makefile + + obj-m := $(MNAME).o +endif diff --git a/schulung_tools/drivers/modules/hellodriver/hello.c b/schulung_tools/drivers/modules/hellodriver/hello.c new file mode 100644 index 0000000..caded6d --- /dev/null +++ b/schulung_tools/drivers/modules/hellodriver/hello.c @@ -0,0 +1,209 @@ +#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; + } + + 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_out1; + } + + if (hello->minor >= HELLO_MAX_DEVICES) { + dev_err(&pdev->dev, "invalid index: %u\n", hello->minor); + ret = -EINVAL; + goto err_out1; + } + + 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_out1; + } + + 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_out2; + } + + dev_info(&pdev->dev, "HELLO! I am hello device %d!\n", hello->minor); + + return 0; + +err_out2: + cdev_del(&hello->cdev); +err_out1: + platform_set_drvdata(pdev, NULL); + 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", + .owner = THIS_MODULE, + .of_match_table = hello_match, + }, + .probe = hello_probe, + .remove = hello_remove, +}; + +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_out1; + } + + ret = platform_driver_register(&hello_driver); + if (ret != 0) + goto err_out2; + + return 0; + +err_out2: + class_destroy(hello_class); +err_out1: + unregister_chrdev_region(hello_devt, HELLO_MAX_DEVICES); + return ret; +} + +static void __exit hello_exit(void) +{ + printk(KERN_INFO "%s\n", __func__); + + 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("20160607"); diff --git a/schulung_tools/drivers/modules/hellodriver/patch-add-sysfs.diff b/schulung_tools/drivers/modules/hellodriver/patch-add-sysfs.diff new file mode 100644 index 0000000..703b6e0 --- /dev/null +++ b/schulung_tools/drivers/modules/hellodriver/patch-add-sysfs.diff @@ -0,0 +1,48 @@ +--- a/hello.c 2016-06-08 20:21:26.751180497 +0200 ++++ b/hello.c 2016-06-08 20:19:31.655178050 +0200 +@@ -76,6 +76,30 @@ + .write = hello_write, + }; + ++static ssize_t major_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d\n", MAJOR(hello_devt)); ++} ++static DEVICE_ATTR_RO(major); ++ ++static ssize_t minor_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct hello_dev *hello = platform_get_drvdata(pdev); ++ ++ return sprintf(buf, "%d\n", hello->minor); ++} ++static DEVICE_ATTR_RO(minor); ++ ++static struct attribute *hello_device_attrs[] = { ++ &dev_attr_major.attr, ++ &dev_attr_minor.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(hello_device); ++ + static int hello_probe(struct platform_device *pdev) + { + struct hello_dev *hello; +@@ -178,6 +202,8 @@ + goto err_out1; + } + ++ hello_class->dev_groups = hello_device_groups; ++ + ret = platform_driver_register(&hello_driver); + if (ret != 0) + goto err_out2; +@@ -206,4 +232,4 @@ + MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>"); + MODULE_DESCRIPTION("a great module for hello-ing!"); + MODULE_LICENSE("GPL v2"); +-MODULE_VERSION("20160607"); ++MODULE_VERSION("20160608"); diff --git a/schulung_tools/drivers/modules/hellodriver/patch-hello-dts.diff b/schulung_tools/drivers/modules/hellodriver/patch-hello-dts.diff new file mode 100644 index 0000000..2d0bf52 --- /dev/null +++ b/schulung_tools/drivers/modules/hellodriver/patch-hello-dts.diff @@ -0,0 +1,26 @@ +diff --git a/arch/arm/boot/dts/vexpress-v2p-ca9.dts b/arch/arm/boot/dts/vexpress-v2p-ca9.dts +index d949fac..4952799 100644 +--- a/arch/arm/boot/dts/vexpress-v2p-ca9.dts ++++ b/arch/arm/boot/dts/vexpress-v2p-ca9.dts +@@ -132,6 +132,21 @@ + status = "disabled"; + }; + ++ hello@1 { ++ compatible = "virtual,hello"; ++ index = <1>; ++ }; ++ ++ hello@5 { ++ compatible = "virtual,hello"; ++ index = <5>; ++ }; ++ ++ hello@8 { ++ compatible = "virtual,hello"; ++ index = <8>; ++ }; ++ + watchdog@100e5000 { + compatible = "arm,sp805", "arm,primecell"; + reg = <0x100e5000 0x1000>; diff --git a/schulung_tools/drivers/modules/simplemodule/Makefile b/schulung_tools/drivers/modules/simplemodule/Makefile new file mode 100644 index 0000000..9413c4d --- /dev/null +++ b/schulung_tools/drivers/modules/simplemodule/Makefile @@ -0,0 +1,19 @@ +MNAME=simple +BDIR=/home/devel/work/build-linux + +ifeq ($(KERNELRELEASE),) +# called from shell + +.PHONY: default clean + +default: + make -C $(BDIR) M=$(shell pwd) modules + +clean: + rm -rf $(MNAME).mod.* $(MNAME).*o .$(MNAME)* .tmp* Module.symvers modules.order + +else +# called from kernel Makefile + + obj-m := $(MNAME).o +endif diff --git a/schulung_tools/drivers/modules/simplemodule/simple.c b/schulung_tools/drivers/modules/simplemodule/simple.c new file mode 100644 index 0000000..e091f68 --- /dev/null +++ b/schulung_tools/drivers/modules/simplemodule/simple.c @@ -0,0 +1,21 @@ +#include <linux/init.h> +#include <linux/module.h> + +static int simple_hello(void) +{ + printk(KERN_ERR "called %s\n", __func__); + return 0; +} + +static void simple_goodbye(void) +{ + printk(KERN_ERR "called %s\n", __func__); +} + +module_init(simple_hello); +module_exit(simple_goodbye); + +MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>"); +MODULE_DESCRIPTION("simple"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("12345.54321"); diff --git a/schulung_tools/drivers/patch-kernel-hello.diff b/schulung_tools/drivers/patch-kernel-hello.diff new file mode 100644 index 0000000..ab2949c --- /dev/null +++ b/schulung_tools/drivers/patch-kernel-hello.diff @@ -0,0 +1,271 @@ +diff -urNp a/drivers/char/hello.c b/drivers/char/hello.c +--- a/drivers/char/hello.c 1970-01-01 01:00:00.000000000 +0100 ++++ b/drivers/char/hello.c 2016-06-08 20:46:01.991211855 +0200 +@@ -0,0 +1,235 @@ ++#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 ssize_t major_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d\n", MAJOR(hello_devt)); ++} ++static DEVICE_ATTR_RO(major); ++ ++static ssize_t minor_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct hello_dev *hello = platform_get_drvdata(pdev); ++ ++ return sprintf(buf, "%d\n", hello->minor); ++} ++static DEVICE_ATTR_RO(minor); ++ ++static struct attribute *hello_device_attrs[] = { ++ &dev_attr_major.attr, ++ &dev_attr_minor.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(hello_device); ++ ++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; ++ } ++ ++ 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_out1; ++ } ++ ++ if (hello->minor >= HELLO_MAX_DEVICES) { ++ dev_err(&pdev->dev, "invalid index: %u\n", hello->minor); ++ ret = -EINVAL; ++ goto err_out1; ++ } ++ ++ 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_out1; ++ } ++ ++ 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_out2; ++ } ++ ++ dev_info(&pdev->dev, "HELLO! I am hello device %d!\n", hello->minor); ++ ++ return 0; ++ ++err_out2: ++ cdev_del(&hello->cdev); ++err_out1: ++ platform_set_drvdata(pdev, NULL); ++ 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", ++ .owner = THIS_MODULE, ++ .of_match_table = hello_match, ++ }, ++ .probe = hello_probe, ++ .remove = hello_remove, ++}; ++ ++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_out1; ++ } ++ ++ hello_class->dev_groups = hello_device_groups; ++ ++ ret = platform_driver_register(&hello_driver); ++ if (ret != 0) ++ goto err_out2; ++ ++ return 0; ++ ++err_out2: ++ class_destroy(hello_class); ++err_out1: ++ unregister_chrdev_region(hello_devt, HELLO_MAX_DEVICES); ++ return ret; ++} ++ ++static void __exit hello_exit(void) ++{ ++ printk(KERN_INFO "%s\n", __func__); ++ ++ 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("20160608"); +diff -urNp a/drivers/char/Kconfig b/drivers/char/Kconfig +--- a/drivers/char/Kconfig 2016-05-16 00:43:13.000000000 +0200 ++++ b/drivers/char/Kconfig 2016-06-08 20:41:14.047205734 +0200 +@@ -6,6 +6,16 @@ menu "Character devices" + + source "drivers/tty/Kconfig" + ++config HELLO ++ tristate "hello demo driver" ++ default n ++ help ++ This driver demonstrates how to create a platform device that is ++ specified in a device tree. ++ ++ It also create a character device to demonstrate how a character ++ device interface can be provided to userspace. ++ + config DEVMEM + bool "/dev/mem virtual device support" + default y +diff -urNp a/drivers/char/Makefile b/drivers/char/Makefile +--- a/drivers/char/Makefile 2016-05-16 00:43:13.000000000 +0200 ++++ b/drivers/char/Makefile 2016-06-08 14:16:44.897437533 +0200 +@@ -16,6 +16,8 @@ obj-$(CONFIG_IBM_BSR) += bsr.o + obj-$(CONFIG_SGI_MBCS) += mbcs.o + obj-$(CONFIG_BFIN_OTP) += bfin-otp.o + ++obj-$(CONFIG_HELLO) += hello.o ++ + obj-$(CONFIG_PRINTER) += lp.o + + obj-$(CONFIG_APM_EMULATION) += apm-emulation.o |
