summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ogness <john.ogness@linutronix.de>2019-02-15 13:10:21 +0106
committerJohn Ogness <john.ogness@linutronix.de>2019-02-15 13:10:21 +0106
commitaed79d1121a9c65dead95fb6ea8c2d8541d01143 (patch)
tree6917e9570dfd2d60f357759e096cb013bc0c001c
parent2d2484928560dd0ffec90069166b0161cdc84a54 (diff)
schulung_tools: hellodriver: add patches for use with leds
These patches are based heavily on the work by Manu to make the driver lessons more interesting. Rather than create a new driver, I took his work and created a series of patches that do interesting modifications to the hello driver. These patches can be used together with the "leds" program to do live blinking demonstrations. Signed-off-by: John Ogness <john.ogness@linutronix.de>
-rw-r--r--schulung_tools/drivers/modules/hellodriver/patches-leds/0001-hello-change-from-platform-to-pci-bus.patch185
-rw-r--r--schulung_tools/drivers/modules/hellodriver/patches-leds/0002-hello-add-pci-memory-mapping.patch64
-rw-r--r--schulung_tools/drivers/modules/hellodriver/patches-leds/0003-hello-add-led-toggling-via-chardev-read-write.patch56
-rw-r--r--schulung_tools/drivers/modules/hellodriver/patches-leds/0004-hello-add-led-toggling-via-chardev-ioctl.patch124
-rw-r--r--schulung_tools/drivers/modules/hellodriver/patches-leds/0005-hello-add-led-toggling-via-leds-subsystem.patch142
-rw-r--r--schulung_tools/drivers/modules/hellodriver/patches-leds/0006-hello-remove-chardev-use-only-leds-subsystem.patch338
-rw-r--r--schulung_tools/drivers/modules/hellodriver/patches-leds/README6
7 files changed, 915 insertions, 0 deletions
diff --git a/schulung_tools/drivers/modules/hellodriver/patches-leds/0001-hello-change-from-platform-to-pci-bus.patch b/schulung_tools/drivers/modules/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/modules/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/modules/hellodriver/patches-leds/0002-hello-add-pci-memory-mapping.patch b/schulung_tools/drivers/modules/hellodriver/patches-leds/0002-hello-add-pci-memory-mapping.patch
new file mode 100644
index 0000000..0a11a57
--- /dev/null
+++ b/schulung_tools/drivers/modules/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/modules/hellodriver/patches-leds/0003-hello-add-led-toggling-via-chardev-read-write.patch b/schulung_tools/drivers/modules/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/modules/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/modules/hellodriver/patches-leds/0004-hello-add-led-toggling-via-chardev-ioctl.patch b/schulung_tools/drivers/modules/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/modules/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/modules/hellodriver/patches-leds/0005-hello-add-led-toggling-via-leds-subsystem.patch b/schulung_tools/drivers/modules/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/modules/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/modules/hellodriver/patches-leds/0006-hello-remove-chardev-use-only-leds-subsystem.patch b/schulung_tools/drivers/modules/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/modules/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/modules/hellodriver/patches-leds/README b/schulung_tools/drivers/modules/hellodriver/patches-leds/README
new file mode 100644
index 0000000..51bbffa
--- /dev/null
+++ b/schulung_tools/drivers/modules/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.)