2015-11-20 23 views
3

我试图涉足低层次的编程。我的目标是让用户在终端中键入一个密钥,捕获并输出另一个密钥。例如,如果用户键入“a”,我会输入“b”,如果他输入“b”,我输出“c”等。Linux内核:如何捕获按键并将其替换为另一个键?

这样做的步骤是什么?我已经很熟悉如何访问Linux内核源代码,编译它并使用它。

谢谢。

+0

是使用X11用户,例如通过像Gnome,XFCE,KDE这样的桌面?您可能不需要更改内核代码(仅用于加载一些新的键盘映射) –

+2

我相信他为了教育目的正在这样做,试图更好地理解内核开发。 –

回答

8

考虑下一个简单的内核模块:

#include <linux/kernel.h> 
#include <linux/module.h> 
#include <linux/interrupt.h> 
#include <asm/io.h> 

#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 
#define KBD_STATUS_MASK  0x80 

static irqreturn_t kbd2_isr(int irq, void *dev_id) 
{ 
    char scancode; 

    scancode = inb(KBD_DATA_REG); 
    /* NOTE: i/o ops take a lot of time thus must be avoided in HW ISRs */ 
    pr_info("Scan Code %x %s\n", 
      scancode & KBD_SCANCODE_MASK, 
      scancode & KBD_STATUS_MASK ? "Released" : "Pressed"); 

    return IRQ_HANDLED; 
} 

static int __init kbd2_init(void) 
{ 
    return request_irq(KBD_IRQ, kbd2_isr, IRQF_SHARED, "kbd2", (void *)kbd2_isr); 
} 

static void __exit kbd2_exit(void) 
{ 
    free_irq(KBD_IRQ, (void *)kbd2_isr); 
} 

module_init(kbd2_init); 
module_exit(kbd2_exit); 

MODULE_LICENSE("GPL"); 

这是最起码的和原始key-logger。它可以很容易地重新替换scan code

免责声明

  • 这个模块是不是跨平台(将只在x86架构上,因为它使用inb()功能)
  • 我认为只适用于PS/2键盘的工作原理(将无法正常工作使用USB键盘)
  • 它在硬件IRQ处理程序中执行缓慢的I/O操作(我的意思是pr_info()),应该避免此操作(理想情况下应使用threaded IRQs))。

但我认为这是很好的教育目的 - 它真的很小,证明这个想法相当不错(不与API搞乱像input_devinput_register_device()serio_write()input_event()input_report_key()等)。

详细

真实的中断处理程序(在keyboard driver)要求为中断共享,这让我们也请求中断,因此处理它也是我们ISR(除ISR在原来的键盘驱动程序)。中断请求在kbd2_init()中完成。

该模块的工作原理如下:

  1. 渔获键按压事件(硬件中断处理程序kbd2_isr()被调用用于每个键按压事件)
  2. 读取的按键的扫描代码(通过inb()功能)
  3. 并通过pr_info()

打印它现在,您要替换该扫描代码。我相信你可以使用outb()这个函数(在x86上)。所以我把它留给你。

如果你想知道为什么我们用数字1请求IRQ,请参阅drivers/input/serio/i8042-io.h

#else 
# define I8042_KBD_IRQ 1 

此外,一定要检查该IRQ在drivers/input/serio/i8042.c共享:

error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED, 
        "i8042", i8042_platform_device); 

这里是文档对于i8042键盘控制器:AT keyboard controller

有用的常量

为了避免magic numbers,您可以使用下一个定义。

drivers/input/serio/i8042-io.h

/* 
* Register numbers. 
*/ 

#define I8042_COMMAND_REG  0x64 
#define I8042_STATUS_REG  0x64 
#define I8042_DATA_REG   0x60 

include/linux/i8042.h

/* 
* Status register bits. 
*/ 

#define I8042_STR_PARITY  0x80 
#define I8042_STR_TIMEOUT  0x40 
#define I8042_STR_AUXDATA  0x20 
#define I8042_STR_KEYLOCK  0x10 
#define I8042_STR_CMDDAT  0x08 
#define I8042_STR_MUXERR  0x04 
#define I8042_STR_IBF   0x02 
#define I8042_STR_OBF   0x01 
+0

非常感谢你,你让我走在正确的道路上。 – StackPWRequirmentsAreCrazy

+0

据我所知,您可以在IO端口上使用'readb()',在现代x86 CPU上使用相同的结果(好吧,现代就像从486开始?)。 – 0andriy

+0

@ 0andriy坦率地说,我只是用'drivers/input/serio/i8042-io.h'中的方法。无论如何,'readb()'比'inb()'有什么好处? –

相关问题