考虑下一个简单的内核模块:
#include <linux/kernel.h>#include <linux/module.h>#include <linux/interrupt.h>#include <asm/io.h>#define KBD_IRQ 1 #define KBD_DATA_REG 0x60 #define KBD_SCANCODE_MASK 0x7f#define KBD_STATUS_MASK 0x80static irqreturn_t kbd2_isr(int irq, void *dev_id){ char scanpre; scanpre = inb(KBD_DATA_REG); pr_info("Scan Code %x %sn", scanpre & KBD_SCANCODE_MASK, scanpre & 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");这是最小的和原始的键记录器。可以很容易地将其重新制作以替换扫描代码。
免责声明
- 该模块不是跨平台的(仅在x86架构上可用,因为它正在使用
inb()
功能) - 我相信它仅适用于PS / 2键盘(不适用于USB键盘)
- 它
pr_info()
在硬件IRQ处理程序中执行缓慢的I / O操作(我的意思是),应避免这种情况(应使用理想的线程IRQ)。
但我认为这是很好的教育目的-它真的很小,证明这个想法相当不错(不与API搞乱像
input_dev,
input_register_device(),
serio_write(),
input_event(),
input_report_key(),等)。
细节
实际的中断处理程序(在键盘驱动程序中)请求为 共享中断
,这使我们还可以请求该中断,从而在我们的ISR中(除了原始键盘驱动程序中的ISR之外)也可以处理该中断。中断请求在中完成
kbd2_init()。
该模块的工作原理如下:
- 捕获按键事件(
kbd2_isr()
每个按键事件都会调用硬件中断处理程序) - 读取按键扫描代码(通过
inb()
功能) - 并通过打印
pr_info()
现在,您要替换该扫描代码。我相信您可以
outb()为此使用功能(在x86上)。所以我留给你。
如果您想知道为什么我们请求编号为1的IRQ,请参阅drivers / input / serio /
i8042-io.h:
#else# define I8042_KBD_IRQ 1
另外,还要确保在驱动程序/input/serio/i8042.c中共享此IRQ :
error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED, "i8042", i8042_platform_device);
这是i8042键盘控制器的文档:AT键盘控制器。
有用的常数
为了避免幻数,可以使用下一个定义。
从driver / input / serio / i8042-io.h:
#define I8042_COMMAND_REG 0x64#define I8042_STATUS_REG 0x64#define I8042_DATA_REG 0x60
从include / linux / i8042.h:
#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_IBF0x02#define I8042_STR_OBF0x01



