mirror of https://github.com/tLDP/LDP
updated
This commit is contained in:
parent
62a07332a5
commit
2a7e9da23b
|
@ -29,7 +29,7 @@
|
|||
<sect1><title>Acknowledgements</title>
|
||||
|
||||
<para>The following people have contributed corrections or good suggestions: Ignacio Martin, David Porter, Daniele Paolo
|
||||
Scarpazza, Dimo Velev and Francois Audeon </para>
|
||||
Scarpazza, Dimo Velev, Francois Audeon and Horst Schirmeier. </para>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
kerneld.</para></footnote> execs modprobe to load the module in. modprobe is passed a string in one of two forms:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>A module name like <filename>softdog</filename> or <filename>ppp</filename>.</listitem>
|
||||
<listitem><para>A more generic identifier like <varname>char-major-10-30</varname>.</listitem>
|
||||
<listitem><para>A module name like <filename>softdog</filename> or <filename>ppp</filename>.</para></listitem>
|
||||
<listitem><para>A more generic identifier like <varname>char-major-10-30</varname>.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>If modprobe is handed a generic identifier, it first looks for that string in the file
|
||||
|
@ -75,7 +75,7 @@ modprobe msdos
|
|||
|
||||
<para> What we've seen here is: <command> insmod </command> requires you to pass it full pathname and to insert the modules
|
||||
in the right order, while <command> modprobe </command> just takes the name, without any extension, and figures out
|
||||
all it needs to know by parsing <filename>/lib/modules/version/modules.dep</filename>.
|
||||
all it needs to know by parsing <filename>/lib/modules/version/modules.dep</filename>.</para>
|
||||
|
||||
<para>Linux distros provide modprobe, insmod and depmod as a package called module-init-tools. In previous versions
|
||||
that package was called modutils. Some distros also set up some wrappers that allow both packages to be installed
|
||||
|
|
|
@ -155,7 +155,7 @@ hostname:~/lkmpg-examples/02-HelloWorld#
|
|||
<filename>/var/log/messages</filename> just to see that it got logged to your system logfile.</para>
|
||||
|
||||
<para>Here's another exercise to the reader. See that comment above the return statement in
|
||||
<function>init_module()</function>? Change the return value to something non-zero, recompile and load the module again.
|
||||
<function>init_module()</function>? Change the return value to something negative, recompile and load the module again.
|
||||
What happens?</para>
|
||||
|
||||
</sect1>
|
||||
|
@ -317,33 +317,34 @@ Module xxxxxx loaded, with warnings
|
|||
used to.</para>
|
||||
|
||||
<para>To allow arguments to be passed to your module, declare the variables that will take the values of the command line
|
||||
arguments as global and then use the <functioN>MODULE_PARM()</function> macro, (defined in <filename
|
||||
role="headerfile">linux/module.h</filename>) to set the mechanism up. At runtime, insmod will fill the variables with any
|
||||
arguments as global and then use the <functioN>module_param()</function> macro, (defined in <filename
|
||||
role="headerfile">linux/moduleparam.h</filename>) to set the mechanism up. At runtime, insmod will fill the variables with any
|
||||
command line arguments that are given, like <command>./insmod mymodule.ko myvariable=5</command>. The variable
|
||||
declarations and macros should be placed at the beginning of the module for clarity. The example code should clear up my
|
||||
admittedly lousy explanation.</para>
|
||||
|
||||
<para>The <function>MODULE_PARM()</function> macro takes 2 arguments: the name of the variable and its type. The supported
|
||||
variable types are "<literal>b</literal>": single byte, "<literal>h</literal>": short int, "<literal>i</literal>": integer,
|
||||
"<literal>l</literal>": long int and "<literal>s</literal>": string, and the integer types can be signed as usual or unsigned.
|
||||
Strings should be declared as "<type>char *</type>" and insmod will allocate memory for them. You should always try to give
|
||||
the variables an initial default value. This is kernel code, and you should program defensively. For example:</para>
|
||||
<para>The <function>module_param()</function> macro takes 3 arguments: the name of the variable, its type and
|
||||
permissions for the corresponding file in sysfs. Integer types can be signed as usual or unsigned.
|
||||
If you'd like to use arrays of integers or strings see <function>module_param_array()</function> and
|
||||
<function>module_param_string()</function>.
|
||||
|
||||
|
||||
<screen>
|
||||
int myint = 3;
|
||||
char *mystr;
|
||||
|
||||
MODULE_PARM(myint, "i");
|
||||
MODULE_PARM(mystr, "s");
|
||||
module_param(myint, int, 0);
|
||||
</screen>
|
||||
|
||||
<para>Arrays are supported too. An integer value preceding the type in MODULE_PARM will indicate an array of some maximum
|
||||
length. Two numbers separated by a '-' will give the minimum and maximum number of values. For example, an array of shorts
|
||||
with at least 2 and no more than 4 values could be declared as:</para>
|
||||
<para>Arrays are supported too, but things are a bit different now than they were in the 2.4. days. To keep track of the
|
||||
number of parameters you need to pass a pointer to a count variable as third parameter. At your option, you could also
|
||||
ignore the count and pass NULL instead. We show both possibilities here:</para>
|
||||
|
||||
<screen>
|
||||
int myshortArray[4];
|
||||
MODULE_PARM (myintArray, "3-9i");
|
||||
int myintarray[2];
|
||||
module_param_array(myintarray, int, NULL, 0); /* not interested in count */
|
||||
|
||||
int myshortarray[4];
|
||||
int count;
|
||||
module_parm_array(myshortarray, short, & count, 0); /* put count into "count" variable */
|
||||
</screen>
|
||||
|
||||
<para>A good use for this is to have the module variable's default values set, like an port or IO address. If the variables
|
||||
|
|
|
@ -139,7 +139,7 @@ int main(void)
|
|||
point to memory locations. Not real ones, anyway. When a process is created, the kernel sets aside a portion of real
|
||||
physical memory and hands it to the process to use for its executing code, variables, stack, heap and other things which a
|
||||
computer scientist would know about<footnote><para>I'm a physicist, not a computer scientist, Jim!</para></footnote>.
|
||||
This memory begins with $0$ and extends up to whatever it needs to be. Since the memory space for any two processes don't
|
||||
This memory begins with 0x00000000 and extends up to whatever it needs to be. Since the memory space for any two processes don't
|
||||
overlap, every process that can access a memory address, say <literal>0xbffff978</literal>, would be accessing a different
|
||||
location in real physical memory! The processes would be accessing an index named <literal>0xbffff978</literal> which
|
||||
points to some kind of offset into the region of memory set aside for that particular process. For the most part, a
|
||||
|
@ -149,7 +149,7 @@ int main(void)
|
|||
<para>The kernel has its own space of memory as well. Since a module is code which can be dynamically inserted and
|
||||
removed in the kernel (as opposed to a semi-autonomous object), it shares the kernel's codespace rather than having its
|
||||
own. Therefore, if your module segfaults, the kernel segfaults. And if you start writing over data because of an
|
||||
off-by-one error, then you're trampling on kernel code. This is even worse than it sounds, so try your best to be
|
||||
off-by-one error, then you're trampling on kernel data (or code). This is even worse than it sounds, so try your best to be
|
||||
careful.</para>
|
||||
|
||||
<para>By the way, I would like to point out that the above discussion is true for any operating system which uses a
|
||||
|
|
|
@ -68,7 +68,7 @@ struct file_operations fops = {
|
|||
</screen>
|
||||
|
||||
<para>However, there's also a C99 way of assigning to elements of a structure, and this is definitely preferred over using
|
||||
the GNU extension. The version of gcc I'm currently using, <literal>2.95</literal>, supports the new C99 syntax. You
|
||||
the GNU extension. The version of gcc the author used when writing this, <literal>2.95</literal>, supports the new C99 syntax. You
|
||||
should use this syntax in case someone wants to port your driver. It will help with compatibility:</para>
|
||||
|
||||
<screen>
|
||||
|
@ -83,7 +83,8 @@ struct file_operations fops = {
|
|||
<para>The meaning is clear, and you should be aware that any member of the structure which you don't explicitly assign
|
||||
will be initialized to <varname>NULL</varname> by gcc.</para>
|
||||
|
||||
<para>A pointer to a <type>struct file_operations</type> is commonly named <varname>fops</varname>.</para>
|
||||
<para>An instance of <type>struct file_operations</type> containing pointers to functions that are used to implement
|
||||
read, write, open, ... syscalls is commonly named <varname>fops</varname>.</para>
|
||||
|
||||
</sect2>
|
||||
|
||||
|
@ -100,7 +101,7 @@ struct file_operations fops = {
|
|||
kernel space function. Also, its name is a bit misleading; it represents an abstract open `file', not a file on a disk,
|
||||
which is represented by a structure named <type>inode</type>.</para>
|
||||
|
||||
<para>A pointer to a <varname>struct file</varname> is commonly named <function>filp</function>. You'll also see it
|
||||
<para>An instance of <varname>struct file</varname> is commonly named <function>filp</function>. You'll also see it
|
||||
refered to as <varname>struct file file</varname>. Resist the temptation.</para>
|
||||
|
||||
<para>Go ahead and look at the definition of <function>file</function>. Most of the entries you see, like
|
||||
|
@ -135,7 +136,7 @@ int register_chrdev(unsigned int major, const char *name, struct file_operations
|
|||
<para>where <varname>unsigned int major</varname> is the major number you want to request, <varname>const char
|
||||
*name</varname> is the name of the device as it'll appear in <filename>/proc/devices</filename> and <varname>struct
|
||||
file_operations *fops</varname> is a pointer to the <varname>file_operations</varname> table for your driver. A negative
|
||||
return value means the registertration failed. Note that we didn't pass the minor number to
|
||||
return value means the registration failed. Note that we didn't pass the minor number to
|
||||
<function>register_chrdev</function>. That's because the kernel doesn't care about the minor number; only our driver uses
|
||||
it.</para>
|
||||
|
||||
|
@ -175,11 +176,11 @@ int register_chrdev(unsigned int major, const char *name, struct file_operations
|
|||
will fail. Note that you don't have to check the counter from within <function>cleanup_module</function> because the
|
||||
check will be performed for you by the system call <function>sys_delete_module</function>, defined in
|
||||
<filename>linux/module.c</filename>. You shouldn't use this counter directly, but there are functions defined in <filename
|
||||
role="headerfile">linux/modules.h</filename> which let you increase, decrease and display this counter:</para>
|
||||
role="headerfile">linux/module.h</filename> which let you increase, decrease and display this counter:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para><varname>try_module_get(THIS_MODULE)</varname>: Increment the use count.</para></listitem>
|
||||
<listitem><para><varname>try_module_put(THIS_MODULE)</varname>: Decrement the use count.</para></listitem>
|
||||
<listitem><para><varname>module_put(THIS_MODULE)</varname>: Decrement the use count.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>It's important to keep the counter accurate; if you ever do lose track of the correct usage count, you'll never be
|
||||
|
|
|
@ -187,6 +187,64 @@ HelloWorld!
|
|||
|
||||
</sect1>
|
||||
|
||||
<sect1><title>Manage /proc file with seq_file</title>
|
||||
|
||||
<indexterm><primary>seq_file</primary></indexterm>
|
||||
|
||||
<para>
|
||||
As we have seen, writing a /proc file may be quite "complex". So to help people writting
|
||||
/proc file, there is an API named seq_file that helps formating a /proc file for output.
|
||||
It's based on sequence, which is composed of 3 functions: start(), next(), and stop().
|
||||
The seq_file API starts a sequence when a user read the /proc file.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A sequence begins with the call of the function start(). If the return is a non NULL value,
|
||||
the function next() is called. This function is an iterator, the goal is to go thought
|
||||
all the data. Each time next() is called, the function show() is also called. It writes
|
||||
data values in the buffer read by the user. The function next() is called until it returns
|
||||
NULL. The sequence ends when next() returns NULL, then the function stop() is called.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
BE CARREFUL: when a sequence is finished, another one starts. That means that at the end
|
||||
of function stop(), the function start() is called again. This loop finishes when the
|
||||
function start() returns NULL. You can see a scheme of this in the figure "How seq_file works".
|
||||
</para>
|
||||
|
||||
<figure>
|
||||
<title>How seq_file works</title>
|
||||
<graphic fileref="figures/seq_file.png"></graphic>
|
||||
</figure>
|
||||
|
||||
<para>
|
||||
Seq_file provides basic functions for file_operations, as seq_read, seq_lseek, and some others.
|
||||
But nothing to write in the /proc file. Of course, you can still use the same way as in the
|
||||
previous example.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>procfs4.c</title>
|
||||
<programlisting>
|
||||
<inlinegraphic fileref="lkmpg-examples/05-TheProcFileSystem/procfs4.c" format="linespecific"/></inlinegraphic>
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<para>
|
||||
If you want more information, you can read this web page:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para><ulink url="http://lwn.net/Articles/22355/"></ulink></para></listitem>
|
||||
<listitem><para><ulink url="http://www.kernelnewbies.org/documents/seq_file_howto.txt"></ulink></para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
You can also read the code of fs/seq_file.c in the linux kernel.
|
||||
</para>
|
||||
|
||||
</sect1>
|
||||
|
||||
<!--
|
||||
vim:textwidth=128
|
||||
-->
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<function>open()</function> system call. This meant I couldn't open any files, I couldn't run any programs, and I couldn't
|
||||
<command>shutdown</command> the computer. I had to pull the power switch. Luckily, no files died. To ensure you won't lose
|
||||
any files either, please run <command>sync</command> right before you do the <command>insmod</command> and the
|
||||
<command>rmmod</command>.
|
||||
<command>rmmod</command>.</para>
|
||||
|
||||
<indexterm><primary>sync</primary></indexterm>
|
||||
<indexterm><primary>insmod</primary></indexterm>
|
||||
|
|
|
@ -3,115 +3,128 @@
|
|||
<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>
|
||||
|
||||
<sect2><title>Interrupt Handlers</title>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<indexterm><primary>bottom half</primary></indexterm>
|
||||
|
||||
<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>
|
||||
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>
|
||||
|
||||
<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>request_irq()</primary></indexterm>
|
||||
<indexterm><primary>/proc/interrupts</primary></indexterm>
|
||||
<indexterm><primary>SA_INTERRUPT</primary></indexterm>
|
||||
<indexterm><primary>SA_SHIRQ</primary></indexterm>
|
||||
|
||||
<indexterm><primary>bottom half</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. <footnote><para>In practice IRQ handling can be a bit more
|
||||
complex. Hardware is often designed in a way that chains two interrupt controllers, so that all the IRQs
|
||||
from interrupt controller B are cascaded to a certain IRQ from interrupt controller A. Of course that
|
||||
requires that the kernel finds out which IRQ it really was afterwards and that adds overhead. Other
|
||||
architectures offer some special, very low overhead, so called "fast IRQ" or FIQs. To take advantage
|
||||
of them requires handlers to be written in assembler, so they do not really fit into the kernel. They
|
||||
can be made to work similar to the others, but after that procedure, they're no longer any faster than
|
||||
"common" IRQs. SMP enabled kernels running on systems with more than one processor need to solve another
|
||||
truckload of problems. It's not enough to know if a certain IRQs has happend, it's also important for
|
||||
what CPU(s) it was for. People still interested in more details, might want to do a web search for
|
||||
"APIC" now ;)</para></footnote>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. Usually
|
||||
there is a certain number of IRQs available. How many IRQs there are is hardware dependant. 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>
|
||||
|
||||
<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>queue_work</primary></indexterm>
|
||||
|
||||
<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.
|
||||
|
||||
<footnote><para>
|
||||
In practice IRQ handling can be a bit more complex. Hardware is often designed in a way that chains two interrupt
|
||||
controllers, so that all the IRQs from interrupt controller B are cascaded to a certain IRQ from
|
||||
interrupt controller A. Of course that requires that the kernel finds out which IRQ it really was
|
||||
afterwards and that adds overhead. Other architectures offer some special, very low overhead,
|
||||
so called "fast IRQ" or FIQs. To take advantage of them requires handlers to be written in assembler,
|
||||
so they do not really fit into the kernel. They can be made to work similar to the others, but after
|
||||
that procedure, they're no longer any faster than "common" IRQs. SMP enabled kernels running on systems
|
||||
with more than one processor need to solve another truckload of problems. It's not enough to know if a certain IRQs
|
||||
has happend, it's also important for what CPU(s) it was for. People still interested in more details,
|
||||
might want to do a web search for "APIC" now ;)
|
||||
</para></footnote>
|
||||
|
||||
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.
|
||||
Usually there is a certain number of IRQs available. How many IRQs there are is hardware dependant.
|
||||
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_work</primary></indexterm>
|
||||
|
||||
<para>Then, from within the interrupt handler, we communicate with the hardware and then use
|
||||
<function>queue_work()</function> <function>mark_bh(BH_IMMEDIATE)</function> to schedule the bottom half.
|
||||
</sect2>
|
||||
<para>
|
||||
Then, from within the interrupt handler, we communicate with the hardware and then use <function>queue_work()
|
||||
</function> <function>mark_bh(BH_IMMEDIATE)</function> to schedule the bottom half.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
|
||||
|
||||
<sect2 id="keyboard"><title>Keyboards on the Intel Architecture</title>
|
||||
<sect2 id="keyboard"><title>Keyboards on the Intel Architecture</title>
|
||||
|
||||
<indexterm><primary>keyboard</primary></indexterm>
|
||||
<indexterm><primary>Intel architecture</primary><secondary>keyboard</secondary></indexterm>
|
||||
<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> -->
|
||||
<!-- <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>
|
||||
|
||||
<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>
|
||||
<!-- </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>
|
||||
<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>
|
||||
<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>
|
||||
<indexterm><primary>source file</primary><secondary><filename>intrpt.c</filename></secondary></indexterm>
|
||||
|
||||
|
||||
<example><title>intrpt.c</title><programlisting><inlinegraphic fileref="lkmpg-examples/12-InterruptHandlers/intrpt.c" format="linespecific"/></inlinegraphic></programlisting></example>
|
||||
<example>
|
||||
<title>intrpt.c</title>
|
||||
<programlisting>
|
||||
<inlinegraphic fileref="lkmpg-examples/12-InterruptHandlers/intrpt.c" format="linespecific"/></inlinegraphic>
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
|
||||
</sect2>
|
||||
</sect2>
|
||||
|
||||
</sect1>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
vim: tw=128
|
||||
-->
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
#FIG 3.2 Produced by xfig version 3.2.5-alpha5
|
||||
Landscape
|
||||
Center
|
||||
Inches
|
||||
Letter
|
||||
100.00
|
||||
Single
|
||||
-2
|
||||
1200 2
|
||||
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||
900 600 5700 600 5700 1200 900 1200 900 600
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||
3300 1800 900 2250 3300 2700 5700 2250 3300 1800
|
||||
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||
900 3300 5700 3300 5700 3900 900 3900 900 3300
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||
3310 4479 910 4929 3310 5379 5710 4929 3310 4479
|
||||
2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
|
||||
900 6000 5700 6000 5700 6600 900 6600 900 6000
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
|
||||
0 0 1.00 60.00 120.00
|
||||
3300 1200 3300 1800
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
|
||||
0 0 1.00 60.00 120.00
|
||||
3300 2700 3300 3300
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
|
||||
0 0 1.00 60.00 120.00
|
||||
3300 3900 3300 4500
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
|
||||
0 0 1.00 60.00 120.00
|
||||
3300 5400 3300 6000
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 6
|
||||
0 0 1.00 60.00 120.00
|
||||
3300 6600 3300 6900 300 6900 300 300 3300 300 3300 600
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
|
||||
900 4950 600 4950 600 3000 3300 3000
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 7 1 0 2
|
||||
0 0 1.00 60.00 120.00
|
||||
5700 2250 6900 2250
|
||||
4 0 0 50 -1 0 16 0.0000 4 255 1890 2700 975 start() treatment\001
|
||||
4 0 0 50 -1 0 16 0.0000 4 195 1725 2700 2325 return is NULL\001
|
||||
4 0 0 50 -1 0 16 0.0000 4 255 1875 2550 3675 next() treatment\001
|
||||
4 0 0 50 -1 0 16 0.0000 4 195 1725 2700 5025 return is NULL\001
|
||||
4 0 0 50 -1 0 16 0.0000 4 255 1845 2700 6300 stop() treatment\001
|
||||
4 0 0 50 -1 0 16 0.0000 4 195 405 600 5400 NO\001
|
||||
4 0 0 50 -1 0 16 0.0000 4 195 495 3450 5850 YES\001
|
||||
4 0 0 50 -1 0 16 0.0000 4 195 495 6150 2175 YES\001
|
||||
4 0 0 50 -1 0 16 0.0000 4 195 405 3450 3150 NO\001
|
Binary file not shown.
After Width: | Height: | Size: 5.9 KiB |
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
|||
obj-m += procfs1.o procfs2.o procfs3.o
|
||||
obj-m += procfs1.o procfs2.o procfs3.o procfs4.o
|
||||
|
||||
all:
|
||||
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
/**
|
||||
* procfs4.c - create a "file" in /proc
|
||||
* This program uses the seq_file library to manage the /proc file.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h> /* We're doing kernel work */
|
||||
#include <linux/module.h> /* Specifically, a module */
|
||||
#include <linux/proc_fs.h> /* Necessary because we use proc fs */
|
||||
#include <linux/seq_file.h> /* for seq_file */
|
||||
|
||||
#define PROC_NAME "iter"
|
||||
|
||||
MODULE_AUTHOR("Philippe Reynes");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/**
|
||||
* This function is called at the beginning of a sequence.
|
||||
* ie, when:
|
||||
* - the /proc file is read (first time)
|
||||
* - after the function stop (end of sequence)
|
||||
*
|
||||
*/
|
||||
static void *my_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
static unsigned long counter = 0;
|
||||
|
||||
/* beginning a new sequence ? */
|
||||
if ( *pos == 0 )
|
||||
{
|
||||
/* yes => return a non null value to begin the sequence */
|
||||
return &counter;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no => it's the end of the sequence, return end to stop reading */
|
||||
*pos = 0;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called after the beginning of a sequence.
|
||||
* It's called untill the return is NULL (this ends the sequence).
|
||||
*
|
||||
*/
|
||||
static void *my_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
unsigned long *tmp_v = (unsigned long *)v;
|
||||
(*tmp_v)++;
|
||||
(*pos)++;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called at the end of a sequence
|
||||
*
|
||||
*/
|
||||
static void my_seq_stop(struct seq_file *s, void *v)
|
||||
{
|
||||
/* nothing to do, we use a static value in start() */
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called for each "step" of a sequence
|
||||
*
|
||||
*/
|
||||
static int my_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
loff_t *spos = (loff_t *) v;
|
||||
|
||||
seq_printf(s, "%Ld\n", *spos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This structure gather "function" to manage the sequence
|
||||
*
|
||||
*/
|
||||
static struct seq_operations my_seq_ops = {
|
||||
.start = my_seq_start,
|
||||
.next = my_seq_next,
|
||||
.stop = my_seq_stop,
|
||||
.show = my_seq_show
|
||||
};
|
||||
|
||||
/**
|
||||
* This function is called when the /proc file is open.
|
||||
*
|
||||
*/
|
||||
static int my_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &my_seq_ops);
|
||||
};
|
||||
|
||||
/**
|
||||
* This structure gather "function" that manage the /proc file
|
||||
*
|
||||
*/
|
||||
static struct file_operations my_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = my_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This function is called when the module is loaded
|
||||
*
|
||||
*/
|
||||
int init_module(void)
|
||||
{
|
||||
struct proc_dir_entry *entry;
|
||||
|
||||
entry = create_proc_entry(PROC_NAME, 0, NULL);
|
||||
if (entry) {
|
||||
entry->proc_fops = &my_file_ops;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called when the module is unloaded.
|
||||
*
|
||||
*/
|
||||
void cleanup_module(void)
|
||||
{
|
||||
remove_proc_entry(PROC_NAME, NULL);
|
||||
}
|
|
@ -11,6 +11,8 @@
|
|||
*/
|
||||
#include "chardev.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h> /* open */
|
||||
#include <unistd.h> /* exit */
|
||||
#include <sys/ioctl.h> /* ioctl */
|
||||
|
@ -62,7 +64,7 @@ ioctl_get_nth_byte(int file_desc)
|
|||
printf("get_nth_byte message:");
|
||||
|
||||
i = 0;
|
||||
while (c != 0) {
|
||||
do {
|
||||
c = ioctl(file_desc, IOCTL_GET_NTH_BYTE, i++);
|
||||
|
||||
if (c < 0) {
|
||||
|
@ -73,7 +75,7 @@ ioctl_get_nth_byte(int file_desc)
|
|||
}
|
||||
|
||||
putchar(c);
|
||||
}
|
||||
} while (c != 0);
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/tty.h> /* For fg_console, MAX_NR_CONSOLES */
|
||||
#include <linux/kd.h> /* For KDSETLED */
|
||||
#include <linux/vt.h>
|
||||
#include <linux/console_struct.h> /* For vc_cons */
|
||||
|
||||
MODULE_DESCRIPTION("Example module illustrating the use of Keyboard LEDs.");
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
</authorgroup>
|
||||
|
||||
<!-- year-month-day -->
|
||||
<pubdate>2005-05-26 ver 2.6.1</pubdate>
|
||||
<pubdate>2005-12-31 ver 2.6.3</pubdate>
|
||||
|
||||
|
||||
<copyright>
|
||||
|
|
Loading…
Reference in New Issue