Наши партнеры








Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

     
Клавиатура в архитектуре Intel

Предупреждение: Остальная часть этой главы полностью специфическая для Intel. Если вы не запускаете код на платформе Intel, он не будет работать.

Я имел проблему с написанием типового кода для этой главы. С одной стороны, для примера, чтобы быть полезным он должен выполняться на любом компьютере со значимыми результатами. С другой стороны, ядро уже включает драйверы устройства для всех общих устройств, и те драйверы устройства не будут сосуществовать с тем, что я собираюсь писать. Решение, которое я нашел состояло в том, чтобы написать обработчик для прерывания клавиатуры и сначала отключать стандартную программу обработки прерывания клавиатуры. Так как это определено как static в исходных файлах ядра (в файле drivers/char/keyboard.c), нет никакого способа восстановить обработчик.

Этот код связывает себя с IRQ 1, который является IRQ клавиатуры, управляемой в архитектуре Intel. При получении прерывания от клавиатуры, он читает состояние клавиатуры (inb(0x64)) и скэн-кода, который является значением, возвращенным клавиатурой. Затем, как только ядро думает, что это выполнимо, выполняется got_char, который дает код используемой клавиши (первые семь битов скэн-кода) и была ли она нажата (если 8-ой бит нулевой) или отпущена (если он равен единице).  

intrpt.c   


/* 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);
}



1999-05-19