2015-11-20 51 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()'有什麼好處? –

相關問題