mirror of https://github.com/tLDP/LDP
216 lines
10 KiB
Plaintext
216 lines
10 KiB
Plaintext
<sect1><title>Interrupt Handlers</title>
|
|
|
|
<indexterm><primary>interrupt handlers</primary></indexterm>
|
|
<indexterm><primary>handlers</primary><secondary>interrupt</secondary></indexterm>
|
|
|
|
|
|
|
|
<sect2><title>Interrupt Handlers</title>
|
|
|
|
<para>Except for the last chapter, everything we did in the kernel so far we've done as a response to a process asking for
|
|
it, either by dealing with a special file, sending an <function>ioctl()</function>, or issuing a system call. But the job
|
|
of the kernel isn't just to respond to process requests. Another job, which is every bit as important, is to speak to the
|
|
hardware connected to the machine.</para>
|
|
|
|
<para>There are two types of interaction between the CPU and the rest of the computer's hardware. The first type is when
|
|
the CPU gives orders to the hardware, the other is when the hardware needs to tell the CPU something. The second, called
|
|
interrupts, is much harder to implement because it has to be dealt with when convenient for the hardware, not the CPU.
|
|
Hardware devices typically have a very small amount of RAM, and if you don't read their information when available, it is
|
|
lost.</para>
|
|
|
|
<para>Under Linux, hardware interrupts are called IRQ's (<emphasis>I</emphasis>nterrupt
|
|
<emphasis>R</emphasis>e<emphasis>q</emphasis>uests)<footnote><para>This is standard nomencalture on the Intel architecture
|
|
where Linux originated.</para></footnote>. There are two types of IRQ's, short and long. A short IRQ is one which is
|
|
expected to take a <emphasis>very</emphasis> short period of time, during which the rest of the machine will be blocked
|
|
and no other interrupts will be handled. A long IRQ is one which can take longer, and during which other interrupts may
|
|
occur (but not interrupts from the same device). If at all possible, it's better to declare an interrupt handler to be
|
|
long.</para>
|
|
|
|
<indexterm><primary>bottom half</primary></indexterm>
|
|
|
|
<para>When the CPU receives an interrupt, it stops whatever it's doing (unless it's processing a more important interrupt,
|
|
in which case it will deal with this one only when the more important one is done), saves certain parameters on the stack
|
|
and calls the interrupt handler. This means that certain things are not allowed in the interrupt handler itself, because
|
|
the system is in an unknown state. The solution to this problem is for the interrupt handler to do what needs to be done
|
|
immediately, usually read something from the hardware or send something to the hardware, and then schedule the handling of
|
|
the new information at a later time (this is called the <quote>bottom half</quote>) and return. The kernel is then
|
|
guaranteed to call the bottom half as soon as possible -- and when it does, everything allowed in kernel modules will be
|
|
allowed.</para>
|
|
|
|
<indexterm><primary>request_irq()</primary></indexterm>
|
|
<indexterm><primary>/proc/interrupts</primary></indexterm>
|
|
<indexterm><primary>SA_INTERRUPT</primary></indexterm>
|
|
<indexterm><primary>SA_SHIRQ</primary></indexterm>
|
|
|
|
<para>The way to implement this is to call <function>request_irq()</function> to get your interrupt handler called when
|
|
the relevant IRQ is received (there are 15 of them, plus 1 which is used to cascade the interrupt controllers, on Intel
|
|
platforms). This function receives the IRQ number, the name of the function, flags, a name for
|
|
<filename>/proc/interrupts</filename> and a parameter to pass to the interrupt handler. The flags can include
|
|
<parameter>SA_SHIRQ</parameter> to indicate you're willing to share the IRQ with other interrupt handlers (usually because
|
|
a number of hardware devices sit on the same IRQ) and <parameter>SA_INTERRUPT</parameter> to indicate this is a fast
|
|
interrupt. This function will only succeed if there isn't already a handler on this IRQ, or if you're both willing to
|
|
share.</para>
|
|
|
|
<indexterm><primary>queue_task_irq</primary></indexterm>
|
|
<indexterm><primary>tq_immediate</primary></indexterm>
|
|
<indexterm><primary>mark_bh</primary></indexterm>
|
|
<indexterm><primary>BH_IMMEDIATE</primary></indexterm>
|
|
|
|
<para>Then, from within the interrupt handler, we communicate with the hardware and then use
|
|
<function>queue_task_irq()</function> with <function>tq_immediate()</function> and
|
|
<function>mark_bh(BH_IMMEDIATE)</function> to schedule the bottom half. The reason we can't use the standard
|
|
<function>queue_task</function> <indexterm><primary>queue_task</primary></indexterm> in version 2.0 is that the interrupt
|
|
might happen right in the middle of somebody else's
|
|
<function>queue_task</function><footnote><para><function>queue_task_irq</function> is protected from this by a global lock
|
|
-- in 2.2 there is no <function>queue_task_irq</function> and <function>queue_task</function> is protected by a
|
|
lock.</para></footnote>. We need <function>mark_bh</function> because earlier versions of Linux only had an array of 32
|
|
bottom halves, and now one of them (<parameter>BH_IMMEDIATE</parameter>) is used for the linked list of bottom halves for
|
|
drivers which didn't get a bottom half entry assigned to them.</para>
|
|
|
|
</sect2>
|
|
|
|
|
|
|
|
<sect2 id="keyboard"><title>Keyboards on the Intel Architecture</title>
|
|
|
|
<indexterm><primary>keyboard</primary></indexterm>
|
|
<indexterm><primary>Intel architecture</primary><secondary>keyboard</secondary></indexterm>
|
|
|
|
<!-- <warning> -->
|
|
<para>The rest of this chapter is completely Intel specific. If you're not running on an Intel platform, it
|
|
will not work. Don't even try to compile the code here.</para>
|
|
<!-- </warning> -->
|
|
|
|
<para>I had a problem with writing the sample code for this chapter. On one hand, for an example to be useful it has to
|
|
run on everybody's computer with meaningful results. On the other hand, the kernel already includes device drivers for
|
|
all of the common devices, and those device drivers won't coexist with what I'm going to write. The solution I've found
|
|
was to write something for the keyboard interrupt, and disable the regular keyboard interrupt handler first. Since it is
|
|
defined as a static symbol in the kernel source files (specifically, <filename>drivers/char/keyboard.c</filename>), there
|
|
is no way to restore it. Before <userinput>insmod</userinput>'ing this code, do on another terminal <userinput>sleep 120
|
|
; reboot</userinput> if you value your file system.</para>
|
|
|
|
<indexterm><primary>inb</primary></indexterm>
|
|
|
|
<para> This code binds itself to IRQ 1, which is the IRQ of the keyboard controlled under Intel architectures. Then, when
|
|
it receives a keyboard interrupt, it reads the keyboard's status (that's the purpose of the
|
|
<userinput>inb(0x64)</userinput>) and the scan code, which is the value returned by the keyboard. Then, as soon as the
|
|
kernel thinks it's feasible, it runs <function>got_char</function> which gives the code of the key used (the first seven
|
|
bits of the scan code) and whether it has been pressed (if the 8th bit is zero) or released (if it's one).</para>
|
|
|
|
<indexterm><primary>source file</primary><secondary><filename>intrpt.c</filename></secondary></indexterm>
|
|
|
|
|
|
<example><title>intrpt.c</title>
|
|
<programlisting><![CDATA[
|
|
/* intrpt.c - An interrupt handler.
|
|
*
|
|
* Copyright (C) 2001 by Peter Jay Salzman
|
|
*/
|
|
|
|
/* 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.
|
|
* 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.
|
|
*/
|
|
return request_irq(1, /* The number of the keyboard IRQ on PCs */
|
|
irq_handler, /* our handler */
|
|
SA_SHIRQ,
|
|
"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);
|
|
}
|
|
]]></programlisting>
|
|
</example>
|
|
|
|
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
|
|
|
|
<!--
|
|
vim: tw=128
|
|
-->
|