Pages

2010-08-26

Linux: Interrupts in the Linux kernel

In this post, we will see how to deal with interrupts when writing device drivers for Linux.

First we should include the header file <linux/interrupt.h>

To deal with interrupts we use two functions:

 int request_irq(unsigned int irq, 
                         irqreturn_t (*handler)(int irq, void *dev_id), 
                         unsigned long flags, 
                         const char *dev_name,
                         void *dev_id);


void free_irq(unsigned int irq, void *dev_id);

The first function will return 0 if it succeed, or a negative value for fail.
  • irq: it's the IRQ number
  • handler: is the function that the kernel will call to deal with the requested IRQ
  • flags: SA_INTERRUPT for fast interrupt handler, SA_SHIRQ for shared interrupt handler, IRQF_DISABLED flag specifies that this interrupt handler has to be treated as a fast handler, so the kernel has to disable interrupts while invoking the handler, IRQF_TRIGGER_RISING announces a rising-edge interrupt, IRQF_TRIGGER_HIGH for level-sensitive interrupt
  • dev_name: this string will be visible form /proc/interrupts
  • dev_id: if the interrupt is not shared, it can be set to NULL 
Example: Interrupt handler for PC Keyboard (IRQ number 1)
int init_module()
{
    /*
     * Since the keyboard handler won't co-exist with another handler,
     * such as us, we have to disable it (free its IRQ) before we do
     * anything.  Since we don't know where it is, there's no way to
     * reinstate it later - so the computer will have to be rebooted
     * when we're done.
     */
    free_irq(1, NULL);

    /*
     * Request IRQ 1, the keyboard IRQ, to go to our irq_handler.
     * SA_SHIRQ means we're willing to have othe handlers on this IRQ.
     * SA_INTERRUPT can be used to make the handler into a fast interrupt.
     */
    return request_irq(1,             /* The number of the keyboard IRQ on PCs */
                       irq_handler,   /* our handler */
                       SA_SHIRQ,      

                       "keyboard_irq_handler",
                       (void *)(irq_handler));
}


void cleanup_module()
{
    /*
     * This is only here for completeness. It's totally irrelevant, since
     * we don't have a way to restore the normal keyboard interrupt so the
     * computer is completely useless and has to be rebooted.
     */
    free_irq(1, NULL);
}


/*
 * This function services keyboard interrupts. It reads the relevant
 * information from the keyboard.
 */
irqreturn_t irq_handler(int irq, void *dev_id)
{
    unsigned char scancode;
    unsigned char status;

    /*
     * Read keyboard status
     */
    status = inb(0x64);
    scancode = inb(0x60);
    printk(KERN_INFO "Scan Code %x %s.\n", 
                      (int)*((char *)scancode) & 0x7F,
                      *((char *)scancode) & 0x80 ? "Released" : "Pressed");
    return IRQ_HANDLED;
}


Now to enable/disable interrupts, we use respectively enable_irq(unsigned int irq_number) and disable_irq(unisigned int irq_number).

    No comments:

    Post a Comment