0

I want to create a kernel module that registers an interrupt handler for the keyboard. The interrupt handler will output custom scancodes instead of the ones the user actually typed. It doesn't work - after freeing the default handler, the output to the terminal is new line, instead of my scancodes. Another strange thing, is that I checked with inb() and the scancode from the keyboard is always '7a' with status "Released".
So I have two questions:

  1. What causes this strange behaviour?
  2. Why my scancodes are not printed?

Thanks in advance :)

Here is my code:

#include <linux/module.h>       /* Needed by all modules */
#include <linux/kernel.h>       /* Needed for KERN_INFO */
#include <linux/init.h>         /* Needed for the macros */
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <asm/io.h>

MODULE_LICENSE("GPL");

#define KBD_IRQ             1       /* IRQ number for keyboard (i8042) */
#define KBD_DATA_REG        0x60    /* I/O port for keyboard data */
#define KBD_SCANCODE_MASK   0x7f

static int current_index = 0;
static int text_len = 2;
static int scancodes[2] = {0x15, 0x1e};

static irqreturn_t keyboard_intr_handler(int irq, void *dev)
{
    printk(KERN_INFO "in interrupt handler\n");
    char scancode = scancodes[current_index];
    printk(KERN_INFO "got scancode %x\n", scancode);
    current_index++;
    if (text_len == current_index)
        current_index = 0;
    printk(KERN_INFO "gonna output scancode\n");
    outb(KBD_DATA_REG, scancode);
    printk(KERN_INFO "outputed scancode\n");
    return IRQ_HANDLED;
}

static int __init my_register_interrupt(void)
{
    struct irq_desc* desc = irq_to_desc(1);                                  
    struct irqaction *action, **action_ptr;                                  
    action_ptr = &desc->action;                                              
    action = *action_ptr;                                                    
    free_irq(1, action->dev_id); 

    printk(KERN_INFO "in init\n");
    if (request_irq(KBD_IRQ, keyboard_intr_handler, IRQF_SHARED,
        "i8042", (void*) keyboard_intr_handler)) {
        printk(KERN_ERR "my_device: cannot register IRQ 1\n");
        return -EIO;
    }
    return 0;
}

static void __exit my_unregister_interrupt(void)
{
    free_irq(1, (void*) keyboard_intr_handler);
}

module_init(my_register_interrupt);
module_exit(my_unregister_interrupt);

Thanks in advance!

  • You have two interrupt handlers running in parallel, so, what do you expect? You need to unregister real handler and call it from your handler instead. – 0andriy Mar 19 '20 at 18:51
  • @0andriy Can you please show me how to do it? – Gili Jacobi Mar 21 '20 at 18:07
  • On SO it were several questions similar to yours, try to spend some time for homework. Sorry, I can't be more helpful here... – 0andriy Mar 21 '20 at 18:20
  • @0andriy I saw in the internet that in order to unregister the real handler I need to call `free_irq(1, NULL)`. However, when I do it, I get an error message says that I cant free an already free IRQ. That's why I asked you if you can show me how to do it... Any help will be appreciated. – Gili Jacobi Mar 21 '20 at 19:13
  • You should convert IRQ hardware number to Linux IRQ. `free_irq()` takes Linux number, and not hardware one. – 0andriy Mar 21 '20 at 19:25
  • @0andriy I updated the question and my code. Can you please take a look? Thanks in advance :) – Gili Jacobi Mar 22 '20 at 10:57
  • 1
    @Sam Protsenko I saw you answered a similair question (https://stackoverflow.com/questions/33836541/linux-kernel-how-to-capture-a-key-press-and-replace-it-with-another-key). Can you please take a look at my question? I couldn't find anything about it on the interent... – Gili Jacobi Mar 23 '20 at 11:02
  • @SamProtsenko I saw you answered a similair question (https://stackoverflow.com/questions/33836541/linux-kernel-how-to-capture-a-key-press-and-replace-it-with-another-key). Can you please take a look at my question? I couldn't find anything about it on the interent.. – Gili Jacobi Apr 21 '20 at 14:22

0 Answers0