mirror of
https://github.com/sysprog21/lkmpg.git
synced 2024-11-22 04:09:18 +08:00
Provide a tasklet-free example
Co-authored-by: Bob Mottram <bob@freedombone.net>
This commit is contained in:
parent
3e472c84fd
commit
56f566abe6
|
@ -1,3 +1,4 @@
|
||||||
bottomhalf
|
bottomhalf
|
||||||
|
bh_threaded
|
||||||
intrpt
|
intrpt
|
||||||
vkbd
|
vkbd
|
||||||
|
|
|
@ -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
149
examples/bh_threaded.c
Normal 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");
|
35
lkmpg.tex
35
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.
|
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.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user