Provide a tasklet-free example

Co-authored-by: Bob Mottram <bob@freedombone.net>
This commit is contained in:
Jim Huang 2021-09-25 11:52:03 +08:00 committed by Jim Huang
parent 3e472c84fd
commit 56f566abe6
4 changed files with 186 additions and 0 deletions

View File

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

View File

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

149
examples/bh_threaded.c Normal file
View File

@ -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");

View File

@ -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. 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. 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} \subsection{Tasklets}
\label{sec:tasklet} \label{sec:tasklet}
Here is an example tasklet module. 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} \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} \section{Virtual Input Device Driver}
\label{sec:vinput} \label{sec:vinput}
The input device driver is a module that provides a way to communicate with the interaction device via the event. The input device driver is a module that provides a way to communicate with the interaction device via the event.