Предупреждение: Остальная часть этой главы полностью специфическая для Intel. Если вы не запускаете код на платформе Intel, он не будет работать.
Я имел проблему с написанием типового кода для этой главы. С одной стороны, для примера, чтобы быть полезным он должен выполняться на любом компьютере со значимыми результатами. С другой стороны, ядро уже включает драйверы устройства для всех общих устройств, и те драйверы устройства не будут сосуществовать с тем, что я собираюсь писать. Решение, которое я нашел состояло в том, чтобы написать обработчик для прерывания клавиатуры и сначала отключать стандартную программу обработки прерывания клавиатуры. Так как это определено как static в исходных файлах ядра (в файле drivers/char/keyboard.c), нет никакого способа восстановить обработчик.
Этот код связывает себя с IRQ 1, который является IRQ клавиатуры, управляемой в архитектуре Intel. При получении прерывания от клавиатуры, он читает состояние клавиатуры (inb(0x64)) и скэн-кода, который является значением, возвращенным клавиатурой. Затем, как только ядро думает, что это выполнимо, выполняется got_char, который дает код используемой клавиши (первые семь битов скэн-кода) и была ли она нажата (если 8-ой бит нулевой) или отпущена (если он равен единице).
/* intrpt.c - An interrupt handler. */
/* Copyright (C) 1998 by Ori Pomerantz */
/* The necessary header files */
/* Standard in kernel modules */
#include <linux/kernel.h> /* We're doing kernel work */
#include <linux/module.h> /* Specifically, a module */
/* Deal with CONFIG_MODVERSIONS */
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/sched.h>
#include <linux/tqueue.h>
/* We want an interrupt */
#include <linux/interrupt.h>
#include <asm/io.h>
/* In 2.2.3 /usr/include/linux/version.h includes a
* macro for this, but 2.0.35 doesn't - so I add it
* here if necessary. */
#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
#endif
/* Bottom Half - this will get called by the kernel
* as soon as it's safe to do everything normally
* allowed by kernel modules. */
static void got_char(void *scancode)
{
printk("Scan Code %x %s.\n", (int) *((char *) scancode) & 0x7F,
*((char *) scancode) & 0x80 ? "Released" : "Pressed");
}
/* This function services keyboard interrupts. It reads
* the relevant information from the keyboard and then
* scheduales the bottom half to run when the kernel
* considers it safe. */
void irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
/* This variables are static because they need to be
* accessible (through pointers) to the bottom
* half routine. */
static unsigned char scancode;
static struct tq_struct task = {NULL, 0, got_char, &scancode};
unsigned char status;
/* Read keyboard status */
status = inb(0x64);
scancode = inb(0x60);
/* Scheduale bottom half to run */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)
queue_task(&task, &tq_immediate);
#else
queue_task_irq(&task, &tq_immediate);
#endif
mark_bh(IMMEDIATE_BH);
}
/* Initialize the module - register the IRQ handler */
int init_module()
{
/* Since the keyboard handler won't co-exist with
* another handler, such as us, we have to disable
* it (free its IRQ) before we do anything. Since we
* don't know where it is, there's no way to
* reinstate it later - so the computer will have to
* be rebooted when we're done.
*/
free_irq(1, NULL);
/* Request IRQ 1, the keyboard IRQ, to go to our
* irq_handler. */
return request_irq(
1, /* The number of the keyboard IRQ on PCs */
irq_handler, /* our handler */
SA_SHIRQ,
/* SA_SHIRQ means we're willing to have othe
* handlers on this IRQ.
*
* SA_INTERRUPT can be used to make the
* handler into a fast interrupt.
*/
"test_keyboard_irq_handler", NULL);
}
/* Cleanup */
void cleanup_module()
{
/* This is only here for completeness. It's totally
* irrelevant, since we don't have a way to restore
* the normal keyboard interrupt so the computer
* is completely useless and has to be rebooted. */
free_irq(1, NULL);
}