/* * 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 /* Macro DECLARE_TASKLET_OLD exists for compatibiity. * 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" }, }; /* Tasklet containing some non-trivial amount of processing */ static void bottomhalf_tasklet_fn(unsigned long data) { pr_info("Bottom half tasklet starts\n"); /* do something which takes a while */ mdelay(500); pr_info("Bottom half tasklet ends\n"); } static DECLARE_TASKLET_OLD(buttontask, bottomhalf_tasklet_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 */ tasklet_schedule(&buttontask); 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_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); 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_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); 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");