<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
-->
