summaryrefslogtreecommitdiff
path: root/schulung_tools/drivers/hellodriver
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
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')
-rw-r--r--schulung_tools/drivers/hellodriver/Makefile19
-rw-r--r--schulung_tools/drivers/hellodriver/hello.c230
-rw-r--r--schulung_tools/drivers/hellodriver/patch-add-sysfs.diff57
-rw-r--r--schulung_tools/drivers/hellodriver/patch-hello-dts.diff26
-rw-r--r--schulung_tools/drivers/hellodriver/patch-kernel-build.diff32
-rw-r--r--schulung_tools/drivers/hellodriver/patches-leds/0001-hello-change-from-platform-to-pci-bus.patch185
-rw-r--r--schulung_tools/drivers/hellodriver/patches-leds/0002-hello-add-pci-memory-mapping.patch64
-rw-r--r--schulung_tools/drivers/hellodriver/patches-leds/0003-hello-add-led-toggling-via-chardev-read-write.patch56
-rw-r--r--schulung_tools/drivers/hellodriver/patches-leds/0004-hello-add-led-toggling-via-chardev-ioctl.patch124
-rw-r--r--schulung_tools/drivers/hellodriver/patches-leds/0005-hello-add-led-toggling-via-leds-subsystem.patch142
-rw-r--r--schulung_tools/drivers/hellodriver/patches-leds/0006-hello-remove-chardev-use-only-leds-subsystem.patch338
-rw-r--r--schulung_tools/drivers/hellodriver/patches-leds/README6
12 files changed, 1279 insertions, 0 deletions
diff --git a/schulung_tools/drivers/hellodriver/Makefile b/schulung_tools/drivers/hellodriver/Makefile
new file mode 100644
index 0000000..764fefd
--- /dev/null
+++ b/schulung_tools/drivers/hellodriver/Makefile
@@ -0,0 +1,19 @@
+MNAME = hello
+BDIR = /lib/modules/`uname -r`/build
+
+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/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");
diff --git a/schulung_tools/drivers/hellodriver/patch-add-sysfs.diff b/schulung_tools/drivers/hellodriver/patch-add-sysfs.diff
new file mode 100644
index 0000000..44c913e
--- /dev/null
+++ b/schulung_tools/drivers/hellodriver/patch-add-sysfs.diff
@@ -0,0 +1,57 @@
+From 9ec20c5b243d6f1f83b38b57474cf532c4575a02 Mon Sep 17 00:00:00 2001
+From: John Ogness <john.ogness@linutronix.de>
+Date: Fri, 15 Feb 2019 12:58:25 +0106
+Subject: [PATCH] hello: add custom sysfs files
+
+Signed-off-by: John Ogness <john.ogness@linutronix.de>
+---
+ hello.c | 26 ++++++++++++++++++++++
+ 1 file changed, 26 insertions(+)
+
+diff --git a/hello.c b/hello.c
+index 7f78d06..4c7989a 100644
+--- a/hello.c
++++ b/hello.c
+@@ -77,6 +77,30 @@ static const struct file_operations hello_fops = {
+ .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;
+@@ -187,6 +211,8 @@ static int __init hello_init(void)
+ goto err_region;
+ }
+
++ hello_class->dev_groups = hello_device_groups;
++
+ ret = platform_driver_register(&hello_driver);
+ if (ret != 0)
+ goto err_class;
+--
+2.1.4
+
diff --git a/schulung_tools/drivers/hellodriver/patch-hello-dts.diff b/schulung_tools/drivers/hellodriver/patch-hello-dts.diff
new file mode 100644
index 0000000..2d0bf52
--- /dev/null
+++ b/schulung_tools/drivers/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/hellodriver/patch-kernel-build.diff b/schulung_tools/drivers/hellodriver/patch-kernel-build.diff
new file mode 100644
index 0000000..8d43ca7
--- /dev/null
+++ b/schulung_tools/drivers/hellodriver/patch-kernel-build.diff
@@ -0,0 +1,32 @@
+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
diff --git a/schulung_tools/drivers/hellodriver/patches-leds/0001-hello-change-from-platform-to-pci-bus.patch b/schulung_tools/drivers/hellodriver/patches-leds/0001-hello-change-from-platform-to-pci-bus.patch
new file mode 100644
index 0000000..35dc817
--- /dev/null
+++ b/schulung_tools/drivers/hellodriver/patches-leds/0001-hello-change-from-platform-to-pci-bus.patch
@@ -0,0 +1,185 @@
+From 28357d61e0ec39851ca88a09b04ce876b8758448 Mon Sep 17 00:00:00 2001
+From: John Ogness <john.ogness@linutronix.de>
+Date: Fri, 15 Feb 2019 12:11:27 +0100
+Subject: [PATCH 2/7] hello: change from platform to pci bus
+
+Signed-off-by: John Ogness <john.ogness@linutronix.de>
+---
+ hello.c | 89 +++++++++++++++++++++++++++++++----------------------------------
+ 1 file changed, 43 insertions(+), 46 deletions(-)
+
+diff --git a/hello.c b/hello.c
+index 7f78d06..db50302 100644
+--- a/hello.c
++++ b/hello.c
+@@ -5,9 +5,8 @@
+ #include <linux/uaccess.h>
+ #include <linux/slab.h>
+ #include <linux/device.h>
+-#include <linux/platform_device.h>
++#include <linux/pci.h>
+ #include <linux/cdev.h>
+-#include <linux/of.h>
+
+ struct hello_dev {
+ struct device *dev;
+@@ -77,7 +76,7 @@ static const struct file_operations hello_fops = {
+ .write = hello_write,
+ };
+
+-static int hello_probe(struct platform_device *pdev)
++static int hello_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+ {
+ struct hello_dev *hello;
+ struct device *dev;
+@@ -90,15 +89,7 @@ static int hello_probe(struct platform_device *pdev)
+ 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
++ hello->minor = 1;
+
+ if (hello->minor >= HELLO_MAX_DEVICES) {
+ dev_err(&pdev->dev, "invalid index: %u\n", hello->minor);
+@@ -107,7 +98,27 @@ static int hello_probe(struct platform_device *pdev)
+ }
+
+ hello->dev = &pdev->dev;
+- platform_set_drvdata(pdev, hello);
++ pci_set_drvdata(pdev, hello);
++
++ ret = pci_enable_device(pdev);
++ if (ret) {
++ dev_err(&pdev->dev, "failed to enable device\n");
++ goto err_drvdata;
++ }
++
++ if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
++ dev_err(&pdev->dev, "failed to find base address\n");
++ ret = -ENODEV;
++ goto err_enable;
++ }
++
++ ret = pci_request_regions(pdev, "hello");
++ if (ret) {
++ dev_err(&pdev->dev, "failed to request resources\n");
++ goto err_enable;
++ }
++
++ pci_set_master(pdev);
+
+ devt = MKDEV(MAJOR(hello_devt), hello->minor);
+
+@@ -116,7 +127,7 @@ static int hello_probe(struct platform_device *pdev)
+ ret = cdev_add(&hello->cdev, devt, 1);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "cdev_add failed\n");
+- goto err_drvdata;
++ goto err_regions;
+ }
+
+ dev = device_create(hello_class, &pdev->dev, devt, hello,
+@@ -133,43 +144,41 @@ static int hello_probe(struct platform_device *pdev)
+
+ err_cdev:
+ cdev_del(&hello->cdev);
++err_regions:
++ pci_release_regions(pdev);
++err_enable:
++ pci_disable_device(pdev);
+ err_drvdata:
+- platform_set_drvdata(pdev, NULL);
+-err_out:
++ pci_set_drvdata(pdev, NULL);
+ return ret;
+ }
+
+-static int hello_remove(struct platform_device *pdev)
++static void hello_remove(struct pci_dev *pdev)
+ {
+- struct hello_dev *hello = platform_get_drvdata(pdev);
++ struct hello_dev *hello = pci_get_drvdata(pdev);
+
+ device_destroy(hello_class, MKDEV(MAJOR(hello_devt), hello->minor));
+ cdev_del(&hello->cdev);
+- platform_set_drvdata(pdev, NULL);
++ pci_release_regions(pdev);
++ pci_disable_device(pdev);
++ pci_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", },
++static const struct pci_device_id hello_id_table[] = {
++ { 0x1af4, 0x1110, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0 },
+ { /* end of table */ }
+ };
++MODULE_DEVICE_TABLE(pci, hello_id_table);
+
+-static struct platform_driver hello_driver = {
+- .driver = {
+- .name = "hello",
+- .of_match_table = hello_match,
+- },
++static struct pci_driver hello_driver = {
++ .name = "hello",
++ .id_table = hello_id_table,
+ .probe = hello_probe,
+ .remove = hello_remove,
+ };
+
+-#ifndef CONFIG_OF
+-static struct platform_device *pdevs[3];
+-#endif
+-
+ static int __init hello_init(void)
+ {
+ int ret;
+@@ -187,16 +196,10 @@ static int __init hello_init(void)
+ goto err_region;
+ }
+
+- ret = platform_driver_register(&hello_driver);
++ ret = pci_register_driver(&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:
+@@ -210,13 +213,7 @@ 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);
++ pci_unregister_driver(&hello_driver);
+ class_destroy(hello_class);
+ unregister_chrdev_region(hello_devt, HELLO_MAX_DEVICES);
+ }
+--
+2.11.0
+
diff --git a/schulung_tools/drivers/hellodriver/patches-leds/0002-hello-add-pci-memory-mapping.patch b/schulung_tools/drivers/hellodriver/patches-leds/0002-hello-add-pci-memory-mapping.patch
new file mode 100644
index 0000000..0a11a57
--- /dev/null
+++ b/schulung_tools/drivers/hellodriver/patches-leds/0002-hello-add-pci-memory-mapping.patch
@@ -0,0 +1,64 @@
+From 3afec946845a02a8fce94d00abda9b4c8abcbce0 Mon Sep 17 00:00:00 2001
+From: John Ogness <john.ogness@linutronix.de>
+Date: Fri, 15 Feb 2019 11:03:59 +0106
+Subject: [PATCH 3/7] hello: add pci memory mapping
+
+Signed-off-by: John Ogness <john.ogness@linutronix.de>
+---
+ hello.c | 12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/hello.c b/hello.c
+index db50302..58a64e5 100644
+--- a/hello.c
++++ b/hello.c
+@@ -12,6 +12,7 @@ struct hello_dev {
+ struct device *dev;
+ struct cdev cdev;
+ unsigned int minor;
++ void __iomem *mem;
+ };
+
+ #define HELLO_MAX_DEVICES 10
+@@ -120,6 +121,12 @@ static int hello_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+
+ pci_set_master(pdev);
+
++ hello->mem = pci_iomap(pdev, 2, 0);
++ if (!hello->mem) {
++ ret = -ENOMEM;
++ goto err_regions;
++ }
++
+ devt = MKDEV(MAJOR(hello_devt), hello->minor);
+
+ cdev_init(&hello->cdev, &hello_fops);
+@@ -127,7 +134,7 @@ static int hello_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+ ret = cdev_add(&hello->cdev, devt, 1);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "cdev_add failed\n");
+- goto err_regions;
++ goto err_iomap;
+ }
+
+ dev = device_create(hello_class, &pdev->dev, devt, hello,
+@@ -144,6 +151,8 @@ static int hello_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+
+ err_cdev:
+ cdev_del(&hello->cdev);
++err_iomap:
++ pci_iounmap(pdev, hello->mem);
+ err_regions:
+ pci_release_regions(pdev);
+ err_enable:
+@@ -159,6 +168,7 @@ static void hello_remove(struct pci_dev *pdev)
+
+ device_destroy(hello_class, MKDEV(MAJOR(hello_devt), hello->minor));
+ cdev_del(&hello->cdev);
++ pci_iounmap(pdev, hello->mem);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+--
+2.11.0
+
diff --git a/schulung_tools/drivers/hellodriver/patches-leds/0003-hello-add-led-toggling-via-chardev-read-write.patch b/schulung_tools/drivers/hellodriver/patches-leds/0003-hello-add-led-toggling-via-chardev-read-write.patch
new file mode 100644
index 0000000..8f81e39
--- /dev/null
+++ b/schulung_tools/drivers/hellodriver/patches-leds/0003-hello-add-led-toggling-via-chardev-read-write.patch
@@ -0,0 +1,56 @@
+From c7a4c4448632af43383818654dd79d2251b27e3e Mon Sep 17 00:00:00 2001
+From: John Ogness <john.ogness@linutronix.de>
+Date: Fri, 15 Feb 2019 11:04:56 +0106
+Subject: [PATCH 4/7] hello: add led toggling via chardev read/write
+
+Signed-off-by: John Ogness <john.ogness@linutronix.de>
+---
+ hello.c | 28 +++++++++++++++++++++++++++-
+ 1 file changed, 27 insertions(+), 1 deletion(-)
+
+diff --git a/hello.c b/hello.c
+index 58a64e5..f664121 100644
+--- a/hello.c
++++ b/hello.c
+@@ -61,11 +61,37 @@ static ssize_t hello_read(struct file *f, char __user *u, size_t s, loff_t *l)
+ return len;
+ }
+
++static void toggle_led(struct hello_dev *hello, int offset)
++{
++ if (readb(hello->mem + offset))
++ writeb(0, hello->mem + offset);
++ else
++ writeb(0xff, hello->mem + offset);
++}
++
+ 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);
++ size_t sz = s;
++ char buf[32];
++
++ if (sz >= sizeof(buf))
++ sz = sizeof(buf) - 1;
++ if (copy_from_user(buf, u, sz) != 0)
++ return -EFAULT;
++ buf[sz] = 0;
++
++ dev_info(hello->dev, "%s: size=%zu offset=%llu buf=%s\n", __func__,
++ s, *l, buf);
++
++ if (strstarts(buf, "heartbeat"))
++ toggle_led(hello, 0);
++ else if (strstarts(buf, "net"))
++ toggle_led(hello, 1);
++ else if (strstarts(buf, "disk"))
++ toggle_led(hello, 2);
++
+ return s;
+ }
+
+--
+2.11.0
+
diff --git a/schulung_tools/drivers/hellodriver/patches-leds/0004-hello-add-led-toggling-via-chardev-ioctl.patch b/schulung_tools/drivers/hellodriver/patches-leds/0004-hello-add-led-toggling-via-chardev-ioctl.patch
new file mode 100644
index 0000000..a67bf98
--- /dev/null
+++ b/schulung_tools/drivers/hellodriver/patches-leds/0004-hello-add-led-toggling-via-chardev-ioctl.patch
@@ -0,0 +1,124 @@
+From be59ddb43fbf039ed0bc848ac56b7f72f7a619c8 Mon Sep 17 00:00:00 2001
+From: John Ogness <john.ogness@linutronix.de>
+Date: Fri, 15 Feb 2019 11:07:10 +0106
+Subject: [PATCH 5/7] hello: add led toggling via chardev ioctl
+
+Compile userspace application with:
+
+$ gcc -Wall -Werror hello-led.c -ohello-led
+
+Signed-off-by: John Ogness <john.ogness@linutronix.de>
+---
+ hello-led.c | 38 ++++++++++++++++++++++++++++++++++++++
+ hello.c | 22 ++++++++++++++++++++++
+ hello.h | 7 +++++++
+ 3 files changed, 67 insertions(+)
+ create mode 100644 hello-led.c
+ create mode 100644 hello.h
+
+diff --git a/hello-led.c b/hello-led.c
+new file mode 100644
+index 0000000..43230cd
+--- /dev/null
++++ b/hello-led.c
+@@ -0,0 +1,38 @@
++#include <stdio.h>
++#include <string.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <unistd.h>
++#include <sys/ioctl.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include "hello.h"
++
++int main(int argc, char *argv[])
++{
++ int fd;
++
++ if (argc != 3) {
++ fprintf(stderr, "usage: %s <device> <led>\n", argv[0]);
++ return 1;
++ }
++
++ fd = open(argv[1], O_RDWR);
++ if (fd < 0) {
++ fprintf(stderr, "open() failed: %s\n", strerror(errno));
++ return 1;
++ }
++
++ if (strcmp(argv[2], "heartbeat") == 0)
++ ioctl(fd, HELLO_IOCTL_TOGGLE_LED, 0);
++ else if (strcmp(argv[2], "net") == 0)
++ ioctl(fd, HELLO_IOCTL_TOGGLE_LED, 1);
++ else if (strcmp(argv[2], "disk") == 0)
++ ioctl(fd, HELLO_IOCTL_TOGGLE_LED, 2);
++ else
++ fprintf(stderr, "unknown led\n");
++
++ close(fd);
++
++ return 0;
++}
+diff --git a/hello.c b/hello.c
+index f664121..c42c75f 100644
+--- a/hello.c
++++ b/hello.c
+@@ -7,6 +7,7 @@
+ #include <linux/device.h>
+ #include <linux/pci.h>
+ #include <linux/cdev.h>
++#include "hello.h"
+
+ struct hello_dev {
+ struct device *dev;
+@@ -95,12 +96,33 @@ static ssize_t hello_write(struct file *f, const char __user *u, size_t s,
+ return s;
+ }
+
++static long hello_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
++{
++ struct hello_dev *hello = f->private_data;
++ u32 val;
++
++ dev_info(hello->dev, "%s: cmd=%u arg=%lu\n", __func__, cmd, arg);
++
++ switch (cmd) {
++ case HELLO_IOCTL_TOGGLE_LED:
++ val = arg;
++ if (val >= 0 && val <= 2)
++ toggle_led(hello, val);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
+ static const struct file_operations hello_fops = {
+ .owner = THIS_MODULE,
+ .open = hello_open,
+ .release = hello_release,
+ .read = hello_read,
+ .write = hello_write,
++ .unlocked_ioctl = hello_ioctl,
+ };
+
+ static int hello_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+diff --git a/hello.h b/hello.h
+new file mode 100644
+index 0000000..cb172d9
+--- /dev/null
++++ b/hello.h
+@@ -0,0 +1,7 @@
++#ifndef HELLO_H
++#define HELLO_H
++
++#define HELLO_BASE 0x9F
++#define HELLO_IOCTL_TOGGLE_LED _IOW(HELLO_BASE, 0x00, unsigned int)
++
++#endif /* HELLO_H */
+--
+2.11.0
+
diff --git a/schulung_tools/drivers/hellodriver/patches-leds/0005-hello-add-led-toggling-via-leds-subsystem.patch b/schulung_tools/drivers/hellodriver/patches-leds/0005-hello-add-led-toggling-via-leds-subsystem.patch
new file mode 100644
index 0000000..32f37ce
--- /dev/null
+++ b/schulung_tools/drivers/hellodriver/patches-leds/0005-hello-add-led-toggling-via-leds-subsystem.patch
@@ -0,0 +1,142 @@
+From 3caece4ff394938f7544b994d35e784e2ce617cb Mon Sep 17 00:00:00 2001
+From: John Ogness <john.ogness@linutronix.de>
+Date: Fri, 15 Feb 2019 11:10:09 +0106
+Subject: [PATCH 6/7] hello: add led toggling via leds subsystem
+
+Signed-off-by: John Ogness <john.ogness@linutronix.de>
+---
+ hello.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 90 insertions(+)
+
+diff --git a/hello.c b/hello.c
+index c42c75f..f9809f3 100644
+--- a/hello.c
++++ b/hello.c
+@@ -7,6 +7,7 @@
+ #include <linux/device.h>
+ #include <linux/pci.h>
+ #include <linux/cdev.h>
++#include <linux/leds.h>
+ #include "hello.h"
+
+ struct hello_dev {
+@@ -14,6 +15,10 @@ struct hello_dev {
+ struct cdev cdev;
+ unsigned int minor;
+ void __iomem *mem;
++
++ struct led_classdev led_heartbeat;
++ struct led_classdev led_net;
++ struct led_classdev led_disk;
+ };
+
+ #define HELLO_MAX_DEVICES 10
+@@ -125,6 +130,67 @@ static const struct file_operations hello_fops = {
+ .unlocked_ioctl = hello_ioctl,
+ };
+
++static void set_brightness(struct hello_dev *hello, int offset,
++ enum led_brightness b)
++{
++ if (b)
++ writeb(0xff, hello->mem + offset);
++ else
++ writeb(0, hello->mem + offset);
++}
++
++static enum led_brightness get_brightness(struct hello_dev *hello, int offset)
++{
++ if (readb(hello->mem + offset))
++ return 1;
++ return 0;
++}
++
++static void hello_led_heartbeat_set(struct led_classdev *ldev,
++ enum led_brightness b)
++{
++ struct hello_dev *hello = container_of(ldev, struct hello_dev,
++ led_heartbeat);
++ set_brightness(hello, 0, b);
++}
++
++static enum led_brightness hello_led_heartbeat_get(struct led_classdev *ldev)
++{
++ struct hello_dev *hello = container_of(ldev, struct hello_dev,
++ led_heartbeat);
++ return get_brightness(hello, 0);
++}
++
++static void hello_led_net_set(struct led_classdev *ldev,
++ enum led_brightness b)
++{
++ struct hello_dev *hello = container_of(ldev, struct hello_dev,
++ led_net);
++ set_brightness(hello, 1, b);
++}
++
++static enum led_brightness hello_led_net_get(struct led_classdev *ldev)
++{
++ struct hello_dev *hello = container_of(ldev, struct hello_dev,
++ led_net);
++ return get_brightness(hello, 1);
++}
++
++static void hello_led_disk_set(struct led_classdev *ldev,
++ enum led_brightness b)
++{
++ struct hello_dev *hello = container_of(ldev, struct hello_dev,
++ led_disk);
++ set_brightness(hello, 2, b);
++}
++
++static enum led_brightness hello_led_disk_get(struct led_classdev *ldev)
++{
++ struct hello_dev *hello = container_of(ldev, struct hello_dev,
++ led_disk);
++ return get_brightness(hello, 2);
++}
++
+ static int hello_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+ {
+ struct hello_dev *hello;
+@@ -193,6 +259,27 @@ static int hello_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+ goto err_cdev;
+ }
+
++ hello->led_heartbeat.name = "heartbeat";
++ hello->led_heartbeat.brightness = get_brightness(hello, 0);
++ hello->led_heartbeat.max_brightness = 1;
++ hello->led_heartbeat.brightness_set = hello_led_heartbeat_set;
++ hello->led_heartbeat.brightness_get = hello_led_heartbeat_get;
++ led_classdev_register(&pdev->dev, &hello->led_heartbeat);
++
++ hello->led_net.name = "net";
++ hello->led_net.brightness = get_brightness(hello, 2);
++ hello->led_net.max_brightness = 1;
++ hello->led_net.brightness_set = hello_led_net_set;
++ hello->led_net.brightness_get = hello_led_net_get;
++ led_classdev_register(&pdev->dev, &hello->led_net);
++
++ hello->led_disk.name = "disk";
++ hello->led_disk.brightness = get_brightness(hello, 2);
++ hello->led_disk.max_brightness = 1;
++ hello->led_disk.brightness_set = hello_led_disk_set;
++ hello->led_disk.brightness_get = hello_led_disk_get;
++ led_classdev_register(&pdev->dev, &hello->led_disk);
++
+ dev_info(&pdev->dev, "HELLO! I am hello device %d!\n", hello->minor);
+
+ return 0;
+@@ -214,6 +301,9 @@ static void hello_remove(struct pci_dev *pdev)
+ {
+ struct hello_dev *hello = pci_get_drvdata(pdev);
+
++ led_classdev_unregister(&hello->led_disk);
++ led_classdev_unregister(&hello->led_net);
++ led_classdev_unregister(&hello->led_heartbeat);
+ device_destroy(hello_class, MKDEV(MAJOR(hello_devt), hello->minor));
+ cdev_del(&hello->cdev);
+ pci_iounmap(pdev, hello->mem);
+--
+2.11.0
+
diff --git a/schulung_tools/drivers/hellodriver/patches-leds/0006-hello-remove-chardev-use-only-leds-subsystem.patch b/schulung_tools/drivers/hellodriver/patches-leds/0006-hello-remove-chardev-use-only-leds-subsystem.patch
new file mode 100644
index 0000000..3274ccd
--- /dev/null
+++ b/schulung_tools/drivers/hellodriver/patches-leds/0006-hello-remove-chardev-use-only-leds-subsystem.patch
@@ -0,0 +1,338 @@
+From 5f05842b160249563a7e4965aaa01998e562f487 Mon Sep 17 00:00:00 2001
+From: John Ogness <john.ogness@linutronix.de>
+Date: Fri, 15 Feb 2019 11:33:24 +0100
+Subject: [PATCH 7/7] hello: remove chardev, use only leds subsystem
+
+Signed-off-by: John Ogness <john.ogness@linutronix.de>
+---
+ hello-led.c | 38 -------------
+ hello.c | 181 +-----------------------------------------------------------
+ hello.h | 7 ---
+ 3 files changed, 3 insertions(+), 223 deletions(-)
+ delete mode 100644 hello-led.c
+ delete mode 100644 hello.h
+
+diff --git a/hello-led.c b/hello-led.c
+deleted file mode 100644
+index 43230cd..0000000
+--- a/hello-led.c
++++ /dev/null
+@@ -1,38 +0,0 @@
+-#include <stdio.h>
+-#include <string.h>
+-#include <errno.h>
+-#include <fcntl.h>
+-#include <unistd.h>
+-#include <sys/ioctl.h>
+-#include <sys/types.h>
+-#include <sys/stat.h>
+-#include "hello.h"
+-
+-int main(int argc, char *argv[])
+-{
+- int fd;
+-
+- if (argc != 3) {
+- fprintf(stderr, "usage: %s <device> <led>\n", argv[0]);
+- return 1;
+- }
+-
+- fd = open(argv[1], O_RDWR);
+- if (fd < 0) {
+- fprintf(stderr, "open() failed: %s\n", strerror(errno));
+- return 1;
+- }
+-
+- if (strcmp(argv[2], "heartbeat") == 0)
+- ioctl(fd, HELLO_IOCTL_TOGGLE_LED, 0);
+- else if (strcmp(argv[2], "net") == 0)
+- ioctl(fd, HELLO_IOCTL_TOGGLE_LED, 1);
+- else if (strcmp(argv[2], "disk") == 0)
+- ioctl(fd, HELLO_IOCTL_TOGGLE_LED, 2);
+- else
+- fprintf(stderr, "unknown led\n");
+-
+- close(fd);
+-
+- return 0;
+-}
+diff --git a/hello.c b/hello.c
+index f9809f3..52df6d7 100644
+--- a/hello.c
++++ b/hello.c
+@@ -1,19 +1,12 @@
+ // 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/pci.h>
+-#include <linux/cdev.h>
+ #include <linux/leds.h>
+-#include "hello.h"
+
+ struct hello_dev {
+ struct device *dev;
+- struct cdev cdev;
+- unsigned int minor;
+ void __iomem *mem;
+
+ struct led_classdev led_heartbeat;
+@@ -21,115 +14,6 @@ struct hello_dev {
+ struct led_classdev led_disk;
+ };
+
+-#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 void toggle_led(struct hello_dev *hello, int offset)
+-{
+- if (readb(hello->mem + offset))
+- writeb(0, hello->mem + offset);
+- else
+- writeb(0xff, hello->mem + offset);
+-}
+-
+-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;
+- size_t sz = s;
+- char buf[32];
+-
+- if (sz >= sizeof(buf))
+- sz = sizeof(buf) - 1;
+- if (copy_from_user(buf, u, sz) != 0)
+- return -EFAULT;
+- buf[sz] = 0;
+-
+- dev_info(hello->dev, "%s: size=%zu offset=%llu buf=%s\n", __func__,
+- s, *l, buf);
+-
+- if (strstarts(buf, "heartbeat"))
+- toggle_led(hello, 0);
+- else if (strstarts(buf, "net"))
+- toggle_led(hello, 1);
+- else if (strstarts(buf, "disk"))
+- toggle_led(hello, 2);
+-
+- return s;
+-}
+-
+-static long hello_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+-{
+- struct hello_dev *hello = f->private_data;
+- u32 val;
+-
+- dev_info(hello->dev, "%s: cmd=%u arg=%lu\n", __func__, cmd, arg);
+-
+- switch (cmd) {
+- case HELLO_IOCTL_TOGGLE_LED:
+- val = arg;
+- if (val >= 0 && val <= 2)
+- toggle_led(hello, val);
+- break;
+- default:
+- return -EINVAL;
+- }
+-
+- return 0;
+-}
+-
+-static const struct file_operations hello_fops = {
+- .owner = THIS_MODULE,
+- .open = hello_open,
+- .release = hello_release,
+- .read = hello_read,
+- .write = hello_write,
+- .unlocked_ioctl = hello_ioctl,
+-};
+-
+ static void set_brightness(struct hello_dev *hello, int offset,
+ enum led_brightness b)
+ {
+@@ -194,8 +78,6 @@ static enum led_brightness hello_led_disk_get(struct led_classdev *ldev)
+ static int hello_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+ {
+ struct hello_dev *hello;
+- struct device *dev;
+- dev_t devt;
+ int ret;
+
+ hello = devm_kzalloc(&pdev->dev, sizeof(*hello), GFP_KERNEL);
+@@ -204,14 +86,6 @@ static int hello_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+ return -ENOMEM;
+ }
+
+- hello->minor = 1;
+-
+- 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;
+ pci_set_drvdata(pdev, hello);
+
+@@ -241,24 +115,6 @@ static int hello_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+ goto err_regions;
+ }
+
+- 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_iomap;
+- }
+-
+- 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;
+- }
+-
+ hello->led_heartbeat.name = "heartbeat";
+ hello->led_heartbeat.brightness = get_brightness(hello, 0);
+ hello->led_heartbeat.max_brightness = 1;
+@@ -280,14 +136,10 @@ static int hello_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+ hello->led_disk.brightness_get = hello_led_disk_get;
+ led_classdev_register(&pdev->dev, &hello->led_disk);
+
+- dev_info(&pdev->dev, "HELLO! I am hello device %d!\n", hello->minor);
++ dev_info(&pdev->dev, "HELLO! I am hello device!\n");
+
+ return 0;
+
+-err_cdev:
+- cdev_del(&hello->cdev);
+-err_iomap:
+- pci_iounmap(pdev, hello->mem);
+ err_regions:
+ pci_release_regions(pdev);
+ err_enable:
+@@ -304,14 +156,12 @@ static void hello_remove(struct pci_dev *pdev)
+ led_classdev_unregister(&hello->led_disk);
+ led_classdev_unregister(&hello->led_net);
+ led_classdev_unregister(&hello->led_heartbeat);
+- device_destroy(hello_class, MKDEV(MAJOR(hello_devt), hello->minor));
+- cdev_del(&hello->cdev);
+ pci_iounmap(pdev, hello->mem);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+
+- dev_info(&pdev->dev, "GOODBYE! I was hello device %d!\n", hello->minor);
++ dev_info(&pdev->dev, "GOODBYE! I was hello device!\n");
+ }
+
+ static const struct pci_device_id hello_id_table[] = {
+@@ -329,32 +179,9 @@ static struct pci_driver hello_driver = {
+
+ 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 = pci_register_driver(&hello_driver);
+- if (ret != 0)
+- goto err_class;
+-
+- return 0;
+-
+-err_class:
+- class_destroy(hello_class);
+-err_region:
+- unregister_chrdev_region(hello_devt, HELLO_MAX_DEVICES);
+- return ret;
++ return pci_register_driver(&hello_driver);
+ }
+
+ static void __exit hello_exit(void)
+@@ -362,8 +189,6 @@ static void __exit hello_exit(void)
+ printk(KERN_INFO "%s\n", __func__);
+
+ pci_unregister_driver(&hello_driver);
+- class_destroy(hello_class);
+- unregister_chrdev_region(hello_devt, HELLO_MAX_DEVICES);
+ }
+
+ module_init(hello_init);
+diff --git a/hello.h b/hello.h
+deleted file mode 100644
+index cb172d9..0000000
+--- a/hello.h
++++ /dev/null
+@@ -1,7 +0,0 @@
+-#ifndef HELLO_H
+-#define HELLO_H
+-
+-#define HELLO_BASE 0x9F
+-#define HELLO_IOCTL_TOGGLE_LED _IOW(HELLO_BASE, 0x00, unsigned int)
+-
+-#endif /* HELLO_H */
+--
+2.11.0
+
diff --git a/schulung_tools/drivers/hellodriver/patches-leds/README b/schulung_tools/drivers/hellodriver/patches-leds/README
new file mode 100644
index 0000000..51bbffa
--- /dev/null
+++ b/schulung_tools/drivers/hellodriver/patches-leds/README
@@ -0,0 +1,6 @@
+This set of patches show how different subsystems can be used
+by the driver to communicate with the hardware and to present
+itself to the user.
+
+For live demonstrations with actual blinking leds, use the
+"leds" program. (See the README there for details.)