/* * bottomhalf.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 an LED and another to turn it off */ #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0) #define NO_GPIO_REQUEST_ARRAY #endif /* Macro DECLARE_TASKLET_OLD exists for compatibility. * See https://lwn.net/Articles/830964/ */ #ifndef DECLARE_TASKLET_OLD #define DECLARE_TASKLET_OLD(arg1, arg2) DECLARE_TASKLET(arg1, arg2, 0L) #endif static int button_irqs[] = { -1, -1 }; /* Define GPIOs for LEDs. * TODO: Change the numbers for the GPIO on your board. */ static struct gpio leds[] = { { 4, GPIOF_OUT_INIT_LOW, "LED 1" } }; /* Define GPIOs for BUTTONS * TODO: 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" }, }; /* Workqueue function containing some non-trivial amount of processing */ static void bottomhalf_work_fn(struct work_struct *work) { pr_info("Bottom half workqueue starts\n"); /* do something which takes a while */ msleep(500); pr_info("Bottom half workqueue ends\n"); } static DECLARE_WORK(bottomhalf_work, bottomhalf_work_fn); /* interrupt function triggered when a button is pressed */ static irqreturn_t button_isr(int irq, void *data) { /* Do something quickly right now */ if (irq == button_irqs[0] && !gpio_get_value(leds[0].gpio)) gpio_set_value(leds[0].gpio, 1); else if (irq == button_irqs[1] && gpio_get_value(leds[0].gpio)) gpio_set_value(leds[0].gpio, 0); /* Do the rest at leisure via the scheduler */ schedule_work(&bottomhalf_work); 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_irq(button_irqs[0], button_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpiomod#button1", NULL); 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_irq(button_irqs[1], button_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpiomod#button2", NULL); 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");