Skip to content

Commit 56f566a

Browse files
jservBob Mottram
andcommitted
Provide a tasklet-free example
Co-authored-by: Bob Mottram <[email protected]>
1 parent 3e472c8 commit 56f566a

File tree

4 files changed

+186
-0
lines changed

4 files changed

+186
-0
lines changed

.ci/non-working

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
bottomhalf
2+
bh_threaded
23
intrpt
34
vkbd

examples/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ obj-m += example_rwlock.o
2626
obj-m += example_atomic.o
2727
obj-m += example_mutex.o
2828
obj-m += bottomhalf.o
29+
obj-m += bh_threaded.o
2930
obj-m += ioctl.o
3031
obj-m += vinput.o
3132
obj-m += vkbd.o

examples/bh_threaded.c

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* bh_thread.c - Top and bottom half interrupt handling
3+
*
4+
* Based upon the RPi example by Stefan Wendler ([email protected])
5+
* from:
6+
* https://github.com/wendlers/rpi-kmod-samples
7+
*
8+
* Press one button to turn on a LED and another to turn it off
9+
*/
10+
11+
#include <linux/module.h>
12+
#include <linux/kernel.h>
13+
#include <linux/gpio.h>
14+
#include <linux/delay.h>
15+
#include <linux/interrupt.h>
16+
17+
static int button_irqs[] = { -1, -1 };
18+
19+
/* Define GPIOs for LEDs.
20+
* FIXME: Change the numbers for the GPIO on your board.
21+
*/
22+
static struct gpio leds[] = { { 4, GPIOF_OUT_INIT_LOW, "LED 1" } };
23+
24+
/* Define GPIOs for BUTTONS
25+
* FIXME: Change the numbers for the GPIO on your board.
26+
*/
27+
static struct gpio buttons[] = {
28+
{ 17, GPIOF_IN, "LED 1 ON BUTTON" },
29+
{ 18, GPIOF_IN, "LED 1 OFF BUTTON" },
30+
};
31+
32+
/* This happens immediately, when the IRQ is triggered */
33+
static irqreturn_t button_top_half(int irq, void *ident)
34+
{
35+
return IRQ_WAKE_THREAD;
36+
}
37+
38+
/* This can happen at leisure, freeing up IRQs for other high priority task */
39+
static irqreturn_t button_bottom_half(int irq, void *ident)
40+
{
41+
pr_info("Bottom half task starts\n");
42+
mdelay(500); /* do something which takes a while */
43+
pr_info("Bottom half task ends\n");
44+
return IRQ_HANDLED;
45+
}
46+
47+
static int __init bottomhalf_init(void)
48+
{
49+
int ret = 0;
50+
51+
pr_info("%s\n", __func__);
52+
53+
/* register LED gpios */
54+
ret = gpio_request_array(leds, ARRAY_SIZE(leds));
55+
56+
if (ret) {
57+
pr_err("Unable to request GPIOs for LEDs: %d\n", ret);
58+
return ret;
59+
}
60+
61+
/* register BUTTON gpios */
62+
ret = gpio_request_array(buttons, ARRAY_SIZE(buttons));
63+
64+
if (ret) {
65+
pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret);
66+
goto fail1;
67+
}
68+
69+
pr_info("Current button1 value: %d\n", gpio_get_value(buttons[0].gpio));
70+
71+
ret = gpio_to_irq(buttons[0].gpio);
72+
73+
if (ret < 0) {
74+
pr_err("Unable to request IRQ: %d\n", ret);
75+
goto fail2;
76+
}
77+
78+
button_irqs[0] = ret;
79+
80+
pr_info("Successfully requested BUTTON1 IRQ # %d\n", button_irqs[0]);
81+
82+
ret = request_threaded_irq(
83+
gpio_to_irq(button_irqs[0]), button_top_half, button_bottom_half,
84+
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpiomod#button1", NULL);
85+
86+
if (ret) {
87+
pr_err("Unable to request IRQ: %d\n", ret);
88+
goto fail2;
89+
}
90+
91+
ret = gpio_to_irq(buttons[1].gpio);
92+
93+
if (ret < 0) {
94+
pr_err("Unable to request IRQ: %d\n", ret);
95+
goto fail2;
96+
}
97+
98+
button_irqs[1] = ret;
99+
100+
pr_info("Successfully requested BUTTON2 IRQ # %d\n", button_irqs[1]);
101+
102+
ret = request_threaded_irq(
103+
gpio_to_irq(button_irqs[1]), button_top_half, button_bottom_half,
104+
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpiomod#button2", NULL);
105+
106+
if (ret) {
107+
pr_err("Unable to request IRQ: %d\n", ret);
108+
goto fail3;
109+
}
110+
111+
return 0;
112+
113+
/* cleanup what has been setup so far */
114+
fail3:
115+
free_irq(button_irqs[0], NULL);
116+
117+
fail2:
118+
gpio_free_array(buttons, ARRAY_SIZE(leds));
119+
120+
fail1:
121+
gpio_free_array(leds, ARRAY_SIZE(leds));
122+
123+
return ret;
124+
}
125+
126+
static void __exit bottomhalf_exit(void)
127+
{
128+
int i;
129+
130+
pr_info("%s\n", __func__);
131+
132+
/* free irqs */
133+
free_irq(button_irqs[0], NULL);
134+
free_irq(button_irqs[1], NULL);
135+
136+
/* turn all LEDs off */
137+
for (i = 0; i < ARRAY_SIZE(leds); i++)
138+
gpio_set_value(leds[i].gpio, 0);
139+
140+
/* unregister */
141+
gpio_free_array(leds, ARRAY_SIZE(leds));
142+
gpio_free_array(buttons, ARRAY_SIZE(buttons));
143+
}
144+
145+
module_init(bottomhalf_init);
146+
module_exit(bottomhalf_exit);
147+
148+
MODULE_LICENSE("GPL");
149+
MODULE_DESCRIPTION("Interrupt with top and bottom half");

lkmpg.tex

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1818,6 +1818,10 @@ \section{Scheduling Tasks}
18181818
Tasklets are a quick and easy way of scheduling a single function to be run.
18191819
For example, when triggered from an interrupt, whereas work queues are more complicated but also better suited to running multiple things in a sequence.
18201820

1821+
It is possible that in future tasklets may be replaced by \textit{threaded irqs}.
1822+
However, discussion about that has been ongoing since 2007 (\href{https://lwn.net/Articles/239633}{Eliminating tasklets}), so do not hold your breath.
1823+
See the section \ref{sec:irq} if you wish to avoid the tasklet debate.
1824+
18211825
\subsection{Tasklets}
18221826
\label{sec:tasklet}
18231827
Here is an example tasklet module.
@@ -1925,6 +1929,37 @@ \subsection{Bottom Half}
19251929

19261930
\samplec{examples/bottomhalf.c}
19271931

1932+
\subsection{Threaded IRQ}
1933+
1934+
Threaded IRQ is a mechanism to handle both top-half and bottom-half of an IRQ at once.
1935+
A threaded IRQ splits one handler into two: one for the top-half, the other for the bottom-half.
1936+
Those two handlers are registered at once by \cpp|request_threaded_irq()|.
1937+
1938+
The top-half handler runs in interrupt context.
1939+
It's the equivalence of the handler passed to the \cpp|request_irq()|.
1940+
The bottom-half handler on the other hand runs in its own thread.
1941+
This thread is created on registration of a threaded IRQ. Its sole purpose is to run this bottom-half handler.
1942+
This is where a threaded IRQ is ``threaded''.
1943+
1944+
Whether the bottom-half handler will be invoked is determined by the return value of the top-half handler.
1945+
If \cpp|IRQ_WAKE_THREAD| is returned, that bottom-half serving thread will wake up.
1946+
The thread then runs the bottom-half handler.
1947+
1948+
Here is an example of how to do the same thing as before, with top and bottom halves, but using threads.
1949+
1950+
\samplec{examples/bh_threaded.c}
1951+
1952+
\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.
1953+
In this example it is the \cpp|button_bottom_half()|.
1954+
Usage of other parameters are the same as \cpp|request_irq()|.
1955+
1956+
Presence of both handlers is not mandatory.
1957+
If either of them is not needed, pass the \cpp|NULL| instead.
1958+
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()|.
1959+
In fact, this is how \cpp|request_irq()| is implemented.
1960+
1961+
Note that passing \cpp|NULL| as both handlers is considered an error and will make registration fail.
1962+
19281963
\section{Virtual Input Device Driver}
19291964
\label{sec:vinput}
19301965
The input device driver is a module that provides a way to communicate with the interaction device via the event.

0 commit comments

Comments
 (0)