Чтение онлайн

ЖАНРЫ

Энциклопедия разработчика модулей ядра Linux

Померанц Ори

Шрифт:

Когда CPU получает прерывание, он останавливает любые процессы (если это не более важное прерывание, тогда обработка пришедшего прерывания будет иметь место только когда более важное будет выполнено), сохраняет параметры в стеке и вызывает программу обработки прерывания (обработчик прерывания). Это означает, что некоторые вещи не позволяются в программе обработки прерывания непосредственно, потому что система находится в неизвестном состоянии. Решение для этой проблемы: программа обработки прерывания, должна разобраться что должно быть выполнено немедленно (обычно чтение чего-то из аппаратных средств или посылка чего-либо аппаратным средствам), затем запланировать обработку новой информации в более позднее время (это названо «нижней половиной»)

и вернуть управление. Ядро гарантирует вызов нижней половины как можно скорее. Когда оно это сделает, все позволенное в модулях будет доступно нашему обработчику.

Способ выполнять это состоит в том, чтобы вызвать request_irq для получения нашей программы обработки прерывания, вызванную, когда релевантное IRQ получено (их имеется 16 на платформах Intel). Эта функция получает IRQ номер, имя функции, флажки, имя для /proc/interrupts и параметр для для вызова обработчика прерываний. Флажки могут включать SA_SHIRQ, чтобы указать, что вы желаете совместно использовать IRQ с другими программами обработки прерывания (обычно, потому что ряд аппаратных устройств сидит на том же самом IRQ) и SA_INTERRUPT, чтобы указать, что это быстрое прерывание. Эта функция сработает только если еще нет драйвера на этом IRQ, или если вы оба желаете совместно использовать данный IRQ.

Затем, из программы обработки прерывания, мы связываемся с аппаратными средствами и затем используем queue_task_irq с tq_immediate и mark_bh(BH_IMMEDIATE), чтобы запланировать нижнюю половину. Причина по которой мы не можем использовать стандартный вызов queue_task в версии 2.0 в том, что прерывание могло бы случиться в середине какого-то процесса. queue_task [13] . mark_bh нужен потому что более ранние версии Linux имели массив только из 32 нижних частей, и теперь одни из них (а именно BH_IMMEDIATE) используется для связанного списка нижних частей драйверов.

13

queue_task_irq защищен от этого глобальной блокировкой. В версии 2.2 queue_task_irq и queue_task защищены блокировкой.

Клавиатура в архитектуре 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

Поделиться с друзьями: