Search code examples
clinux-kernelraspbianraspberry-pi3gpio

How to include three IRQ handlers in one kernel module?


I'm currently learning from Derek Molloy's example in his book "Exploring Raspberry Pi - Interfacing to The Real World with Embedded Linux". I took the example at Listing 16-3, which unfortunately I could not found online.

The example contains a kernel module code for a single interrupt. It read a signal from a button at GPIO 17 and then send an interrupt to turn on an LED at GPIO 27. The book uses the default GPIO pin numbering, so I do the same.

What I want to do is modifying the code to include 2 other button-LED pairs. I want to do it like this:

  • GPIO 17 turns on/off GPIO 23
  • GPIO 27 turns on/off GPIO 24
  • GPIO 22 turns on/off GPIO 25

This is the modified code I use.

static unsigned int gpioDevice1 = 17;
static unsigned int gpioDevice2 = 27;
static unsigned int gpioDevice3 = 22;
static unsigned int gpioButton1 = 24; 
static unsigned int gpioButton2 = 23;
static unsigned int gpioButton3 = 25;
static unsigned int irqNumber1On; 
static unsigned int irqNumber2On; 
static unsigned int irqNumber3On;
static unsigned int buttonCounter1 = 0; 
static unsigned int buttonCounter2 = 0; 
static unsigned int buttonCounter3 = 0; 
static unsigned int totalCounter = 0; 
static bool devOn1 = 0; // Initial state of devices
static bool devOn2 = 0;
static bool devOn3 = 0;

// prototype for the custom IRQ handler function, function below. Should I use IRQF_SHARED here?

static irq_handler_t rpi3_gpio_irq_handler_1(unsigned int irq, void *dev_id, struct pt_regs *regs); 
static irq_handler_t rpi3_gpio_irq_handler_2(unsigned int irq, void *dev_id, struct pt_regs *regs); 
static irq_handler_t rpi3_gpio_irq_handler_3(unsigned int irq, void *dev_id, struct pt_regs *regs);

/** LKM initialization function */

static int __init rpi3_gpio_init(void) {
    int result1On = 0;
    int result2On = 0;
    int result3On = 0;
    printk(KERN_INFO "GPIO_TEST: Initializing the GPIO_TEST LKM\n");

    /* GPIO validation on the three devices */
    if (!gpio_is_valid(gpioDevice1) || !gpio_is_valid(gpioDevice2) || !gpio_is_valid(gpioDevice3)) {
        printk(KERN_INFO "GPIO_TEST: invalid GPIO for Devices\n");
        return -ENODEV; //wouldn't using ENXIO is more appropriate than ENODEV?
    }

    /* Configuring GPIO pins for the pairs */
    gpio_request(gpioDevice1, "sysfs"); // request LED GPIO
    gpio_direction_output(gpioDevice1, devOn1); // set in output mode
    gpio_export(gpioDevice1, false); // appears in /sys/class/gpio
    // false prevents in/out change

    gpio_request(gpioDevice2, "sysfs");
    gpio_direction_output(gpioDevice2, devOn2);
    gpio_export(gpioDevice2, false); 

    gpio_request(gpioDevice3, "sysfs"); 
    gpio_direction_output(gpioDevice3, devOn3); 
    gpio_export(gpioDevice3, false); 

    gpio_request(gpioButton1, "sysfs"); // set up gpioButton1
    gpio_direction_input(gpioButton1); // set up as input
    gpio_set_debounce(gpioButton1, 200); // debounce delay of 200ms to avoid erratic and uncontrolled interrupt
    gpio_export(gpioButton1, false); // appears in /sys/class/gpio

    gpio_request(gpioButton2, "sysfs");
    gpio_direction_input(gpioButton2);
    gpio_set_debounce(gpioButton2, 200);
    gpio_export(gpioButton2, false);

    gpio_request(gpioButton3, "sysfs");
    gpio_direction_input(gpioButton3);
    gpio_set_debounce(gpioButton3, 200);
    gpio_export(gpioButton3, false);

    printk(KERN_INFO "GPIO_TEST: button1 value is currently: %d\n", gpio_get_value(gpioButton1));
    irqNumber1On = gpio_to_irq(gpioButton1); // map GPIO to IRQ number 189?
    printk(KERN_INFO "GPIO_TEST: button1 mapped to IRQ: %d\n", irqNumber1On);

    printk(KERN_INFO "GPIO_TEST: button2 value is currently: %d\n", gpio_get_value(gpioButton2));
    irqNumber2On = gpio_to_irq(gpioButton2); // map GPIO to IRQ number 190?
    printk(KERN_INFO "GPIO_TEST: button2 mapped to IRQ: %d\n", irqNumber2On);

    printk(KERN_INFO "GPIO_TEST: button3 value is currently: %d\n", gpio_get_value(gpioButton3));
    irqNumber3On = gpio_to_irq(gpioButton3); // map GPIO to IRQ number 191?
    printk(KERN_INFO "GPIO_TEST: button3 mapped to IRQ: %d\n", irqNumber3On);

    /* Interrupt lines when tactile button is pressed */
    result1On = request_irq(irqNumber1On, // interrupt number requested
            (irq_handler_t) rpi3_gpio_irq_handler_1, // handler function
            // TO DO: Insert IRQF_SHARED here?
            IRQF_TRIGGER_RISING, // on rising edge (press, not release)
            "rpi3_gpio_handler", // used in /proc/interrupts
            NULL); // *dev_id for shared interrupt lines shouldn't be NULL
    printk(KERN_INFO "GPIO_TEST: IRQ request result for device 1 is: %d\n", result1On);
    return result1On;

    result2On = request_irq(irqNumber2On,
            (irq_handler_t) rpi3_gpio_irq_handler_2,
            IRQF_TRIGGER_RISING,
            "rpi3_gpio_handler",
            NULL); 
    printk(KERN_INFO "GPIO_TEST: IRQ request result for device 2 is: %d\n", result2On);
    return result2On;

    result3On = request_irq(irqNumber3On,
            (irq_handler_t) rpi3_gpio_irq_handler_3,
            IRQF_TRIGGER_RISING,
            "rpi3_gpio_handler",
            NULL); 
    printk(KERN_INFO "GPIO_TEST: IRQ request result for device 3 is: %d\n", result3On);
    return result3On;
}

static void __exit rpi3_gpio_exit(void) {

    printk(KERN_INFO "GPIO_TEST: button 1 value is currently: %d\n", gpio_get_value(gpioButton1));
    printk(KERN_INFO "GPIO_TEST: button 1 was pressed %d times\n", buttonCounter1);
    printk(KERN_INFO "GPIO_TEST: button 2 value is currently: %d\n", gpio_get_value(gpioButton2));
    printk(KERN_INFO "GPIO_TEST: button 2 was pressed %d times\n", buttonCounter2);
    printk(KERN_INFO "GPIO_TEST: button 3 value is currently: %d\n", gpio_get_value(gpioButton3));
    printk(KERN_INFO "GPIO_TEST: button 3 was pressed %d times\n", buttonCounter3);
    printk(KERN_INFO "GPIO_TEST: in total the buttons was pressed %d times\n", totalCounter);

    gpio_set_value(gpioDevice1, 0); // turn the LED off
    gpio_unexport(gpioDevice1); // unexport the LED GPIO
    free_irq(irqNumber1On, NULL); // free the IRQ number, no *dev_id?
    gpio_unexport(gpioButton1); // unexport the Button GPIO
    gpio_free(gpioDevice1); // free the LED GPIO
    gpio_free(gpioButton1); // free the Button GPIO

    gpio_set_value(gpioDevice2, 0);
    gpio_unexport(gpioDevice2);
    free_irq(irqNumber2On, NULL);
    gpio_unexport(gpioButton2);
    gpio_free(gpioDevice2);
    gpio_free(gpioButton2);

    gpio_set_value(gpioDevice3, 0);
    gpio_unexport(gpioDevice3);
    free_irq(irqNumber3On, NULL);
    gpio_unexport(gpioButton3);
    gpio_free(gpioDevice3);
    gpio_free(gpioButton3);

    printk(KERN_INFO "GPIO_TEST: Goodbye from the LKM!\n");
}

/** GPIO IRQ Handler functions */

static irq_handler_t rpi3_gpio_irq_handler_1(unsigned int irq, void *dev_id, struct pt_regs *regs) {
    devOn1 = !devOn1; // invert the LED state
    gpio_set_value(gpioDevice1, devOn1); // set LED accordingly
    printk(KERN_INFO "GPIO_TEST: Interrupt! (button 1 is %d)\n",
    gpio_get_value(gpioButton1));
    buttonCounter1++;
    totalCounter++; // global counter
    return (irq_handler_t) IRQ_HANDLED; // announce IRQ handled
}

static irq_handler_t rpi3_gpio_irq_handler_2(unsigned int irq, void *dev_id, struct pt_regs *regs) {
    devOn2 = !devOn2;
    gpio_set_value(gpioDevice2, devOn2);
    printk(KERN_INFO "GPIO_TEST: Interrupt! (button 2 is %d)\n",
    gpio_get_value(gpioButton2));
    buttonCounter2++;
    totalCounter++; 
    return (irq_handler_t) IRQ_HANDLED;
}

static irq_handler_t rpi3_gpio_irq_handler_3(unsigned int irq, void *dev_id, struct pt_regs *regs) {
    devOn3 = !devOn3;
    gpio_set_value(gpioDevice3, devOn3);
    printk(KERN_INFO "GPIO_TEST: Interrupt! (button 3 is %d)\n",
    gpio_get_value(gpioButton3));
    buttonCounter3++;
    totalCounter++; 
    return (irq_handler_t) IRQ_HANDLED;
}

module_init(rpi3_gpio_init);
module_exit(rpi3_gpio_exit);

To get the IRQ numbers I use gpio_to_irq() like in the example, because I don't know what values would be a valid number.

The first pair worked well, but the other pairs won't work no matter how many times I pushed the buttons. When I checked the IRQ numbers with cat /proc/interrupts I got this display in the terminal. I've inverted the colors to make it easier to skim through

It seems that only the first one get an IRQ number, which is 189. Hypothetically, the other two should probably get 190 and 191, but they are not there.

The printk() functions also only displayed the lines for irqnumber1On, while the lines for irqnumber2On and irqnumber3On did not appear.

I gave the interrupts NULL dev_id because I don't know how to give/read ID for buttons. I tried random number combinations, like 500, 505, and 550 but terminal said warning: passing argument 5 of 'request_irq' makes pointer from integer without a cast.

So, what did I do terribly wrong here? I'm stuck on this for quite a while. Should I try using IRQF_SHARED? But it needs particular dev_id for each interrupt (or buttons in this case). My newbie mind think it is quite impossible to do so.

PS: I know the code looks messy and horrible, but please bear with me.

PPS: I could erase some part of the code if deemed necessary.


Solution

  • any code after this statement:

    return result1On; 
    

    will never be executed.

    That means the other two button interrupts will never be handled