Przeglądaj źródła

Provide a tasklet-free example

Co-authored-by: Bob Mottram <bob@freedombone.net>
Jim Huang 3 lat temu
rodzic
commit
56f566abe6
4 zmienionych plików z 186 dodań i 0 usunięć
  1. 1 0
      .ci/non-working
  2. 1 0
      examples/Makefile
  3. 149 0
      examples/bh_threaded.c
  4. 35 0
      lkmpg.tex

+ 1 - 0
.ci/non-working

@@ -1,3 +1,4 @@
 bottomhalf
+bh_threaded
 intrpt
 vkbd

+ 1 - 0
examples/Makefile

@@ -26,6 +26,7 @@ obj-m += example_rwlock.o
 obj-m += example_atomic.o
 obj-m += example_mutex.o
 obj-m += bottomhalf.o
+obj-m += bh_threaded.o
 obj-m += ioctl.o
 obj-m += vinput.o
 obj-m += vkbd.o

+ 149 - 0
examples/bh_threaded.c

@@ -0,0 +1,149 @@
+/*
+ * bh_thread.c - Top and bottom half interrupt handling
+ *
+ * Based upon the RPi example by Stefan Wendler (devnull@kaltpost.de)
+ * from:
+ *    https://github.com/wendlers/rpi-kmod-samples
+ *
+ * Press one button to turn on a LED and another to turn it off
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+static int button_irqs[] = { -1, -1 };
+
+/* Define GPIOs for LEDs.
+ * FIXME: Change the numbers for the GPIO on your board.
+ */
+static struct gpio leds[] = { { 4, GPIOF_OUT_INIT_LOW, "LED 1" } };
+
+/* Define GPIOs for BUTTONS
+ * FIXME: Change the numbers for the GPIO on your board.
+ */
+static struct gpio buttons[] = {
+    { 17, GPIOF_IN, "LED 1 ON BUTTON" },
+    { 18, GPIOF_IN, "LED 1 OFF BUTTON" },
+};
+
+/* This happens immediately, when the IRQ is triggered */
+static irqreturn_t button_top_half(int irq, void *ident)
+{
+    return IRQ_WAKE_THREAD;
+}
+
+/* This can happen at leisure, freeing up IRQs for other high priority task */
+static irqreturn_t button_bottom_half(int irq, void *ident)
+{
+    pr_info("Bottom half task starts\n");
+    mdelay(500); /* do something which takes a while */
+    pr_info("Bottom half task ends\n");
+    return IRQ_HANDLED;
+}
+
+static int __init bottomhalf_init(void)
+{
+    int ret = 0;
+
+    pr_info("%s\n", __func__);
+
+    /* register LED gpios */
+    ret = gpio_request_array(leds, ARRAY_SIZE(leds));
+
+    if (ret) {
+        pr_err("Unable to request GPIOs for LEDs: %d\n", ret);
+        return ret;
+    }
+
+    /* register BUTTON gpios */
+    ret = gpio_request_array(buttons, ARRAY_SIZE(buttons));
+
+    if (ret) {
+        pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret);
+        goto fail1;
+    }
+
+    pr_info("Current button1 value: %d\n", gpio_get_value(buttons[0].gpio));
+
+    ret = gpio_to_irq(buttons[0].gpio);
+
+    if (ret < 0) {
+        pr_err("Unable to request IRQ: %d\n", ret);
+        goto fail2;
+    }
+
+    button_irqs[0] = ret;
+
+    pr_info("Successfully requested BUTTON1 IRQ # %d\n", button_irqs[0]);
+
+    ret = request_threaded_irq(
+        gpio_to_irq(button_irqs[0]), button_top_half, button_bottom_half,
+        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpiomod#button1", NULL);
+
+    if (ret) {
+        pr_err("Unable to request IRQ: %d\n", ret);
+        goto fail2;
+    }
+
+    ret = gpio_to_irq(buttons[1].gpio);
+
+    if (ret < 0) {
+        pr_err("Unable to request IRQ: %d\n", ret);
+        goto fail2;
+    }
+
+    button_irqs[1] = ret;
+
+    pr_info("Successfully requested BUTTON2 IRQ # %d\n", button_irqs[1]);
+
+    ret = request_threaded_irq(
+        gpio_to_irq(button_irqs[1]), button_top_half, button_bottom_half,
+        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpiomod#button2", NULL);
+
+    if (ret) {
+        pr_err("Unable to request IRQ: %d\n", ret);
+        goto fail3;
+    }
+
+    return 0;
+
+/* cleanup what has been setup so far */
+fail3:
+    free_irq(button_irqs[0], NULL);
+
+fail2:
+    gpio_free_array(buttons, ARRAY_SIZE(leds));
+
+fail1:
+    gpio_free_array(leds, ARRAY_SIZE(leds));
+
+    return ret;
+}
+
+static void __exit bottomhalf_exit(void)
+{
+    int i;
+
+    pr_info("%s\n", __func__);
+
+    /* free irqs */
+    free_irq(button_irqs[0], NULL);
+    free_irq(button_irqs[1], NULL);
+
+    /* turn all LEDs off */
+    for (i = 0; i < ARRAY_SIZE(leds); i++)
+        gpio_set_value(leds[i].gpio, 0);
+
+    /* unregister */
+    gpio_free_array(leds, ARRAY_SIZE(leds));
+    gpio_free_array(buttons, ARRAY_SIZE(buttons));
+}
+
+module_init(bottomhalf_init);
+module_exit(bottomhalf_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Interrupt with top and bottom half");

+ 35 - 0
lkmpg.tex

@@ -1818,6 +1818,10 @@ There are two main ways of running tasks: tasklets and work queues.
 Tasklets are a quick and easy way of scheduling a single function to be run.
 For example, when triggered from an interrupt, whereas work queues are more complicated but also better suited to running multiple things in a sequence.
 
+It is possible that in future tasklets may be replaced by \textit{threaded irqs}.
+However, discussion about that has been ongoing since 2007 (\href{https://lwn.net/Articles/239633}{Eliminating tasklets}), so do not hold your breath.
+See the section \ref{sec:irq} if you wish to avoid the tasklet debate.
+
 \subsection{Tasklets}
 \label{sec:tasklet}
 Here is an example tasklet module.
@@ -1925,6 +1929,37 @@ The example below modifies the previous example to also run an additional task w
 
 \samplec{examples/bottomhalf.c}
 
+\subsection{Threaded IRQ}
+
+Threaded IRQ is a mechanism to handle both top-half and bottom-half of an IRQ at once.
+A threaded IRQ splits one handler into two: one for the top-half, the other for the bottom-half.
+Those two handlers are registered at once by \cpp|request_threaded_irq()|.
+
+The top-half handler runs in interrupt context.
+It's the equivalence of the handler passed to the \cpp|request_irq()|.
+The bottom-half handler on the other hand runs in its own thread.
+This thread is created on registration of a threaded IRQ. Its sole purpose is to run this bottom-half handler.
+This is where a threaded IRQ is ``threaded''.
+
+Whether the bottom-half handler will be invoked is determined by the return value of the top-half handler.
+If \cpp|IRQ_WAKE_THREAD| is returned, that bottom-half serving thread will wake up.
+The thread then runs the bottom-half handler.
+
+Here is an example of how to do the same thing as before, with top and bottom halves, but using threads.
+
+\samplec{examples/bh_threaded.c}
+
+\cpp|request_threaded_irq()| only takes one additional parameter than the \cpp|request_irq()| -- the bottom-half handling function that runs in its own thread.
+In this example it is the \cpp|button_bottom_half()|.
+Usage of other parameters are the same as \cpp|request_irq()|.
+
+Presence of both handlers is not mandatory.
+If either of them is not needed, pass the \cpp|NULL| instead.
+A \cpp|NULL| top-half handler implicitly means doing nothing but waking up the bottom-half serving thread; A \cpp|NULL| bottom-half handler will have the same effect as \cpp|request_irq()|.
+In fact, this is how \cpp|request_irq()| is implemented.
+
+Note that passing \cpp|NULL| as both handlers is considered an error and will make registration fail.
+
 \section{Virtual Input Device Driver}
 \label{sec:vinput}
 The input device driver is a module that provides a way to communicate with the interaction device via the event.