Linux 内核:如何捕获按键按下并将其替换为另一个键? [英] Linux Kernel: How to capture a key press and replace it with another key?

查看:16
本文介绍了Linux 内核:如何捕获按键按下并将其替换为另一个键?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试涉足低级编程.我的目标是让用户在终端中键入一个键,捕获它并输出另一个键.例如,如果用户输入a",我将输入b",如果他输入b",我将输出c",等等.

I'm attempting to dabble with low level programming. My goal is to have a user type a key into the terminal, capture that and output another key. So for example, if the user types "a" I would type out "b", if he types in "b" I output "c", etc.

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

What would be the steps to do that? I'm already familiar with how to access the Linux kernel source code, compile it, and use it.

谢谢.

推荐答案

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

Consider next simple kernel module:

#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
",
            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.它可以很容易地重新设计以替换扫描代码.

This is most minimal and primitive key-logger. It can be easily reworked for replacing of scan code.

  • 此模块不是跨平台的(仅适用于 x86 架构,因为它使用了 inb() 函数)
  • 我相信它只适用于 PS/2 键盘(不适用于 USB 键盘)
  • 它在硬件 IRQ 处理程序中执行缓慢的 I/O 操作(我的意思是 pr_info()),应该避免这种情况(理想情况下 应该使用线程 IRQ)).
  • This module is not cross-platform (will work only on x86 architecture, because it's using inb() function)
  • I believe it only works with PS/2 keyboard (won't work with USB keyboard)
  • It's performing slow I/O operation (I mean pr_info()) in hardware IRQ handler, which should be avoided (ideally threaded IRQs should be used)).

但我认为它有利于教育目的——它真的很小并且很好地展示了这个想法(没有混淆像 input_devinput_register_device()serio_write()input_event()input_report_key() 等).

But I think it's good for educational purposes -- it's really small and demonstrates the idea pretty well (without messing with API like input_dev, input_register_device(), serio_write(), input_event(), input_report_key(), etc).

真正的中断处理程序(在键盘驱动程序) 作为共享中断请求,这允许我们也请求该中断,从而也在我们的ISR(除了原始键盘驱动程序中的 ISR).中断请求在kbd2_init()中完成.

Real interrupt handler (in keyboard driver) requested as shared interrupt, which allows us also request that interrupt and thus handle it also in our ISR (in addition to ISR in original keyboard driver). Interrupt requesting is done in kbd2_init().

这个模块的工作原理如下:

This module works as follows:

  1. 捕获按键事件(硬件中断处理程序kbd2_isr() 为每个按键事件调用)
  2. 读取按键的扫描码(通过inb()函数)
  3. 并通过pr_info()
  4. 打印出来
  1. Catches key press event (hardware interrupt handler kbd2_isr() is called for each key press event)
  2. Reads scan code of pressed key (via inb() function)
  3. And prints it via pr_info()

现在,您要替换该扫描码.我相信您可以为此使用 outb() 函数(在 x86 上).所以我把它留给你.

Now, you want to replace that scan code. I believe you can use outb() function for this (on x86). So I leave it for you.

如果您想知道我们为什么要请求编号为 1 的 IRQ,请参阅 drivers/input/serio/i8042-io.h:

If you wonder why we are requesting IRQ with number 1, see at drivers/input/serio/i8042-io.h:

#else
# define I8042_KBD_IRQ  1

还要确保检查此 IRQ 是否在 驱动程序/输入/serio/i8042.c:

Also be sure to check that this IRQ is shared in drivers/input/serio/i8042.c:

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

这里是 i8042 键盘控制器的文档:AT 键盘控制器.

Here is documentation for i8042 keyboard controller: AT keyboard controller.

为避免幻数,您可以使用下一个定义.

To avoid magic numbers, you can use next definitions.

来自 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

这篇关于Linux 内核:如何捕获按键按下并将其替换为另一个键?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆