/* * 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 #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0) #define NO_GPIO_REQUEST_ARRAY #endif 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 */ #ifdef NO_GPIO_REQUEST_ARRAY ret = gpio_request(leds[0].gpio, leds[0].label); #else ret = gpio_request_array(leds, ARRAY_SIZE(leds)); #endif if (ret) { pr_err("Unable to request GPIOs for LEDs: %d\n", ret); return ret; } /* register BUTTON gpios */ #ifdef NO_GPIO_REQUEST_ARRAY ret = gpio_request(buttons[0].gpio, buttons[0].label); if (ret) { pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret); goto fail1; } ret = gpio_request(buttons[1].gpio, buttons[1].label); if (ret) { pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret); goto fail2; } #else ret = gpio_request_array(buttons, ARRAY_SIZE(buttons)); if (ret) { pr_err("Unable to request GPIOs for BUTTONs: %d\n", ret); goto fail1; } #endif 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); #ifdef NO_GPIO_REQUEST_ARRAY goto fail3; #else goto fail2; #endif } button_irqs[0] = ret; pr_info("Successfully requested BUTTON1 IRQ # %d\n", button_irqs[0]); ret = request_threaded_irq(button_irqs[0], button_top_half, button_bottom_half, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpiomod#button1", &buttons[0]); if (ret) { pr_err("Unable to request IRQ: %d\n", ret); #ifdef NO_GPIO_REQUEST_ARRAY goto fail3; #else goto fail2; #endif } ret = gpio_to_irq(buttons[1].gpio); if (ret < 0) { pr_err("Unable to request IRQ: %d\n", ret); #ifdef NO_GPIO_REQUEST_ARRAY goto fail3; #else goto fail2; #endif } button_irqs[1] = ret; pr_info("Successfully requested BUTTON2 IRQ # %d\n", button_irqs[1]); ret = request_threaded_irq(button_irqs[1], button_top_half, button_bottom_half, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpiomod#button2", &buttons[1]); if (ret) { pr_err("Unable to request IRQ: %d\n", ret); #ifdef NO_GPIO_REQUEST_ARRAY goto fail4; #else goto fail3; #endif } return 0; /* cleanup what has been setup so far */ #ifdef NO_GPIO_REQUEST_ARRAY fail4: free_irq(button_irqs[0], NULL); fail3: gpio_free(buttons[1].gpio); fail2: gpio_free(buttons[0].gpio); fail1: gpio_free(leds[0].gpio); #else fail3: free_irq(button_irqs[0], NULL); fail2: gpio_free_array(buttons, ARRAY_SIZE(leds)); fail1: gpio_free_array(leds, ARRAY_SIZE(leds)); #endif return ret; } static void __exit bottomhalf_exit(void) { pr_info("%s\n", __func__); /* free irqs */ free_irq(button_irqs[0], NULL); free_irq(button_irqs[1], NULL); /* turn all LEDs off */ #ifdef NO_GPIO_REQUEST_ARRAY gpio_set_value(leds[0].gpio, 0); #else int i; for (i = 0; i < ARRAY_SIZE(leds); i++) gpio_set_value(leds[i].gpio, 0); #endif /* unregister */ #ifdef NO_GPIO_REQUEST_ARRAY gpio_free(leds[0].gpio); gpio_free(buttons[0].gpio); gpio_free(buttons[1].gpio); #else gpio_free_array(leds, ARRAY_SIZE(leds)); gpio_free_array(buttons, ARRAY_SIZE(buttons)); #endif } module_init(bottomhalf_init); module_exit(bottomhalf_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Interrupt with top and bottom half");