diff --git a/.ci/non-working b/.ci/non-working index 1826ab6..1d49cae 100644 --- a/.ci/non-working +++ b/.ci/non-working @@ -1,3 +1,4 @@ bottomhalf +bh_threaded intrpt vkbd diff --git a/examples/Makefile b/examples/Makefile index 2e0dfb9..01ab18b 100644 --- a/examples/Makefile +++ b/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 diff --git a/examples/bh_threaded.c b/examples/bh_threaded.c new file mode 100644 index 0000000..8c00236 --- /dev/null +++ b/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 +#include +#include +#include +#include + +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"); diff --git a/lkmpg.tex b/lkmpg.tex index 955c3cd..d486252 100644 --- a/lkmpg.tex +++ b/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.