mirror of https://github.com/tLDP/LDP
updated
This commit is contained in:
parent
f06bcd9b6e
commit
77ba6298b3
|
@ -34,29 +34,6 @@
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
|
|
||||||
<!--
|
|
||||||
This is in the Chinese translation:
|
|
||||||
|
|
||||||
<sect1><title>Note From the Chinese Translator</title>
|
|
||||||
|
|
||||||
<para>I am Jerry Tian, the translator of the simplified Chinese edition of this guide, and now a university student in
|
|
||||||
Beijing. I became a GNU/Linux enthusiast when I first encountered it as a senior high school; I like the spirit of
|
|
||||||
freedom in open source software.</para>
|
|
||||||
|
|
||||||
<para>This guide is the most updated document in Linux kernel module programming. I have also translated the former 2.4
|
|
||||||
branch of LKMPG into simplified Chinese, but based on HTML files, so it's inconvenient to convert it to other formats. Thanks
|
|
||||||
to Peter's advice and encouragement, the translation of 2.6 branch is based on DOCBOOK files, and it's very easy to convert it
|
|
||||||
to other formats like HTML, PDF, etc. Most of its content is from the translation of 2.4 branch, but some translation
|
|
||||||
mistakes in 2.4 branch are corrected. Since I hardly have enough time to go through the 2.4 branch of translation again and
|
|
||||||
correct the mistakes in the HTML files, I suggest if you need to read the simplified translation of 2.4 branch of LKMPG, do
|
|
||||||
refer to the translation of 2.6 branch. Please contact me if you have any ideas on how to improve the simplified Chinese
|
|
||||||
translation of LKMPG or want to help me maintain it.</para>
|
|
||||||
|
|
||||||
<para>My email address is jerrytianbupt@163.com. Learning and sharing the learning is the motive of my translation. I hope
|
|
||||||
you enjoy it! </para>
|
|
||||||
|
|
||||||
</sect1>
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|
|
@ -35,73 +35,56 @@
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
|
|
||||||
<para>If modprobe is handed a generic identifier, it first looks for that string in the file
|
<para>If modprobe is handed a generic identifier, it first looks for that string in the file
|
||||||
<filename>/etc/modules.conf</filename>. If it finds an alias line like:</para>
|
<filename>/etc/modprobe.conf</filename>.<footnote><para>If such a file exists. Note that the acual behavoir might be
|
||||||
|
distribution dependant. If you're interested in the details,read the man pages that came with module-init-tools,
|
||||||
|
and see for yourself what's really going on. You could use something like <command> strace modprobe dummy </command>
|
||||||
|
to find out how dummy.ko gets loaded. FYI: The dummy.ko I'm talking about here is part of the mainline kernel
|
||||||
|
and can be found in the networking section. It needs to be compiled as a module (and installed, of course) for this to
|
||||||
|
work. </para></footnote>
|
||||||
|
If it finds an alias line like:</para>
|
||||||
|
|
||||||
<screen>
|
<screen>
|
||||||
alias char-major-10-30 softdog
|
alias char-major-10-30 softdog
|
||||||
</screen>
|
</screen>
|
||||||
|
|
||||||
<para>it knows that the generic identifier refers to the module <filename>softdog.o</filename>.</para>
|
<para>it knows that the generic identifier refers to the module <filename>softdog.ko</filename>.</para>
|
||||||
|
|
||||||
<para>Next, modprobe looks through the file <filename>/lib/modules/version/modules.dep</filename>, to see if other modules
|
<para>Next, modprobe looks through the file <filename>/lib/modules/version/modules.dep</filename>, to see if other modules
|
||||||
must be loaded before the requested module may be loaded. This file is created by <command>depmod -a</command> and contains
|
must be loaded before the requested module may be loaded. This file is created by <command>depmod -a</command> and
|
||||||
module dependencies. For example, <filename>msdos.o</filename> requires the <filename>fat.o</filename> module to be already
|
contains module dependencies. For example, <filename>msdos.ko</filename> requires the <filename>fat.ko</filename> module
|
||||||
loaded into the kernel. The requested module has a dependancy on another module if the other module defines symbols
|
to be already loaded into the kernel. The requested module has a dependancy on another module if the other module defines
|
||||||
(variables or functions) that the requested module uses.</para>
|
symbols (variables or functions) that the requested module uses.</para>
|
||||||
|
|
||||||
<para>Lastly, modprobe uses insmod to first load any prerequisite modules into the kernel, and then the requested module.
|
<para>Lastly, modprobe uses insmod to first load any prerequisite modules into the kernel, and then the requested module.
|
||||||
modprobe directs insmod to <filename role="directory">/lib/modules/version/</filename><footnote><para>If you are modifying the
|
modprobe directs insmod to <filename role="directory">/lib/modules/version/</filename><footnote><para>If you are modifying
|
||||||
kernel, to avoid overwriting your existing modules you may want to use the <varname>EXTRAVERSION</varname> variable in the
|
the kernel, to avoid overwriting your existing modules you may want to use the <varname>EXTRAVERSION</varname> variable in
|
||||||
kernel Makefile to create a seperate directory.</para></footnote>, the standard directory for modules. insmod is intended to
|
the kernel Makefile to create a seperate directory.</para></footnote>, the standard directory for modules. insmod is
|
||||||
be fairly dumb about the location of modules, whereas modprobe is aware of the default location of modules. So for example,
|
intended to be fairly dumb about the location of modules, whereas modprobe is aware of the default location of modules,
|
||||||
if you wanted to load the msdos module, you'd have to either run:</para>
|
knows how to figure out the dependancies and load the modules in the right order.
|
||||||
|
So for example, if you wanted to load the msdos module, you'd have to either run:</para>
|
||||||
|
|
||||||
<screen>
|
<screen>
|
||||||
insmod /lib/modules/2.5.1/kernel/fs/fat/fat.o
|
insmod /lib/modules/2.6.11/kernel/fs/fat/fat.ko
|
||||||
insmod /lib/modules/2.5.1/kernel/fs/msdos/msdos.o
|
insmod /lib/modules/2.6.11/kernel/fs/msdos/msdos.ko
|
||||||
</screen>
|
</screen>
|
||||||
|
|
||||||
<para>or just run "<command>modprobe -a msdos</command>".</para>
|
|
||||||
|
|
||||||
<indexterm><primary>modules.conf</primary><secondary>keep</secondary></indexterm>
|
|
||||||
<indexterm><primary>modules.conf</primary><secondary>comment</secondary></indexterm>
|
|
||||||
<indexterm><primary>modules.conf</primary><secondary>alias</secondary></indexterm>
|
|
||||||
<indexterm><primary>modules.conf</primary><secondary>options</secondary></indexterm>
|
|
||||||
<indexterm><primary>modules.conf</primary><secondary>path</secondary></indexterm>
|
|
||||||
|
|
||||||
|
|
||||||
<para>Linux distros provide modprobe, insmod and depmod as a package called modutils or mod-utils.</para>
|
|
||||||
|
|
||||||
<para>Before finishing this chapter, let's take a quick look at a piece of <filename>/etc/modules.conf</filename>:</para>
|
|
||||||
|
|
||||||
|
<para> or: </para>
|
||||||
<screen>
|
<screen>
|
||||||
#This file is automatically generated by update-modules
|
modprobe msdos
|
||||||
path[misc]=/lib/modules/2.4.?/local
|
|
||||||
keep
|
|
||||||
path[net]=~p/mymodules
|
|
||||||
options mydriver irq=10
|
|
||||||
alias eth0 eepro
|
|
||||||
</screen>
|
</screen>
|
||||||
|
|
||||||
<para>Lines beginning with a '#' are comments. Blank lines are ignored.</para>
|
<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>.
|
||||||
|
|
||||||
<para>The <literal>path[misc]</literal> line tells modprobe to replace the search path for misc modules with the directory
|
<para>Linux distros provide modprobe, insmod and depmod as a package called module-init-tools. In previous versions
|
||||||
<filename role="directory">/lib/modules/2.4.?/local</filename>. As you can see, shell meta characters are honored.</para>
|
that package was called modutils. Some distros also set up some wrappers that allow both packages to be installed
|
||||||
|
parallel and do the right thing in order to be able to deal with 2.4 and 2.6 kernels. Users should not need to care
|
||||||
<para>The <literal>path[net]</literal> line tells modprobe to look for net modules in the directory <filename
|
about the details, as long as they're running recent versions of that tools.</para>
|
||||||
role="directory">~p/mymodules</filename>, however, the "keep" directive preceding the <literal>path[net]</literal> directive
|
|
||||||
tells modprobe to add this directory to the standard search path of net modules as opposed to replacing the standard search
|
|
||||||
path, as we did for the misc modules.</para>
|
|
||||||
|
|
||||||
<para>The alias line says to load in <filename>eepro.o</filename> whenever kmod requests that the generic identifier `eth0' be
|
|
||||||
loaded.</para>
|
|
||||||
|
|
||||||
<para>You won't see lines like "alias block-major-2 floppy" in <filename>/etc/modules.conf</filename> because modprobe already
|
|
||||||
knows about the standard drivers which will be used on most systems.</para>
|
|
||||||
|
|
||||||
<para>Now you know how modules get into the kernel. There's a bit more to the story if you want to write your own modules
|
<para>Now you know how modules get into the kernel. There's a bit more to the story if you want to write your own modules
|
||||||
which depend on other modules (we calling this `stacking modules'). But this will have to wait for a future chapter. We have
|
which depend on other modules (we calling this `stacking modules'). But this will have to wait for a future chapter.
|
||||||
a lot to cover before addressing this relatively high-level issue.</para>
|
We have a lot to cover before addressing this relatively high-level issue.</para>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -72,51 +72,91 @@
|
||||||
|
|
||||||
<indexterm><primary>insmod</primary></indexterm>
|
<indexterm><primary>insmod</primary></indexterm>
|
||||||
|
|
||||||
<para>Kernel modules need to be compiled with certain gcc options to make them work. In addition, they also need to be
|
<para>Kernel modules need to be compiled a bit different than regular userspace apps. Former kernel versions required us
|
||||||
compiled with certain symbols defined. Former kernel versions required us to care much about these settings,
|
to care much about these settings, which are usually stored in Makefiles. Although hierarchically organized, many redundant
|
||||||
which are usually stored in Makefiles. Although hierarchically organized, many redundant settings accumulated in
|
settings accumulated in sublevel Makefiles and made them large and rather difficult to maintain.
|
||||||
sublevel Makefiles and made them large and rather difficult to maintain.
|
|
||||||
|
|
||||||
Fortunately, there is a new way of doing these things, called kbuild, and the build process for external loadable modules is now
|
Fortunately, there is a new way of doing these things, called kbuild, and the build process for external loadable modules
|
||||||
fully integrated into the standard kernel build mechanism. To learn more on how to compile modules which are not part of the
|
is now fully integrated into the standard kernel build mechanism. To learn more on how to compile modules which are not
|
||||||
official kernel (as ours), see file <filename>linux/Documentation/kbuild/modules.txt</filename>.</para>
|
part of the official kernel (such as all the examples you'll find in this guide), see file
|
||||||
|
<filename>linux/Documentation/kbuild/modules.txt</filename>.</para>
|
||||||
|
|
||||||
<para>So, let's look at a simple Makefile for compiling a module named <filename>hello-1.c</filename>:</para>
|
<para>So, let's look at a simple Makefile for compiling a module named <filename>hello-1.c</filename>:</para>
|
||||||
|
|
||||||
<example><title>Makefile for a basic kernel module</title><programlisting><inlinegraphic fileref="lkmpg-examples/02-HelloWorld/Makefile.1" format="linespecific"/></inlinegraphic></programlisting></example>
|
<example><title>Makefile for a basic kernel module</title><programlisting><inlinegraphic fileref="lkmpg-examples/02-HelloWorld/Makefile.1" format="linespecific"/></inlinegraphic></programlisting></example>
|
||||||
|
|
||||||
<para>Now you can compile the module by issuing the command <command> make -C /usr/src/linux-`uname -r` SUBDIRS=$PWD modules </command>.
|
<para>From a technical point of view just the first line is really necessary,
|
||||||
|
the "all" and "clean" targets were added for pure convenience.</para>
|
||||||
|
|
||||||
|
<para>Now you can compile the module by issuing the command <command> make </command>.
|
||||||
You should obtain an output which resembles the following:</para>
|
You should obtain an output which resembles the following:</para>
|
||||||
|
|
||||||
<screen>
|
<screen>
|
||||||
[root@pcsenonsrv test_module]# make -C /usr/src/linux-`uname -r` SUBDIRS=$PWD modules
|
hostname:~/lkmpg-examples/02-HelloWorld# make
|
||||||
make: Entering directory `/usr/src/linux-2.6.x
|
make -C /lib/modules/2.6.11/build M=/root/lkmpg-examples/02-HelloWorld modules
|
||||||
CC [M] /root/test_module/hello-1.o
|
make[1]: Entering directory `/usr/src/linux-2.6.11'
|
||||||
Building modules, stage 2.
|
CC [M] /root/lkmpg-examples/02-HelloWorld/hello-1.o
|
||||||
|
Building modules, stage 2.
|
||||||
MODPOST
|
MODPOST
|
||||||
CC /root/test_module/hello-1.mod.o
|
CC /root/lkmpg-examples/02-HelloWorld/hello-1.mod.o
|
||||||
LD [M] /root/test_module/hello-1.ko
|
LD [M] /root/lkmpg-examples/02-HelloWorld/hello-1.ko
|
||||||
make: Leaving directory `/usr/src/linux-2.6.x
|
make[1]: Leaving directory `/usr/src/linux-2.6.11'
|
||||||
|
hostname:~/lkmpg-examples/02-HelloWorld#
|
||||||
</screen>
|
</screen>
|
||||||
|
|
||||||
<para>Please note that kernel 2.6 introduces a new file naming convention: kernel modules now have a <filename>.ko</filename>
|
<para>Note that kernel 2.6 introduces a new file naming convention: kernel modules now have a <filename>.ko</filename>
|
||||||
extension (in place of the old <filename>.o</filename> extension) which easily distinguishes them from conventional object files.
|
extension (in place of the old <filename>.o</filename> extension) which easily distinguishes them from conventional object
|
||||||
Additional details about Makefiles for kernel modules are available in <filename>linux/Documentation/kbuild/makefiles.txt</filename>.
|
files. The reason for this is that they contain an additional .modinfo section that where additional information about the
|
||||||
Be sure to read this and the related files before starting to dig into Makefiles.</para>
|
module is kept. We'll soon see what this information is good for. </para>
|
||||||
|
|
||||||
|
<para> Use <command> modinfo hello-*.ko </command> to see what kind of information it is. </para>
|
||||||
|
|
||||||
|
<screen>
|
||||||
|
hostname:~/lkmpg-examples/02-HelloWorld# modinfo hello-1.ko
|
||||||
|
filename: hello-1.ko
|
||||||
|
vermagic: 2.6.11 preempt PENTIUMII 4KSTACKS gcc-3.3
|
||||||
|
depends:
|
||||||
|
</screen>
|
||||||
|
|
||||||
|
<para> Nothing spectacular, so far. That changes once we're using modinfo on one of our the later examples,
|
||||||
|
<filename> hello-5.ko </filename>. </para>
|
||||||
|
|
||||||
|
<screen>
|
||||||
|
hostname:~/lkmpg-examples/02-HelloWorld# modinfo hello-5.ko
|
||||||
|
filename: hello-5.ko
|
||||||
|
license: GPL
|
||||||
|
author: Peter Jay Salzman
|
||||||
|
vermagic: 2.6.11 preempt PENTIUMII 4KSTACKS gcc-3.3
|
||||||
|
depends:
|
||||||
|
parm: myintArray:An array of integers (array of int)
|
||||||
|
parm: mystring:A character string (charp)
|
||||||
|
parm: mylong:A long integer (long)
|
||||||
|
parm: myint:An integer (int)
|
||||||
|
parm: myshort:A short integer (short)
|
||||||
|
hostname:~/lkmpg-examples/02-HelloWorld#
|
||||||
|
</screen>
|
||||||
|
|
||||||
|
<para> Lot's of useful information to see here. An author string for bugreports,
|
||||||
|
license information, even a short description of the parameters it accepts. </para>
|
||||||
|
|
||||||
|
<para> Additional details about Makefiles for kernel modules are available in
|
||||||
|
<filename>linux/Documentation/kbuild/makefiles.txt</filename>.
|
||||||
|
Be sure to read this and the related files before starting to hacking Makefiles.
|
||||||
|
It'll probably save you lots of work. </para>
|
||||||
|
|
||||||
<para>Now it is time to insert your freshly-compiled module it into the kernel with <command>insmod ./hello-1.ko</command>
|
<para>Now it is time to insert your freshly-compiled module it into the kernel with <command>insmod ./hello-1.ko</command>
|
||||||
(ignore anything you see about tainted kernels; we'll cover that shortly).</para>
|
(ignore anything you see about tainted kernels; we'll cover that shortly).</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
All modules
|
All modules loaded into the kernel are listed in <filename>/proc/modules</filename>.
|
||||||
loaded into the kernel are listed in <filename>/proc/modules</filename>. Go ahead and cat that file to see that your module
|
Go ahead and cat that file to see that your module is really a part of the kernel.
|
||||||
is really a part of the kernel. Congratulations, you are now the author of Linux kernel code! When the novelty wares off,
|
Congratulations, you are now the author of Linux kernel code! When the novelty wares off,
|
||||||
remove your module from the kernel by using <command>rmmod hello-1</command>. Take a look at
|
remove your module from the kernel by using <command>rmmod hello-1</command>. Take a look at
|
||||||
<filename>/var/log/messages</filename> just to see that it got logged to your system logfile.</para>
|
<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
|
<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. What
|
<function>init_module()</function>? Change the return value to something non-zero, recompile and load the module again.
|
||||||
happens?</para>
|
What happens?</para>
|
||||||
|
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
|
@ -129,8 +169,8 @@ make: Leaving directory `/usr/src/linux-2.6.x
|
||||||
<para>As of Linux 2.4, you can rename the init and cleanup functions of your modules; they no longer have to be called
|
<para>As of Linux 2.4, you can rename the init and cleanup functions of your modules; they no longer have to be called
|
||||||
<function>init_module()</function> and <function>cleanup_module()</function> respectively. This is done with the
|
<function>init_module()</function> and <function>cleanup_module()</function> respectively. This is done with the
|
||||||
<function>module_init()</function> and <function>module_exit()</function> macros. These macros are defined in <filename
|
<function>module_init()</function> and <function>module_exit()</function> macros. These macros are defined in <filename
|
||||||
role="header">linux/init.h</filename>. The only caveat is that your init and cleanup functions must be defined before calling
|
role="header">linux/init.h</filename>. The only caveat is that your init and cleanup functions must be defined before
|
||||||
the macros, otherwise you'll get compilation errors. Here's an example of this technique:</para>
|
calling the macros, otherwise you'll get compilation errors. Here's an example of this technique:</para>
|
||||||
|
|
||||||
<indexterm><primary>source file</primary><secondary>hello-2.c</secondary></indexterm>
|
<indexterm><primary>source file</primary><secondary>hello-2.c</secondary></indexterm>
|
||||||
|
|
||||||
|
@ -191,23 +231,21 @@ make: Leaving directory `/usr/src/linux-2.6.x
|
||||||
|
|
||||||
<sect1><title>Hello World (part 4): Licensing and Module Documentation</title>
|
<sect1><title>Hello World (part 4): Licensing and Module Documentation</title>
|
||||||
|
|
||||||
<para>If you're running kernel 2.4 or later, you might have noticed something like this when you loaded the previous example
|
<para>If you're running kernel 2.4 or later, you might have noticed something like this when you loaded proprietary modules:</para>
|
||||||
modules:</para>
|
|
||||||
|
|
||||||
<screen>
|
<screen>
|
||||||
# insmod hello-3.o
|
# insmod xxxxxx.o
|
||||||
Warning: loading hello-3.o will taint the kernel: no license
|
Warning: loading xxxxxx.ko will taint the kernel: no license
|
||||||
See http://www.tux.org/lkml/#export-tainted for information about tainted modules
|
See http://www.tux.org/lkml/#export-tainted for information about tainted modules
|
||||||
Hello, world 3
|
Module xxxxxx loaded, with warnings
|
||||||
Module hello-3 loaded, with warnings
|
|
||||||
</screen>
|
</screen>
|
||||||
|
|
||||||
<indexterm><primary><function>MODULE_LICENSE()</function></primary></indexterm>
|
<indexterm><primary><function>MODULE_LICENSE()</function></primary></indexterm>
|
||||||
|
|
||||||
<para>In kernel 2.4 and later, a mechanism was devised to identify code licensed under the GPL (and friends) so people can be
|
<para>In kernel 2.4 and later, a mechanism was devised to identify code licensed under the GPL (and friends) so people can
|
||||||
warned that the code is non open-source. This is accomplished by the <function>MODULE_LICENSE()</function> macro which is
|
be warned that the code is non open-source. This is accomplished by the <function>MODULE_LICENSE()</function> macro which
|
||||||
demonstrated in the next piece of code. By setting the license to GPL, you can keep the warning from being printed. This
|
is demonstrated in the next piece of code. By setting the license to GPL, you can keep the warning from being printed.
|
||||||
license mechanism is defined and documented in <filename role="headerfile">linux/module.h</filename>:
|
This license mechanism is defined and documented in <filename role="headerfile">linux/module.h</filename>:
|
||||||
|
|
||||||
<screen>
|
<screen>
|
||||||
/*
|
/*
|
||||||
|
@ -248,10 +286,20 @@ Module hello-3 loaded, with warnings
|
||||||
declares what types of devices the module supports.</para>
|
declares what types of devices the module supports.</para>
|
||||||
|
|
||||||
<para>These macros are all defined in <filename role="headerfile">linux/module.h</filename> and aren't used by the kernel
|
<para>These macros are all defined in <filename role="headerfile">linux/module.h</filename> and aren't used by the kernel
|
||||||
itself. They're simply for documentation and can be viewed by a tool like <application>objdump</application>. As an exercise
|
itself. They're simply for documentation and can be viewed by a tool like <application>objdump</application>.
|
||||||
to the reader, try grepping through <filename role="directory">linux/drivers</filename> to see how module authors use these
|
As an exercise to the reader, try and search for this macros in <filename role="directory">linux/drivers</filename>
|
||||||
macros to document their modules.</para>
|
to see how module authors use these macros to document their modules.</para>
|
||||||
|
|
||||||
|
<para> I'd recommend to use something like <command> grep -inr MODULE_AUTHOR * </command> in
|
||||||
|
<filename> /usr/src/linux-2.6.x/ </filename>. People unfamiliar with command line tools will probably like
|
||||||
|
some web base solution, search for sites that offer kernel trees that got indexed with LXR. (or setup it up on your
|
||||||
|
local machine).</para>
|
||||||
|
|
||||||
|
<para> Users of traditional Unix editors, like <command> emacs </command> or <command> vi </command> will also find
|
||||||
|
tag files useful. They can be generated by <command> make tags </command> or <command> make TAGS </command> in
|
||||||
|
<filename> /usr/src/linux-2.6.x/ </filename>. Once you've got such a tagfile in your kerneltree you can put the cursor
|
||||||
|
on some function call and use some key combination to directly jump to the definition function. </para>
|
||||||
|
|
||||||
<indexterm><primary>source file</primary><secondary>hello-4.c</secondary></indexterm>
|
<indexterm><primary>source file</primary><secondary>hello-4.c</secondary></indexterm>
|
||||||
|
|
||||||
|
|
||||||
|
@ -271,9 +319,9 @@ Module hello-3 loaded, with warnings
|
||||||
<para>To allow arguments to be passed to your module, declare the variables that will take the values of the command line
|
<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
|
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
|
role="headerfile">linux/module.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.o myvariable=5</command>. The variable declarations
|
command line arguments that are given, like <command>./insmod mymodule.ko myvariable=5</command>. The variable
|
||||||
and macros should be placed at the beginning of the module for clarity. The example code should clear up my admittedly lousy
|
declarations and macros should be placed at the beginning of the module for clarity. The example code should clear up my
|
||||||
explanation.</para>
|
admittedly lousy explanation.</para>
|
||||||
|
|
||||||
<para>The <function>MODULE_PARM()</function> macro takes 2 arguments: the name of the variable and its type. The supported
|
<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,
|
variable types are "<literal>b</literal>": single byte, "<literal>h</literal>": short int, "<literal>i</literal>": integer,
|
||||||
|
@ -314,7 +362,7 @@ MODULE_PARM (myintArray, "3-9i");
|
||||||
<para>I would recommend playing around with this code:</para>
|
<para>I would recommend playing around with this code:</para>
|
||||||
|
|
||||||
<screen>
|
<screen>
|
||||||
satan# insmod hello-5.o mystring="bebop" mybyte=255 myintArray=-1
|
satan# insmod hello-5.ko mystring="bebop" mybyte=255 myintArray=-1
|
||||||
mybyte is an 8 bit integer: 255
|
mybyte is an 8 bit integer: 255
|
||||||
myshort is a short integer: 1
|
myshort is a short integer: 1
|
||||||
myint is an integer: 20
|
myint is an integer: 20
|
||||||
|
@ -325,7 +373,7 @@ myintArray is -1 and 420
|
||||||
satan# rmmod hello-5
|
satan# rmmod hello-5
|
||||||
Goodbye, world 5
|
Goodbye, world 5
|
||||||
|
|
||||||
satan# insmod hello-5.o mystring="supercalifragilisticexpialidocious" \
|
satan# insmod hello-5.ko mystring="supercalifragilisticexpialidocious" \
|
||||||
> mybyte=256 myintArray=-1,-1
|
> mybyte=256 myintArray=-1,-1
|
||||||
mybyte is an 8 bit integer: 0
|
mybyte is an 8 bit integer: 0
|
||||||
myshort is a short integer: 1
|
myshort is a short integer: 1
|
||||||
|
@ -337,7 +385,7 @@ myintArray is -1 and -1
|
||||||
satan# rmmod hello-5
|
satan# rmmod hello-5
|
||||||
Goodbye, world 5
|
Goodbye, world 5
|
||||||
|
|
||||||
satan# insmod hello-5.o mylong=hello
|
satan# insmod hello-5.ko mylong=hello
|
||||||
hello-5.o: invalid argument syntax for mylong: 'h'
|
hello-5.o: invalid argument syntax for mylong: 'h'
|
||||||
</screen>
|
</screen>
|
||||||
|
|
||||||
|
@ -350,31 +398,8 @@ hello-5.o: invalid argument syntax for mylong: 'h'
|
||||||
<sect1><title>Modules Spanning Multiple Files</title>
|
<sect1><title>Modules Spanning Multiple Files</title>
|
||||||
|
|
||||||
<indexterm><primary>source files</primary><secondary>multiple</secondary></indexterm>
|
<indexterm><primary>source files</primary><secondary>multiple</secondary></indexterm>
|
||||||
<indexterm><primary>__NO_VERSION__</primary></indexterm>
|
|
||||||
<indexterm><primary>module.h</primary></indexterm>
|
<para>Sometimes it makes sense to divide a kernel module between several source files.</para>
|
||||||
<indexterm><primary>version.h</primary></indexterm>
|
|
||||||
<indexterm><primary>kernel\_version</primary></indexterm>
|
|
||||||
<indexterm><primary>ld</primary></indexterm>
|
|
||||||
<indexterm><primary>elf_i386</primary></indexterm>
|
|
||||||
|
|
||||||
<para>Sometimes it makes sense to divide a kernel module between several source files. In this case, you need to:</para>
|
|
||||||
|
|
||||||
<orderedlist>
|
|
||||||
|
|
||||||
<listitem><para>In all the source files but one, add the line <command>#define __NO_VERSION__</command>. This is important
|
|
||||||
because <filename role="headerfile">module.h</filename> normally includes the definition of
|
|
||||||
<varname>kernel_version</varname>, a global variable with the kernel version the module is compiled for. If you need
|
|
||||||
<filename role="headerfile">version.h</filename>, you need to include it yourself, because <filename
|
|
||||||
role="headerfile">module.h</filename> won't do it for you with <varname>__NO_VERSION__</varname>.</para></listitem>
|
|
||||||
|
|
||||||
<listitem><para>Compile all the source files as usual.</para></listitem>
|
|
||||||
|
|
||||||
<listitem><para>Combine all the object files into a single one. Under x86, use <command>ld -m elf_i386 -r -o <module
|
|
||||||
name.o> <1st src file.o> <2nd src file.o></command>.</para></listitem>
|
|
||||||
|
|
||||||
</orderedlist>
|
|
||||||
|
|
||||||
<para>The makefile will, once again, save us from having to get our hands dirty with compiling and linking the object files.</para>
|
|
||||||
|
|
||||||
<para>Here's an example of such a kernel module.</para>
|
<para>Here's an example of such a kernel module.</para>
|
||||||
|
|
||||||
|
@ -393,6 +418,10 @@ hello-5.o: invalid argument syntax for mylong: 'h'
|
||||||
|
|
||||||
<example><title>Makefile</title><programlisting><inlinegraphic fileref="lkmpg-examples/02-HelloWorld/Makefile" format="linespecific"/></inlinegraphic></programlisting></example>
|
<example><title>Makefile</title><programlisting><inlinegraphic fileref="lkmpg-examples/02-HelloWorld/Makefile" format="linespecific"/></inlinegraphic></programlisting></example>
|
||||||
|
|
||||||
|
<para>This is the complete makefile for all the examples we've seen so far. The first five lines are nothing special,
|
||||||
|
but for the last example we'll need two lines. First we invent an object name for our combined module, second we tell
|
||||||
|
<command> make </command> what object files are part of that module. </para>
|
||||||
|
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
<sect1><title>Building modules for a precompiled kernel</title>
|
<sect1><title>Building modules for a precompiled kernel</title>
|
||||||
|
@ -518,3 +547,4 @@ CC scripts/empty.o
|
||||||
<!--
|
<!--
|
||||||
vim: tw=128
|
vim: tw=128
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
|
@ -240,6 +240,14 @@ int register_chrdev(unsigned int major, const char *name, struct file_operations
|
||||||
use LKMPG version 2.4.x for kernels 2.4.x, use LKMPG version 2.6.x for kernels 2.6.x and so on.
|
use LKMPG version 2.4.x for kernels 2.4.x, use LKMPG version 2.6.x for kernels 2.6.x and so on.
|
||||||
Also make sure that you always use current, up to date versions of both, kernel and guide.
|
Also make sure that you always use current, up to date versions of both, kernel and guide.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para> Update: What we've said above was true for kernels up to and including 2.6.10. You might already
|
||||||
|
have noticed that recent kernels look different. In case you haven't they look like 2.6.x.y now.
|
||||||
|
The meaning of the first three items basically stays the same, but a subpatchlevel has been added and will
|
||||||
|
indicate security fixes till the next stable patchlevel is out. So people can choose between a stable
|
||||||
|
tree with security updates <emphasis> and </emphasis> use the latest kernel as developer tree.
|
||||||
|
Search the kernel mailing list archives if you're interested in the full story.
|
||||||
|
</para>
|
||||||
|
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
|
|
|
@ -3,23 +3,25 @@
|
||||||
<indexterm><primary><filename role=directory>/proc</filename> filesystem</primary></indexterm>
|
<indexterm><primary><filename role=directory>/proc</filename> filesystem</primary></indexterm>
|
||||||
<indexterm><primary>filesystem</primary><secondary><filename role=directory>/proc</filename></secondary></indexterm>
|
<indexterm><primary>filesystem</primary><secondary><filename role=directory>/proc</filename></secondary></indexterm>
|
||||||
|
|
||||||
<para>In Linux there is an additional mechanism for the kernel and kernel modules to send information to processes --- the
|
<para>
|
||||||
|
In Linux, there is an additional mechanism for the kernel and kernel modules to send information to processes --- the
|
||||||
<filename role="directory">/proc</filename> file system. Originally designed to allow easy access to information about
|
<filename role="directory">/proc</filename> file system. Originally designed to allow easy access to information about
|
||||||
processes (hence the name), it is now used by every bit of the kernel which has something interesting to report, such as
|
processes (hence the name), it is now used by every bit of the kernel which has something interesting to report, such as
|
||||||
<filename>/proc/modules</filename> which has the list of modules and <filename>/proc/meminfo</filename> which has memory usage
|
<filename>/proc/modules</filename> which provides the list of modules and <filename>/proc/meminfo</filename>
|
||||||
statistics.</para>
|
which stats memory usage statistics.
|
||||||
|
</para>
|
||||||
|
|
||||||
<indexterm><primary><filename>/proc/modules</filename></primary></indexterm>
|
<indexterm><primary><filename>/proc/modules</filename></primary></indexterm>
|
||||||
<indexterm><primary><filename>/proc/meminfo</filename></primary></indexterm>
|
<indexterm><primary><filename>/proc/meminfo</filename></primary></indexterm>
|
||||||
|
|
||||||
<para>The method to use the proc file system is very similar to the one used with device drivers --- you create a structure
|
<para>The method to use the proc file system is very similar to the one used with device drivers --- a structure is created
|
||||||
with all the information needed for the <filename role="directory">/proc</filename> file, including pointers to any handler
|
with all the information needed for the <filename role="directory">/proc</filename> file, including pointers to any handler
|
||||||
functions (in our case there is only one, the one called when somebody attempts to read from the <filename
|
functions (in our case there is only one, the one called when somebody attempts to read from the <filename
|
||||||
role="directory">/proc</filename> file). Then, <function>init_module</function> registers the structure with the kernel and
|
role="directory">/proc</filename> file). Then, <function>init_module</function> registers the structure with the kernel and
|
||||||
<function>cleanup_module</function> unregisters it.</para>
|
<function>cleanup_module</function> unregisters it.</para>
|
||||||
|
|
||||||
<para>The reason we use <function>proc_register_dynamic</function><footnote><para>In version 2.0, in version 2.2 this is done
|
<para>The reason we use <function>proc_register_dynamic</function><footnote><para>In version 2.0, in version 2.2 this is done
|
||||||
for us automatically if we set the inode to zero.</para></footnote> is because we don't want to determine the inode number
|
automatically if we set the inode to zero.</para></footnote> is because we don't want to determine the inode number
|
||||||
used for our file in advance, but to allow the kernel to determine it to prevent clashes. Normal file systems are located on a
|
used for our file in advance, but to allow the kernel to determine it to prevent clashes. Normal file systems are located on a
|
||||||
disk, rather than just in memory (which is where <filename role="directory">/proc</filename> is), and in that case the inode
|
disk, rather than just in memory (which is where <filename role="directory">/proc</filename> is), and in that case the inode
|
||||||
number is a pointer to a disk location where the file's index-node (inode for short) is located. The inode contains
|
number is a pointer to a disk location where the file's index-node (inode for short) is located. The inode contains
|
||||||
|
@ -30,19 +32,160 @@
|
||||||
<indexterm><primary><function>proc_register</function></primary></indexterm>
|
<indexterm><primary><function>proc_register</function></primary></indexterm>
|
||||||
<indexterm><primary>inode</primary></indexterm>
|
<indexterm><primary>inode</primary></indexterm>
|
||||||
|
|
||||||
<para>Because we don't get called when the file is opened or closed, there's no where for us to put
|
<para>
|
||||||
<varname>try_module_get</varname> and <varname>try_module_put</varname> in this module, and if the file is opened and
|
Because we don't get called when the file is opened or closed, there's nowhere for us to put
|
||||||
then the module is removed, there's no way to avoid the consequences. In the next chapter we'll see a harder to implement, but
|
<varname>try_module_get</varname> and <varname>try_module_put</varname> in this module, and if
|
||||||
more flexible, way of dealing with <filename role="directory">/proc</filename> files which will allow us to protect against
|
the file is opened and then the module is removed, there's no way to avoid the consequences.
|
||||||
this problem as well.</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Here a simple example showing how to use a /proc file. This is the HelloWorld for the /proc filesystem.
|
||||||
|
There are three parts: create the file <filename>/proc/helloworld</filename> in the function <function>init_module</function>,
|
||||||
|
return a value (and a buffer) when the file <filename>/proc/helloworld</filename> is read in the callback
|
||||||
|
function <function>procfs_read</function>, and delete the file <filename>/proc/helloworld</filename>
|
||||||
|
in the function <function>cleanup_module</function>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <filename>/proc/helloworld</filename> is created when the module is loaded with the function
|
||||||
|
<function>create_proc_entry</function>. The return value is a 'struct proc_dir_entry *', and it
|
||||||
|
will be used to configure the file <filename>/proc/helloworld</filename> (for example, the owner of this file).
|
||||||
|
A null return value means that the creation has failed.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Each time, everytime the file <filename>/proc/helloworld</filename> is read, the function
|
||||||
|
<function>procfs_read</function> is called.
|
||||||
|
Two parameters of this function are very important: the buffer (the first parameter) and the offset (the third one).
|
||||||
|
The content of the buffer will be returned to the application which read it (for example the cat command). The offset
|
||||||
|
is the current position in the file. If the return value of the function isn't null, then this function is
|
||||||
|
called again. So be careful with this function, if it never returns zero, the read function is called endlessly.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<screen>
|
||||||
|
% cat /proc/helloworld
|
||||||
|
HelloWorld!
|
||||||
|
</screen>
|
||||||
|
|
||||||
|
<example>
|
||||||
<example><title>procfs.c</title><programlisting><inlinegraphic fileref="lkmpg-examples/05-TheProcFileSystem/procfs.c" format="linespecific"/></inlinegraphic></programlisting></example>
|
<title>procfs1.c</title>
|
||||||
|
<programlisting>
|
||||||
|
<inlinegraphic fileref="lkmpg-examples/05-TheProcFileSystem/procfs1.c" format="linespecific"/></inlinegraphic>
|
||||||
|
</programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
|
|
||||||
|
<sect1><title>Read and Write a /proc File</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
We have seen a very simple example for a /proc file where we only read the file <filename>/proc/helloworld</filename>.
|
||||||
|
It's also possible to write in a /proc file. It works the same way as read, a function is called when the /proc
|
||||||
|
file is written. But there is a little difference with read, data comes from user, so you have to import data from
|
||||||
|
user space to kernel space (with <function>copy_from_user</function> or <function>get_user</function>)
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<indexterm><primary><function>get_user</function></primary></indexterm>
|
||||||
|
<indexterm><primary><function>put_user</function></primary></indexterm>
|
||||||
|
<indexterm><primary><function>copy_from_user</function></primary></indexterm>
|
||||||
|
<indexterm><primary><function>copy_to_user</function></primary></indexterm>
|
||||||
|
<indexterm><primary>memory segments</primary></indexterm>
|
||||||
|
<indexterm><primary>segment</primary><secondary>memory</secondary></indexterm>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The reason for <function>copy_from_user</function> or <function>get_user</function> is that Linux memory (on Intel
|
||||||
|
architecture, it may be different under some other processors) is segmented. This means that a pointer, by itself,
|
||||||
|
does not reference a unique location in memory, only a location in a memory segment, and you need to know which
|
||||||
|
memory segment it is to be able to use it. There is one memory segment for the kernel, and one for each of the processes.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The only memory segment accessible to a process is its own, so when writing regular programs to run as processes,
|
||||||
|
there's no need to worry about segments. When you write a kernel module, normally you want to access the kernel memory
|
||||||
|
segment, which is handled automatically by the system. However, when the content of a memory buffer needs to be passed between
|
||||||
|
the currently running process and the kernel, the kernel function receives a pointer to the memory buffer which is in the
|
||||||
|
process segment. The <function>put_user</function> and <function>get_user</function> macros allow you to access that
|
||||||
|
memory. These functions handle only one caracter, you can handle several caracters with <function>copy_to_user</function> and
|
||||||
|
<function>copy_from_user</function>. As the buffer (in read or write function) is in kernel space, for write function
|
||||||
|
you need to import data because it comes from user space, but not for the read function because data is already
|
||||||
|
in kernel space.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<indexterm><primary>read</primary><secondary>in the kernel</secondary></indexterm>
|
||||||
|
<indexterm><primary>write</primary><secondary>in the kernel</secondary></indexterm>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<title>procfs2.c</title>
|
||||||
|
<programlisting>
|
||||||
|
<inlinegraphic fileref="lkmpg-examples/05-TheProcFileSystem/procfs2.c" format="linespecific"/></inlinegraphic>
|
||||||
|
</programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
|
||||||
|
<sect1><title>Manage /proc file with standard filesystem</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
We have seen how to read and write a /proc file with the /proc interface. But it's also possible
|
||||||
|
to manage /proc file with inodes. The main interest is to use advanced function, like permissions.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
In Linux, there is a standard mechanism for file system registration. Since every file system has to have its own
|
||||||
|
functions to handle inode and file operations<footnote><para>The difference between the two is that file operations deal with
|
||||||
|
the file itself, and inode operations deal with ways of referencing the file, such as creating links to it.</para></footnote>,
|
||||||
|
there is a special structure to hold pointers to all those functions, <varname>struct inode_operations</varname>, which
|
||||||
|
includes a pointer to <varname>struct file_operations</varname>. In /proc, whenever we register a new file, we're allowed to
|
||||||
|
specify which <varname>struct inode_operations</varname> will be used to access to it. This is the mechanism we use, a
|
||||||
|
<varname>struct inode_operations</varname> which includes a pointer to a <varname>struct file_operations</varname> which
|
||||||
|
includes pointers to our <function>procfs_read</function> and <function>procfs_write</function> functions.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<indexterm><primary>filesystem</primary><secondary>registration</secondary></indexterm>
|
||||||
|
<indexterm><primary>filesystem registration</primary></indexterm>
|
||||||
|
<indexterm><primary><varname>struct inode_operations</varname></primary></indexterm>
|
||||||
|
<indexterm><primary><varname>inode_operations</varname> structure</primary></indexterm>
|
||||||
|
<indexterm><primary><varname>struct file_operations</varname></primary></indexterm>
|
||||||
|
<indexterm><primary><varname>file_operations</varname> structure</primary></indexterm>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Another interesting point here is the <function>module_permission</function> function. This function is called whenever
|
||||||
|
a process tries to do something with the <filename role="directory">/proc</filename> file, and it can decide whether to allow
|
||||||
|
access or not. Right now it is only based on the operation and the uid of the current user (as available in
|
||||||
|
<varname>current</varname>, a pointer to a structure which includes information on the currently running process), but it
|
||||||
|
could be based on anything we like, such as what other processes are doing with the same file, the time of day, or the last
|
||||||
|
input we received.</para>
|
||||||
|
|
||||||
|
<indexterm><primary>pointer</primary><secondary>current</secondary></indexterm>
|
||||||
|
<indexterm><primary>permission</primary></indexterm>
|
||||||
|
<indexterm><primary><varname>module_permissions</varname></primary></indexterm>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
It's important to note that the standard roles of read and write are reversed in the kernel. Read functions are used for
|
||||||
|
output, whereas write functions are used for input. The reason for that is that read and write refer to the user's point of
|
||||||
|
view --- if a process reads something from the kernel, then the kernel needs to output it, and if a process writes something
|
||||||
|
to the kernel, then the kernel receives it as input.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<title>procfs3.c</title>
|
||||||
|
<programlisting>
|
||||||
|
<inlinegraphic fileref="lkmpg-examples/05-TheProcFileSystem/procfs3.c" format="linespecific"/></inlinegraphic>
|
||||||
|
</programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Still hungry for procfs examples? Well, first of all keep in mind, there are rumors around, claiming
|
||||||
|
that procfs is on it's way out, consider using sysfs instead. Second, if you really can't get enough,
|
||||||
|
there's a highly recommendable bonus level for procfs below <filename> linux/Documentation/DocBook/ </filename>.
|
||||||
|
Use <command> make help </command> in your toplevel kernel directory for instructions about how to convert it into
|
||||||
|
your favourite format. Example: <command> make htmldocs </command>. Consider using this mechanism,
|
||||||
|
in case you want to document something kernel related yourself.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</sect1>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
vim:textwidth=128
|
vim:textwidth=128
|
||||||
|
|
|
@ -1,82 +1,10 @@
|
||||||
<sect1><title>Using /proc For Input</title>
|
<sect1><title>TODO: Write a chapter about sysfs</title>
|
||||||
|
|
||||||
<indexterm><primary>input</primary><secondary>using /proc for</secondary></indexterm>
|
|
||||||
<indexterm><primary>proc</primary><secondary>using for input</secondary></indexterm>
|
|
||||||
|
|
||||||
<para>So far we have two ways to generate output from kernel modules: we can register a device driver and
|
|
||||||
<command>mknod</command> a device file, or we can create a <filename role="directory">/proc</filename> file. This allows the
|
|
||||||
kernel module to tell us anything it likes. The only problem is that there is no way for us to talk back. The first way we'll
|
|
||||||
send input to kernel modules will be by writing back to the <filename role="directory">/proc</filename> file.</para>
|
|
||||||
|
|
||||||
<para>Because the proc filesystem was written mainly to allow the kernel to report its situation to processes, there are no
|
|
||||||
special provisions for input. The <varname>struct proc_dir_entry</varname> doesn't include a pointer to an input function,
|
|
||||||
the way it includes a pointer to an output function. Instead, to write into a <filename role="directory">/proc</filename>
|
|
||||||
file, we need to use the standard filesystem mechanism.</para>
|
|
||||||
|
|
||||||
<indexterm><primary><varname>proc_dir_entry</varname></primary></indexterm>
|
|
||||||
|
|
||||||
<para>In Linux there is a standard mechanism for file system registration. Since every file system has to have its own
|
|
||||||
functions to handle inode and file operations<footnote><para>The difference between the two is that file operations deal with
|
|
||||||
the file itself, and inode operations deal with ways of referencing the file, such as creating links to it.</para></footnote>,
|
|
||||||
there is a special structure to hold pointers to all those functions, <varname>struct inode_operations</varname>, which
|
|
||||||
includes a pointer to <varname>struct file_operations</varname>. In /proc, whenever we register a new file, we're allowed to
|
|
||||||
specify which <varname>struct inode_operations</varname> will be used for access to it. This is the mechanism we use, a
|
|
||||||
<varname>struct inode_operations</varname> which includes a pointer to a <varname>struct file_operations</varname> which
|
|
||||||
includes pointers to our <function>module_input</function> and <function>module_output</function> functions.</para>
|
|
||||||
|
|
||||||
<indexterm><primary>filesystem</primary><secondary>registration</secondary></indexterm>
|
|
||||||
<indexterm><primary>filesystem registration</primary></indexterm>
|
|
||||||
<indexterm><primary><varname>struct inode_operations</varname></primary></indexterm>
|
|
||||||
<indexterm><primary><varname>inode_operations</varname> structure</primary></indexterm>
|
|
||||||
<indexterm><primary><varname>struct file_operations</varname></primary></indexterm>
|
|
||||||
<indexterm><primary><varname>file_operations</varname> structure</primary></indexterm>
|
|
||||||
|
|
||||||
<para>It's important to note that the standard roles of read and write are reversed in the kernel. Read functions are used for
|
|
||||||
output, whereas write functions are used for input. The reason for that is that read and write refer to the user's point of
|
|
||||||
view --- if a process reads something from the kernel, then the kernel needs to output it, and if a process writes something
|
|
||||||
to the kernel, then the kernel receives it as input.</para>
|
|
||||||
|
|
||||||
<indexterm><primary>read</primary><secondary>in the kernel</secondary></indexterm>
|
|
||||||
<indexterm><primary>write</primary><secondary>in the kernel</secondary></indexterm>
|
|
||||||
|
|
||||||
<para>Another interesting point here is the <function>module_permission</function> function. This function is called whenever
|
|
||||||
a process tries to do something with the <filename role="directory">/proc</filename> file, and it can decide whether to allow
|
|
||||||
access or not. Right now it is only based on the operation and the uid of the current user (as available in
|
|
||||||
<varname>current</varname>, a pointer to a structure which includes information on the currently running process), but it
|
|
||||||
could be based on anything we like, such as what other processes are doing with the same file, the time of day, or the last
|
|
||||||
input we received.</para>
|
|
||||||
|
|
||||||
<indexterm><primary>pointer</primary><secondary>current</secondary></indexterm>
|
|
||||||
<indexterm><primary>permission</primary></indexterm>
|
|
||||||
<indexterm><primary><varname>module_permissions</varname></primary></indexterm>
|
|
||||||
|
|
||||||
<para>The reason for <function>put_user</function> and <function>get_user</function> is that Linux memory (under Intel
|
|
||||||
architecture, it may be different under some other processors) is segmented. This means that a pointer, by itself, does not
|
|
||||||
reference a unique location in memory, only a location in a memory segment, and you need to know which memory segment it is to
|
|
||||||
be able to use it. There is one memory segment for the kernel, and one of each of the processes.</para>
|
|
||||||
|
|
||||||
<indexterm><primary><function>put_user</function></primary></indexterm>
|
|
||||||
<indexterm><primary><function>get_user</function></primary></indexterm>
|
|
||||||
<indexterm><primary>memory segments</primary></indexterm>
|
|
||||||
<indexterm><primary>segment</primary><secondary>memory</secondary></indexterm>
|
|
||||||
|
|
||||||
<para>The only memory segment accessible to a process is its own, so when writing regular programs to run as processes,
|
|
||||||
there's no need to worry about segments. When you write a kernel module, normally you want to access the kernel memory
|
|
||||||
segment, which is handled automatically by the system. However, when the content of a memory buffer needs to be passed between
|
|
||||||
the currently running process and the kernel, the kernel function receives a pointer to the memory buffer which is in the
|
|
||||||
process segment. The <function>put_user</function> and <function>get_user</function> macros allow you to access that
|
|
||||||
memory.</para>
|
|
||||||
|
|
||||||
|
|
||||||
<example><title>procfs.c</title><programlisting><inlinegraphic fileref="lkmpg-examples/06-UsingProcForInput/procfs.c" format="linespecific"/></inlinegraphic></programlisting></example>
|
|
||||||
|
|
||||||
<para> Still hungry for procfs examples? Well, first of all keep in mind, there are rumors around, claiming
|
|
||||||
that procfs is on it's way out, consider using sysfs instead. Second, if you really can't get enough,
|
|
||||||
there's a highly recommendable bonus level for procfs below <filename> linux/Documentation/DocBook/ </filename>.
|
|
||||||
Use <command> make help </command> in your toplevel kernel directory for instructions about how to convert it into
|
|
||||||
your favourite format. Example: <command> make htmldocs </command>. Consider using this mechanism,
|
|
||||||
in case you want to document something kernel related yourself.</para>
|
|
||||||
|
|
||||||
|
<para> This is just a placeholder for now. Finally I'd like to see a
|
||||||
|
(yet to be written) chapter about sysfs instead here. If you are familiar
|
||||||
|
with sysfs and would like to take part in writing this chapter, feel free to
|
||||||
|
contact us (the LKMPG maintainers) for further details.
|
||||||
|
</para>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<sect1><title>Talking to Device Files (writes and IOCTLs)}</title>
|
<sect1><title>Talking to Device Files (writes and IOCTLs)</title>
|
||||||
|
|
||||||
<indexterm><primary>ioctl</primary></indexterm>
|
<indexterm><primary>ioctl</primary></indexterm>
|
||||||
<indexterm><primary>device files</primary><secondary>input to</secondary></indexterm>
|
<indexterm><primary>device files</primary><secondary>input to</secondary></indexterm>
|
||||||
|
|
|
@ -84,12 +84,15 @@
|
||||||
|
|
||||||
|
|
||||||
<para>Note that all the related problems make syscall stealing unfeasiable for production use. In order to keep people from
|
<para>Note that all the related problems make syscall stealing unfeasiable for production use. In order to keep people from
|
||||||
doing potential harmful things sys_call_table is no longer exported. This means, if you want to do something more than a mere
|
doing potential harmful things sys_call_table is no longer exported. This means, if you want to do something more than a
|
||||||
dry run of this example, you will have to patch your current kernel in order to have sys_call_table exported. In the example
|
mere dry run of this example, you will have to patch your current kernel in order to have sys_call_table exported.
|
||||||
directory you will find a README and the patch. As you can imagine, such modifications are not to be taken lightly. Do not try
|
In the example directory you will find a README and the patch. As you can imagine, such modifications are not to be
|
||||||
this on valueable systems (ie systems that you do not own - or cannot restore easily). It should go O.K., but kernel people,
|
taken lightly. Do not try this on valueable systems (ie systems that you do not own - or cannot restore easily).
|
||||||
choose to not support hacks like this in 2.6. and I'm sure they've had their reasons for that. For more details, see the
|
You'll need to get the complete sourcecode of this guide as a tarball in order to get the patch and the README.
|
||||||
README. If in doubt, saying N here and skipping this example is the safe choice.</para>
|
Depending on your kernel version, you might even need to hand apply the patch. Still here? Well, so is this chapter.
|
||||||
|
If Wyle E. Coyote was a kernel hacker, this would be the first thing he'd try. ;)
|
||||||
|
|
||||||
|
</para>
|
||||||
|
|
||||||
|
|
||||||
<indexterm><primary>try_module_get</primary></indexterm>
|
<indexterm><primary>try_module_get</primary></indexterm>
|
||||||
|
|
|
@ -3,10 +3,6 @@
|
||||||
<indexterm><primary>blocking processes</primary></indexterm>
|
<indexterm><primary>blocking processes</primary></indexterm>
|
||||||
<indexterm><primary>processes</primary><secondary>blocking</secondary></indexterm>
|
<indexterm><primary>processes</primary><secondary>blocking</secondary></indexterm>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<sect2><title>Enter Sandman</title>
|
|
||||||
|
|
||||||
<para>What do you do when somebody asks you for something you can't do right away? If you're a human being and you're
|
<para>What do you do when somebody asks you for something you can't do right away? If you're a human being and you're
|
||||||
bothered by a human being, the only thing you can say is: <quote>Not right now, I'm busy. <emphasis>Go
|
bothered by a human being, the only thing you can say is: <quote>Not right now, I'm busy. <emphasis>Go
|
||||||
away!</emphasis></quote>. But if you're a kernel module and you're bothered by a process, you have another possibility.
|
away!</emphasis></quote>. But if you're a kernel module and you're bothered by a process, you have another possibility.
|
||||||
|
@ -46,6 +42,11 @@
|
||||||
processes that the file is still open and go on with its life. When the other processes get a piece of the CPU, they'll
|
processes that the file is still open and go on with its life. When the other processes get a piece of the CPU, they'll
|
||||||
see that global variable and go back to sleep.</para>
|
see that global variable and go back to sleep.</para>
|
||||||
|
|
||||||
|
<para> So we'll use <command> tail -f </command> to keep the file open in the background, while
|
||||||
|
trying to access it with another process (again in the background, so that we need not switch to
|
||||||
|
a different vt). As soon as the first background process is killed with <command> kill %1 </command>,
|
||||||
|
the second is woken up, is able to access the file and finally terminates. </para>
|
||||||
|
|
||||||
<indexterm><primary>signal</primary></indexterm>
|
<indexterm><primary>signal</primary></indexterm>
|
||||||
<indexterm><primary>SIGINT</primary></indexterm>
|
<indexterm><primary>SIGINT</primary></indexterm>
|
||||||
<indexterm><primary>module_wake_up</primary></indexterm>
|
<indexterm><primary>module_wake_up</primary></indexterm>
|
||||||
|
@ -75,13 +76,36 @@
|
||||||
<command>cat_noblock</command>, available in the source directory for this chapter, can be used to open a file with
|
<command>cat_noblock</command>, available in the source directory for this chapter, can be used to open a file with
|
||||||
<parameter>O_NONBLOCK</parameter>.</para>
|
<parameter>O_NONBLOCK</parameter>.</para>
|
||||||
|
|
||||||
|
<screen>
|
||||||
|
hostname:~/lkmpg-examples/09-BlockingProcesses# insmod sleep.ko
|
||||||
|
hostname:~/lkmpg-examples/09-BlockingProcesses# cat_noblock /proc/sleep
|
||||||
|
Last input:
|
||||||
|
hostname:~/lkmpg-examples/09-BlockingProcesses# tail -f /proc/sleep &
|
||||||
|
Last input:
|
||||||
|
Last input:
|
||||||
|
Last input:
|
||||||
|
Last input:
|
||||||
|
Last input:
|
||||||
|
Last input:
|
||||||
|
Last input:
|
||||||
|
tail: /proc/sleep: file truncated
|
||||||
|
[1] 6540
|
||||||
|
hostname:~/lkmpg-examples/09-BlockingProcesses# cat_noblock /proc/sleep
|
||||||
|
Open would block
|
||||||
|
hostname:~/lkmpg-examples/09-BlockingProcesses# kill %1
|
||||||
|
[1]+ Terminated tail -f /proc/sleep
|
||||||
|
hostname:~/lkmpg-examples/09-BlockingProcesses# cat_noblock /proc/sleep
|
||||||
|
Last input:
|
||||||
|
hostname:~/lkmpg-examples/09-BlockingProcesses#
|
||||||
|
</screen>
|
||||||
|
|
||||||
<indexterm><primary>source file</primary><secondary>sleep.c</secondary></indexterm>
|
<indexterm><primary>source file</primary><secondary>sleep.c</secondary></indexterm>
|
||||||
|
|
||||||
|
|
||||||
<example><title>sleep.c</title><programlisting><inlinegraphic fileref="lkmpg-examples/09-BlockingProcesses/sleep.c" format="linespecific"/></inlinegraphic></programlisting></example>
|
<example><title>sleep.c</title><programlisting><inlinegraphic fileref="lkmpg-examples/09-BlockingProcesses/sleep.c" format="linespecific"/></inlinegraphic></programlisting></example>
|
||||||
|
|
||||||
|
<example><title>cat_noblock.c</title><programlisting><inlinegraphic fileref="lkmpg-examples/09-BlockingProcesses/cat_noblock.c" format="linespecific"/></inlinegraphic></programlisting></example>
|
||||||
|
|
||||||
</sect2>
|
|
||||||
|
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
|
@ -90,3 +114,4 @@
|
||||||
<!--
|
<!--
|
||||||
vim: tw=128
|
vim: tw=128
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,10 @@
|
||||||
<sect1><title>Flashing keyboard LEDs</title>
|
<sect1><title>Flashing keyboard LEDs</title>
|
||||||
<indexterm><primary>keyboard LEDs</primary><secondary>flashing</secondary></indexterm>
|
<indexterm><primary>keyboard LEDs</primary><secondary>flashing</secondary></indexterm>
|
||||||
|
|
||||||
<para>In certain conditions, you may desire for your module a simpler and more direct way to communicate to the external world that he's running.
|
<para>In certain conditions, you may desire a simpler and more direct way to communicate to the external world.
|
||||||
Flashing keyboard LEDs can be such a solution: It is an immediate way to attract attention or to display a status condition.
|
Flashing keyboard LEDs can be such a solution: It is an immediate way to attract attention or to display a status condition.
|
||||||
Keyboard LEDs are present on every hardware, they are always visible, they do not need any setup, and their use is rather simple and
|
Keyboard LEDs are present on every hardware, they are always visible, they do not need any setup, and their use is rather simple and
|
||||||
non-intrusive, if compared to writing to a tty or a file.</para>
|
non-intrusive, compared to writing to a tty or a file.</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
The following source code illustrates a minimal kernel module which, when loaded, starts blinking the keyboard LEDs until it is unloaded.
|
The following source code illustrates a minimal kernel module which, when loaded, starts blinking the keyboard LEDs until it is unloaded.
|
||||||
|
@ -45,14 +45,14 @@ The following source code illustrates a minimal kernel module which, when loaded
|
||||||
CONFIG_LL_DEBUG in <command> make menuconfig </command> is good for? If you activate that you get low level access to the serial port.
|
CONFIG_LL_DEBUG in <command> make menuconfig </command> is good for? If you activate that you get low level access to the serial port.
|
||||||
While this might not sound very powerful by itself, you can patch <filename>kernel/printk.c</filename> or any other
|
While this might not sound very powerful by itself, you can patch <filename>kernel/printk.c</filename> or any other
|
||||||
essential syscall to use printascii, thus makeing it possible to trace virtually everything what your code does over a
|
essential syscall to use printascii, thus makeing it possible to trace virtually everything what your code does over a
|
||||||
serial line. If your architecture does not support this and is equipped with a serial port, this might be one of the
|
serial line. If you find yourself porting the kernel to some new and former unsupported architecture this is usually
|
||||||
first things that should be implemented. Logging over a netconsole might also be worth a try.
|
amongst the first things that should be implemented. Logging over a netconsole might also be worth a try.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
While you have seen lots of stuff that can be used to aid debugging here, there are some things to be aware of.
|
While you have seen lots of stuff that can be used to aid debugging here, there are some things to be aware of.
|
||||||
Debugging is almost always intrusive. Adding debug code can change the situation enough to make the bug seem to dissappear.
|
Debugging is almost always intrusive. Adding debug code can change the situation enough to make the bug seem to dissappear.
|
||||||
Thus you should try to keep debug code to a minimum and make shure it does not show up in production code.
|
Thus you should try to keep debug code to a minimum and make sure it does not show up in production code.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
|
@ -13,15 +13,14 @@
|
||||||
kernel module which is in memory anyway.</para>
|
kernel module which is in memory anyway.</para>
|
||||||
|
|
||||||
<indexterm><primary>task</primary></indexterm>
|
<indexterm><primary>task</primary></indexterm>
|
||||||
<indexterm><primary>tq_struct</primary></indexterm>
|
<indexterm><primary>workqueue_struct</primary></indexterm>
|
||||||
<indexterm><primary>queue_task</primary></indexterm>
|
<indexterm><primary>queue_delayed_work</primary></indexterm>
|
||||||
<indexterm><primary>tq_timer</primary></indexterm>
|
|
||||||
|
|
||||||
<para>Instead of doing that, we can create a function that will be called once for every timer interrupt. The way we do this
|
<para>Instead of doing that, we can create a function that will be called once for every timer interrupt. The way we do this
|
||||||
is we create a task, held in a <structname>tq_struct</structname> structure, which will hold a pointer to the function. Then,
|
is we create a task, held in a <structname>workqueue_struct</structname> structure, which will hold a pointer to the function. Then,
|
||||||
we use <function>queue_task</function> to put that task on a task list called <structname>tq_timer</structname>, which is the
|
we use <function>queue_delayed_work</function> to put that task on a task list called <structname>my_workqueue</structname>,
|
||||||
list of tasks to be executed on the next timer interrupt. Because we want the function to keep on being executed, we need to
|
which is the list of tasks to be executed on the next timer interrupt. Because we want the function to keep on being executed,
|
||||||
put it back on <structname>tq_timer</structname> whenever it is called, for the next timer interrupt.</para>
|
we need to put it back on <structname>my_workqueue</structname> whenever it is called, for the next timer interrupt.</para>
|
||||||
|
|
||||||
<indexterm><primary>rmmod</primary></indexterm>
|
<indexterm><primary>rmmod</primary></indexterm>
|
||||||
<indexterm><primary>reference count</primary></indexterm>
|
<indexterm><primary>reference count</primary></indexterm>
|
||||||
|
@ -29,22 +28,8 @@
|
||||||
|
|
||||||
<para>There's one more point we need to remember here. When a module is removed by <command>rmmod</command>, first its
|
<para>There's one more point we need to remember here. When a module is removed by <command>rmmod</command>, first its
|
||||||
reference count is checked. If it is zero, <function>module_cleanup</function> is called. Then, the module is removed from
|
reference count is checked. If it is zero, <function>module_cleanup</function> is called. Then, the module is removed from
|
||||||
memory with all its functions. Nobody checks to see if the timer's task list happens to contain a pointer to one of those
|
memory with all its functions. Things need to be shut down properly, or bad things will happen. See the code below how
|
||||||
functions, which will no longer be available. Ages later (from the computer's perspective, from a human perspective it's
|
this can be done in a safe way. </para>
|
||||||
nothing, less than a hundredth of a second), the kernel has a timer interrupt and tries to call the function on the task list.
|
|
||||||
Unfortunately, the function is no longer there. In most cases, the memory page where it sat is unused, and you get an ugly
|
|
||||||
error message. But if some other code is now sitting at the same memory location, things could get <emphasis>very</emphasis>
|
|
||||||
ugly. Unfortunately, we don't have an easy way to unregister a task from a task list.</para>
|
|
||||||
|
|
||||||
<indexterm><primary>sleep_on</primary></indexterm>
|
|
||||||
<indexterm><primary>module_sleep_on</primary></indexterm>
|
|
||||||
|
|
||||||
<para>Since <function>cleanup_module</function> can't return with an error code (it's a void function), the solution is to not
|
|
||||||
let it return at all. Instead, it calls <function>sleep_on</function> or
|
|
||||||
<function>module_sleep_on</function><footnote><para>They're really the same.</para></footnote> to put the
|
|
||||||
<command>rmmod</command> process to sleep. Before that, it informs the function called on the timer interrupt to stop
|
|
||||||
attaching itself by setting a global variable. Then, on the next timer interrupt, the <command>rmmod</command> process will
|
|
||||||
be woken up, when our function is no longer in the queue and it's safe to remove the module.</para>
|
|
||||||
|
|
||||||
<indexterm><primary>source file</primary><secondary>sched.c</secondary></indexterm>
|
<indexterm><primary>source file</primary><secondary>sched.c</secondary></indexterm>
|
||||||
|
|
||||||
|
|
|
@ -42,32 +42,35 @@
|
||||||
<indexterm><primary>SA_INTERRUPT</primary></indexterm>
|
<indexterm><primary>SA_INTERRUPT</primary></indexterm>
|
||||||
<indexterm><primary>SA_SHIRQ</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
|
<para>The way to implement this is to call <function>request_irq()</function> to get your interrupt handler called
|
||||||
the relevant IRQ is received (there are 15 of them, plus 1 which is used to cascade the interrupt controllers, on Intel
|
when the relevant IRQ is received.
|
||||||
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>
|
<footnote><para>
|
||||||
<indexterm><primary>tq_immediate</primary></indexterm>
|
In practice IRQ handling can be a bit more complex. Hardware is often designed in a way that chains two interrupt
|
||||||
<indexterm><primary>mark_bh</primary></indexterm>
|
controllers, so that all the IRQs from interrupt controller B are cascaded to a certain IRQ from
|
||||||
<indexterm><primary>BH_IMMEDIATE</primary></indexterm>
|
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
|
<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>queue_work()</function> <function>mark_bh(BH_IMMEDIATE)</function> to schedule the bottom half.
|
||||||
<function>mark_bh(BH_IMMEDIATE)</function> to schedule the bottom half. The reason we can't use the standard
|
</sect2>
|
||||||
<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>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,91 +1,20 @@
|
||||||
<sect1><title>Changes between 2.0 and 2.2</title>
|
<sect1><title>Changes between 2.4 and 2.6</title>
|
||||||
|
|
||||||
<indexterm><primary>2.2 changes</primary></indexterm>
|
<indexterm><primary>2.6 changes</primary></indexterm>
|
||||||
<indexterm><primary>kernel</primary><secondary>versions</secondary></indexterm>
|
<indexterm><primary>kernel</primary><secondary>versions</secondary></indexterm>
|
||||||
|
|
||||||
|
<sect2><title>Changes between 2.4 and 2.6</title>
|
||||||
|
|
||||||
|
<para>I don't know the entire kernel well enough to document all of the changes.
|
||||||
<sect2><title>Changes between 2.0 and 2.2</title>
|
Some hints for porting can be found by comparing this version of the LKMPG with it's counterpart for
|
||||||
|
kernel 2.4. Apart from that, anybody who needs to port drivers from 2.4 to 2.6 kernels might want to visit
|
||||||
<para>I don't know the entire kernel well enough do document all of the changes. In the course of converting the examples
|
<ulink url="http://lwn.net/Articles/driver-porting/"> http://lwn.net/Articles/driver-porting/ </ulink>.
|
||||||
(or actually, adapting Emmanuel Papirakis's changes) I came across the following differences. I listed all of them here
|
If you still can't find an example that exactly meets your needs there, find a driver that's similar to your driver
|
||||||
together to help module programmers, especially those who learned from previous versions of this book and are most
|
and present in both kernel versions. File comparison tools like <command> xxdiff </command> or
|
||||||
familiar with the techniques I use, convert to the new version.</para>
|
<command> meld </command> can be a great help then. Also check if your driver is covered by docs in
|
||||||
|
<filename> linux/Documentation/ </filename>. Before starting with porting and in case you're stuck it's
|
||||||
<para>An additional resource for people who wish to convert to 2.2 is located on <ulink
|
a good idea to find an appropiate mailinglist and ask people there for pointers.
|
||||||
url="http://www.atnf.csiro.au/~rgooch/linux/docs/porting-to-2.2.html"> Richard Gooch's site </ulink>.</para>
|
</para>
|
||||||
|
|
||||||
<indexterm><primary>asm/uaccess.h</primary></indexterm>
|
|
||||||
<indexterm><primary>asm</primary><secondary>uaccess.h</secondary></indexterm>
|
|
||||||
<indexterm><primary>put_user</primary></indexterm>
|
|
||||||
<indexterm><primary>get_user</primary></indexterm>
|
|
||||||
<indexterm><primary>structure</primary><secondary>file_operations</secondary></indexterm>
|
|
||||||
<indexterm><primary>flush</primary></indexterm>
|
|
||||||
<indexterm><primary>close</primary></indexterm>
|
|
||||||
<indexterm><primary>read</primary></indexterm>
|
|
||||||
<indexterm><primary>write</primary></indexterm>
|
|
||||||
<indexterm><primary>ssize_t</primary></indexterm>
|
|
||||||
<indexterm><primary>proc_register_dynamic</primary></indexterm>
|
|
||||||
<indexterm><primary>signals</primary></indexterm>
|
|
||||||
<indexterm><primary>queue_task_irq</primary></indexterm>
|
|
||||||
<indexterm><primary>queue_task</primary></indexterm>
|
|
||||||
<indexterm><primary>interrupts</primary></indexterm>
|
|
||||||
<indexterm><primary>irqs</primary></indexterm>
|
|
||||||
<indexterm><primary>module</primary><secondary>parameters</secondary></indexterm>
|
|
||||||
<indexterm><primary>module parameters</primary></indexterm>
|
|
||||||
<indexterm><primary>MODULE_PARM</primary></indexterm>
|
|
||||||
<indexterm><primary>Symmetrical Multi-Processing</primary></indexterm>
|
|
||||||
<indexterm><primary>SMP</primary></indexterm>
|
|
||||||
|
|
||||||
<variablelist>
|
|
||||||
|
|
||||||
<varlistentry><term><filename class="headerfile">asm/uaccess.h</filename></term>
|
|
||||||
<listitem><para>If you need <function>put_user</function> or <function>get_user</function> you have to
|
|
||||||
<userinput>#include</userinput> it.</para></listitem></varlistentry>
|
|
||||||
|
|
||||||
<varlistentry><term><function>get_user</function></term>
|
|
||||||
<listitem><para>In version 2.2, <function>get_user</function> receives both the pointer into user memory and the
|
|
||||||
variable in kernel memory to fill with the information. The reason for this is that <function>get_user</function> can
|
|
||||||
now read two or four bytes at a time if the variable we read is two or four bytes long.</para></listitem></varlistentry>
|
|
||||||
|
|
||||||
<varlistentry><term><structname>file_operations</structname></term>
|
|
||||||
<listitem><para>This structure now has a flush function between the <function>open</function> and
|
|
||||||
<function>close</function> functions. </para> </listitem> </varlistentry>
|
|
||||||
|
|
||||||
<varlistentry><term><function>close</function> in <structname>file_operations</structname></term>
|
|
||||||
<listitem><para>In version 2.2, the <function>close</function> function returns an integer, so it's allowed to
|
|
||||||
fail.</para></listitem></varlistentry>
|
|
||||||
|
|
||||||
<varlistentry><term><function>read</function>,<function>write</function> in <structname>file_operations</structname></term>
|
|
||||||
<listitem><para>The headers for these functions changed. They now return <userinput>ssize_t</userinput> instead of an
|
|
||||||
integer, and their parameter list is different. The inode is no longer a parameter, and on the other hand the offset
|
|
||||||
into the file is.</para></listitem></varlistentry>
|
|
||||||
|
|
||||||
<varlistentry><term><function>proc_register_dynamic</function></term>
|
|
||||||
<listitem><para>This function no longer exists. Instead, you call the regular <function>proc_register</function>
|
|
||||||
<indexterm><primary>proc_register</primary></indexterm> and put zero in the inode field of the
|
|
||||||
structure.</para></listitem></varlistentry>
|
|
||||||
|
|
||||||
<varlistentry><term>Signals</term>
|
|
||||||
<listitem><para>The signals in the task structure are no longer a 32 bit integer, but an array of
|
|
||||||
<parameter>_NSIG_WORDS</parameter> <indexterm><primary>_NSIG_WORDS</primary></indexterm>
|
|
||||||
integers.</para></listitem></varlistentry>
|
|
||||||
|
|
||||||
<varlistentry><term><function>queue_task_irq</function></term>
|
|
||||||
<listitem><para>Even if you want to scheduale a task to happen from inside an interrupt handler, you use
|
|
||||||
<function>queue_task</function>, not <function>queue_task_irq</function>.</para></listitem></varlistentry>
|
|
||||||
|
|
||||||
<varlistentry><term>Module Parameters</term>
|
|
||||||
<listitem><para>You no longer just declare module parameters as global variables. In 2.2 you have to also use
|
|
||||||
<parameter>MODULE_PARM</parameter> to declare their type. This is a big improvement, because it allows the module to
|
|
||||||
receive string parameters which start with a digits, for example, without getting
|
|
||||||
confused.</para></listitem></varlistentry>
|
|
||||||
|
|
||||||
<varlistentry><term>Symmetrical Multi-Processing</term>
|
|
||||||
<listitem><para>The kernel is no longer inside one huge spinlock, which means that kernel modules have to be aware of
|
|
||||||
<acronym>SMP</acronym>.</para></listitem></varlistentry>
|
|
||||||
|
|
||||||
</variablelist>
|
|
||||||
|
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
<para>I hope I have helped you in your quest to become a better programmer, or at least to have fun through technology. And,
|
<para>I hope I have helped you in your quest to become a better programmer, or at least to have fun through technology. And,
|
||||||
if you do write useful kernel modules, I hope you publish them under the GPL, so I can use them too.</para>
|
if you do write useful kernel modules, I hope you publish them under the GPL, so I can use them too.</para>
|
||||||
|
|
||||||
|
<para>If you'd like to contribute to this guide, please contact one the maintainers for details. As you've
|
||||||
|
already seen, there's a placeholder chapter now, waiting to be filled with examples for sysfs. </para>
|
||||||
|
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,9 @@ obj-m += hello-4.o
|
||||||
obj-m += hello-5.o
|
obj-m += hello-5.o
|
||||||
obj-m += startstop.o
|
obj-m += startstop.o
|
||||||
startstop-objs := start.o stop.o
|
startstop-objs := start.o stop.o
|
||||||
|
|
||||||
|
all:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||||
|
|
||||||
|
clean:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||||
|
|
|
@ -1,2 +1,7 @@
|
||||||
obj-m += hello-1.o
|
obj-m += hello-1.o
|
||||||
|
|
||||||
|
all:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||||
|
|
||||||
|
clean:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||||
|
|
|
@ -1,2 +1,8 @@
|
||||||
obj-m += hello-1.o
|
obj-m += hello-1.o
|
||||||
obj-m += hello-2.o
|
obj-m += hello-2.o
|
||||||
|
|
||||||
|
all:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||||
|
|
||||||
|
clean:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
* hello-1.c - The simplest kernel module.
|
* hello-1.c - The simplest kernel module.
|
||||||
*/
|
*/
|
||||||
#include <linux/module.h> /* Needed by all modules */
|
#include <linux/module.h> /* Needed by all modules */
|
||||||
#include <linux/kernel.h> /* Needed for KERN_ALERT */
|
#include <linux/kernel.h> /* Needed for KERN_INFO */
|
||||||
|
|
||||||
int init_module(void)
|
int init_module(void)
|
||||||
{
|
{
|
||||||
printk("<1>Hello world 1.\n");
|
printk(KERN_INFO "Hello world 1.\n");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A non 0 return means init_module failed; module can't be loaded.
|
* A non 0 return means init_module failed; module can't be loaded.
|
||||||
|
@ -16,5 +16,5 @@ int init_module(void)
|
||||||
|
|
||||||
void cleanup_module(void)
|
void cleanup_module(void)
|
||||||
{
|
{
|
||||||
printk(KERN_ALERT "Goodbye world 1.\n");
|
printk(KERN_INFO "Goodbye world 1.\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,18 +3,18 @@
|
||||||
* This is preferred over using init_module() and cleanup_module().
|
* This is preferred over using init_module() and cleanup_module().
|
||||||
*/
|
*/
|
||||||
#include <linux/module.h> /* Needed by all modules */
|
#include <linux/module.h> /* Needed by all modules */
|
||||||
#include <linux/kernel.h> /* Needed for KERN_ALERT */
|
#include <linux/kernel.h> /* Needed for KERN_INFO */
|
||||||
#include <linux/init.h> /* Needed for the macros */
|
#include <linux/init.h> /* Needed for the macros */
|
||||||
|
|
||||||
static int __init hello_2_init(void)
|
static int __init hello_2_init(void)
|
||||||
{
|
{
|
||||||
printk(KERN_ALERT "Hello, world 2\n");
|
printk(KERN_INFO "Hello, world 2\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit hello_2_exit(void)
|
static void __exit hello_2_exit(void)
|
||||||
{
|
{
|
||||||
printk(KERN_ALERT "Goodbye, world 2\n");
|
printk(KERN_INFO "Goodbye, world 2\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(hello_2_init);
|
module_init(hello_2_init);
|
||||||
|
|
|
@ -2,20 +2,20 @@
|
||||||
* hello-3.c - Illustrating the __init, __initdata and __exit macros.
|
* hello-3.c - Illustrating the __init, __initdata and __exit macros.
|
||||||
*/
|
*/
|
||||||
#include <linux/module.h> /* Needed by all modules */
|
#include <linux/module.h> /* Needed by all modules */
|
||||||
#include <linux/kernel.h> /* Needed for KERN_ALERT */
|
#include <linux/kernel.h> /* Needed for KERN_INFO */
|
||||||
#include <linux/init.h> /* Needed for the macros */
|
#include <linux/init.h> /* Needed for the macros */
|
||||||
|
|
||||||
static int hello3_data __initdata = 3;
|
static int hello3_data __initdata = 3;
|
||||||
|
|
||||||
static int __init hello_3_init(void)
|
static int __init hello_3_init(void)
|
||||||
{
|
{
|
||||||
printk(KERN_ALERT "Hello, world %d\n", hello3_data);
|
printk(KERN_INFO "Hello, world %d\n", hello3_data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit hello_3_exit(void)
|
static void __exit hello_3_exit(void)
|
||||||
{
|
{
|
||||||
printk(KERN_ALERT "Goodbye, world 3\n");
|
printk(KERN_INFO "Goodbye, world 3\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(hello_3_init);
|
module_init(hello_3_init);
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
/*
|
/*
|
||||||
* hello-4.c - Demonstrates module documentation.
|
* hello-4.c - Demonstrates module documentation.
|
||||||
*/
|
*/
|
||||||
#include <linux/module.h>
|
#include <linux/module.h> /* Needed by all modules */
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h> /* Needed for KERN_INFO */
|
||||||
#include <linux/init.h>
|
#include <linux/init.h> /* Needed for the macros */
|
||||||
#define DRIVER_AUTHOR "Peter Jay Salzman <p@dirac.org>"
|
#define DRIVER_AUTHOR "Peter Jay Salzman <p@dirac.org>"
|
||||||
#define DRIVER_DESC "A sample driver"
|
#define DRIVER_DESC "A sample driver"
|
||||||
|
|
||||||
static int __init init_hello_4(void)
|
static int __init init_hello_4(void)
|
||||||
{
|
{
|
||||||
printk(KERN_ALERT "Hello, world 4\n");
|
printk(KERN_INFO "Hello, world 4\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit cleanup_hello_4(void)
|
static void __exit cleanup_hello_4(void)
|
||||||
{
|
{
|
||||||
printk(KERN_ALERT "Goodbye, world 4\n");
|
printk(KERN_INFO "Goodbye, world 4\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(init_hello_4);
|
module_init(init_hello_4);
|
||||||
|
|
|
@ -14,6 +14,8 @@ static short int myshort = 1;
|
||||||
static int myint = 420;
|
static int myint = 420;
|
||||||
static long int mylong = 9999;
|
static long int mylong = 9999;
|
||||||
static char *mystring = "blah";
|
static char *mystring = "blah";
|
||||||
|
static int myintArray[2] = { -1, -1 };
|
||||||
|
static int arr_argc = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* module_param(foo, int, 0000)
|
* module_param(foo, int, 0000)
|
||||||
|
@ -32,19 +34,36 @@ MODULE_PARM_DESC(mylong, "A long integer");
|
||||||
module_param(mystring, charp, 0000);
|
module_param(mystring, charp, 0000);
|
||||||
MODULE_PARM_DESC(mystring, "A character string");
|
MODULE_PARM_DESC(mystring, "A character string");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* module_param_array(name, type, num, perm);
|
||||||
|
* The first param is the parameter's (in this case the array's) name
|
||||||
|
* The second param is the data type of the elements of the array
|
||||||
|
* The third argument is a pointer to the variable that will store the number
|
||||||
|
* of elements of the array initialized by the user at module loading time
|
||||||
|
* The fourth argument is the permission bits
|
||||||
|
*/
|
||||||
|
module_param_array(myintArray, int, &arr_argc, 0000);
|
||||||
|
MODULE_PARM_DESC(myintArray, "An array of integers");
|
||||||
|
|
||||||
static int __init hello_5_init(void)
|
static int __init hello_5_init(void)
|
||||||
{
|
{
|
||||||
printk(KERN_ALERT "Hello, world 5\n=============\n");
|
int i;
|
||||||
printk(KERN_ALERT "myshort is a short integer: %hd\n", myshort);
|
printk(KERN_INFO "Hello, world 5\n=============\n");
|
||||||
printk(KERN_ALERT "myint is an integer: %d\n", myint);
|
printk(KERN_INFO "myshort is a short integer: %hd\n", myshort);
|
||||||
printk(KERN_ALERT "mylong is a long integer: %ld\n", mylong);
|
printk(KERN_INFO "myint is an integer: %d\n", myint);
|
||||||
printk(KERN_ALERT "mystring is a string: %s\n", mystring);
|
printk(KERN_INFO "mylong is a long integer: %ld\n", mylong);
|
||||||
|
printk(KERN_INFO "mystring is a string: %s\n", mystring);
|
||||||
|
for (i = 0; i < (sizeof myintArray / sizeof (int)); i++)
|
||||||
|
{
|
||||||
|
printk(KERN_INFO "myintArray[%d] = %d\n", i, myintArray[i]);
|
||||||
|
}
|
||||||
|
printk(KERN_INFO "got %d arguments for myintArray.\n", arr_argc);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit hello_5_exit(void)
|
static void __exit hello_5_exit(void)
|
||||||
{
|
{
|
||||||
printk(KERN_ALERT "Goodbye, world 5\n");
|
printk(KERN_INFO "Goodbye, world 5\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(hello_5_init);
|
module_init(hello_5_init);
|
||||||
|
|
|
@ -7,6 +7,6 @@
|
||||||
|
|
||||||
int init_module(void)
|
int init_module(void)
|
||||||
{
|
{
|
||||||
printk("Hello, world - this is the kernel speaking\n");
|
printk(KERN_INFO "Hello, world - this is the kernel speaking\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,5 +7,5 @@
|
||||||
|
|
||||||
void cleanup_module()
|
void cleanup_module()
|
||||||
{
|
{
|
||||||
printk("<1>Short is the life of a kernel module\n");
|
printk(KERN_INFO "Short is the life of a kernel module\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,12 @@
|
||||||
|
#
|
||||||
|
# This is a sort a recursive Makefile
|
||||||
|
# This Makefile is used to compile the module
|
||||||
|
#
|
||||||
|
|
||||||
obj-m += chardev.o
|
obj-m += chardev.o
|
||||||
|
|
||||||
|
all:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||||
|
|
||||||
|
clean:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||||
|
|
|
@ -40,29 +40,30 @@ static struct file_operations fops = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Functions
|
* This function is called when the module is loaded
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int init_module(void)
|
int init_module(void)
|
||||||
{
|
{
|
||||||
Major = register_chrdev(0, DEVICE_NAME, &fops);
|
Major = register_chrdev(0, DEVICE_NAME, &fops);
|
||||||
|
|
||||||
if (Major < 0) {
|
if (Major < 0) {
|
||||||
printk("Registering the character device failed with %d\n",
|
printk(KERN_ALERT "Registering char device failed with %d\n", Major);
|
||||||
Major);
|
return Major;
|
||||||
return Major;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printk("<1>I was assigned major number %d. To talk to\n", Major);
|
printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major);
|
||||||
printk("<1>the driver, create a dev file with\n");
|
printk(KERN_INFO "the driver, create a dev file with\n");
|
||||||
printk("'mknod /dev/hello c %d 0'.\n", Major);
|
printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n", DEVICE_NAME, Major);
|
||||||
printk("<1>Try various minor numbers. Try to cat and echo to\n");
|
printk(KERN_INFO "Try various minor numbers. Try to cat and echo to\n");
|
||||||
printk("the device file.\n");
|
printk(KERN_INFO "the device file.\n");
|
||||||
printk("<1>Remove the device file and module when done.\n");
|
printk(KERN_INFO "Remove the device file and module when done.\n");
|
||||||
|
|
||||||
return 0;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is called when the module is unloaded
|
||||||
|
*/
|
||||||
void cleanup_module(void)
|
void cleanup_module(void)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -70,7 +71,7 @@ void cleanup_module(void)
|
||||||
*/
|
*/
|
||||||
int ret = unregister_chrdev(Major, DEVICE_NAME);
|
int ret = unregister_chrdev(Major, DEVICE_NAME);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
printk("Error in unregister_chrdev: %d\n", ret);
|
printk(KERN_ALERT "Error in unregister_chrdev: %d\n", ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -84,8 +85,10 @@ void cleanup_module(void)
|
||||||
static int device_open(struct inode *inode, struct file *file)
|
static int device_open(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
static int counter = 0;
|
static int counter = 0;
|
||||||
|
|
||||||
if (Device_Open)
|
if (Device_Open)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
Device_Open++;
|
Device_Open++;
|
||||||
sprintf(msg, "I already told you %d times Hello world!\n", counter++);
|
sprintf(msg, "I already told you %d times Hello world!\n", counter++);
|
||||||
msg_Ptr = msg;
|
msg_Ptr = msg;
|
||||||
|
@ -160,6 +163,6 @@ static ssize_t device_read(struct file *filp, /* see include/linux/fs.h */
|
||||||
static ssize_t
|
static ssize_t
|
||||||
device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
|
device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
|
||||||
{
|
{
|
||||||
printk("<1>Sorry, this operation isn't supported.\n");
|
printk(KERN_ALERT "Sorry, this operation isn't supported.\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,7 @@
|
||||||
obj-m += procfs.o
|
obj-m += procfs1.o procfs2.o procfs3.o
|
||||||
|
|
||||||
|
all:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||||
|
|
||||||
|
clean:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||||
|
|
|
@ -1 +1,7 @@
|
||||||
obj-m += procfs.o
|
obj-m += procfs.o
|
||||||
|
|
||||||
|
all:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||||
|
|
||||||
|
clean:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||||
|
|
|
@ -165,10 +165,12 @@ int init_module()
|
||||||
Our_Proc_File->gid = 0;
|
Our_Proc_File->gid = 0;
|
||||||
Our_Proc_File->size = 80;
|
Our_Proc_File->size = 80;
|
||||||
|
|
||||||
|
printk(KERN_INFO "/proc/rw_test created\n");
|
||||||
return 0; /* success */
|
return 0; /* success */
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanup_module()
|
void cleanup_module()
|
||||||
{
|
{
|
||||||
remove_proc_entry(PROC_ENTRY_FILENAME, &proc_root);
|
remove_proc_entry(PROC_ENTRY_FILENAME, &proc_root);
|
||||||
|
printk(KERN_INFO "/proc/rw_test removed\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,13 @@
|
||||||
obj-m += chardev.o
|
obj-m += chardev.o
|
||||||
|
|
||||||
|
all: ioctl module
|
||||||
|
|
||||||
|
module:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||||
|
|
||||||
|
install:
|
||||||
|
mknod /proc/char_dev c 100 0
|
||||||
|
|
||||||
|
clean:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||||
|
rm -f ioctl
|
||||||
|
|
|
@ -36,7 +36,7 @@ static char *Message_Ptr;
|
||||||
static int device_open(struct inode *inode, struct file *file)
|
static int device_open(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printk("device_open(%p)\n", file);
|
printk(KERN_INFO "device_open(%p)\n", file);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -57,7 +57,7 @@ static int device_open(struct inode *inode, struct file *file)
|
||||||
static int device_release(struct inode *inode, struct file *file)
|
static int device_release(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printk("device_release(%p,%p)\n", inode, file);
|
printk(KERN_INFO "device_release(%p,%p)\n", inode, file);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -85,7 +85,7 @@ static ssize_t device_read(struct file *file, /* see include/linux/fs.h */
|
||||||
int bytes_read = 0;
|
int bytes_read = 0;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printk("device_read(%p,%p,%d)\n", file, buffer, length);
|
printk(KERN_INFO "device_read(%p,%p,%d)\n", file, buffer, length);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -113,7 +113,7 @@ static ssize_t device_read(struct file *file, /* see include/linux/fs.h */
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printk("Read %d bytes, %d left\n", bytes_read, length);
|
printk(KERN_INFO "Read %d bytes, %d left\n", bytes_read, length);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -134,7 +134,7 @@ device_write(struct file *file,
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printk("device_write(%p,%s,%d)", file, buffer, length);
|
printk(KERN_INFO "device_write(%p,%s,%d)", file, buffer, length);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (i = 0; i < length && i < BUF_LEN; i++)
|
for (i = 0; i < length && i < BUF_LEN; i++)
|
||||||
|
@ -247,20 +247,20 @@ int init_module()
|
||||||
* Negative values signify an error
|
* Negative values signify an error
|
||||||
*/
|
*/
|
||||||
if (ret_val < 0) {
|
if (ret_val < 0) {
|
||||||
printk("%s failed with %d\n",
|
printk(KERN_ALERT "%s failed with %d\n",
|
||||||
"Sorry, registering the character device ", ret_val);
|
"Sorry, registering the character device ", ret_val);
|
||||||
return ret_val;
|
return ret_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
printk("%s The major device number is %d.\n",
|
printk(KERN_INFO "%s The major device number is %d.\n",
|
||||||
"Registeration is a success", MAJOR_NUM);
|
"Registeration is a success", MAJOR_NUM);
|
||||||
printk("If you want to talk to the device driver,\n");
|
printk(KERN_INFO "If you want to talk to the device driver,\n");
|
||||||
printk("you'll have to create a device file. \n");
|
printk(KERN_INFO "you'll have to create a device file. \n");
|
||||||
printk("We suggest you use:\n");
|
printk(KERN_INFO "We suggest you use:\n");
|
||||||
printk("mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM);
|
printk(KERN_INFO "mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM);
|
||||||
printk("The device file name is important, because\n");
|
printk(KERN_INFO "The device file name is important, because\n");
|
||||||
printk("the ioctl program assumes that's the\n");
|
printk(KERN_INFO "the ioctl program assumes that's the\n");
|
||||||
printk("file you'll use.\n");
|
printk(KERN_INFO "file you'll use.\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -281,5 +281,5 @@ void cleanup_module()
|
||||||
* If there's an error, report it
|
* If there's an error, report it
|
||||||
*/
|
*/
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
printk("Error in module_unregister_chrdev: %d\n", ret);
|
printk(KERN_ALERT "Error: unregister_chrdev: %d\n", ret);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,7 @@
|
||||||
obj-m += syscall.o
|
obj-m += syscall.o
|
||||||
|
|
||||||
|
all:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||||
|
|
||||||
|
clean:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||||
|
|
|
@ -112,13 +112,13 @@ int init_module()
|
||||||
* Warning - too late for it now, but maybe for
|
* Warning - too late for it now, but maybe for
|
||||||
* next time...
|
* next time...
|
||||||
*/
|
*/
|
||||||
printk("I'm dangerous. I hope you did a ");
|
printk(KERN_ALERT "I'm dangerous. I hope you did a ");
|
||||||
printk("sync before you insmod'ed me.\n");
|
printk(KERN_ALERT "sync before you insmod'ed me.\n");
|
||||||
printk("My counterpart, cleanup_module(), is even");
|
printk(KERN_ALERT "My counterpart, cleanup_module(), is even");
|
||||||
printk("more dangerous. If\n");
|
printk(KERN_ALERT "more dangerous. If\n");
|
||||||
printk("you value your file system, it will ");
|
printk(KERN_ALERT "you value your file system, it will ");
|
||||||
printk("be \"sync; rmmod\" \n");
|
printk(KERN_ALERT "be \"sync; rmmod\" \n");
|
||||||
printk("when you remove this module.\n");
|
printk(KERN_ALERT "when you remove this module.\n");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Keep a pointer to the original function in
|
* Keep a pointer to the original function in
|
||||||
|
@ -133,7 +133,7 @@ int init_module()
|
||||||
* call foo, go to sys_call_table[__NR_foo].
|
* call foo, go to sys_call_table[__NR_foo].
|
||||||
*/
|
*/
|
||||||
|
|
||||||
printk("Spying on UID:%d\n", uid);
|
printk(KERN_INFO "Spying on UID:%d\n", uid);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -147,10 +147,10 @@ void cleanup_module()
|
||||||
* Return the system call back to normal
|
* Return the system call back to normal
|
||||||
*/
|
*/
|
||||||
if (sys_call_table[__NR_open] != our_sys_open) {
|
if (sys_call_table[__NR_open] != our_sys_open) {
|
||||||
printk("Somebody else also played with the ");
|
printk(KERN_ALERT "Somebody else also played with the ");
|
||||||
printk("open system call\n");
|
printk(KERN_ALERT "open system call\n");
|
||||||
printk("The system may be left in ");
|
printk(KERN_ALERT "The system may be left in ");
|
||||||
printk("an unstable state.\n");
|
printk(KERN_ALERT "an unstable state.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
sys_call_table[__NR_open] = original_call;
|
sys_call_table[__NR_open] = original_call;
|
||||||
|
|
|
@ -1 +1,10 @@
|
||||||
obj-m += sleep.o
|
obj-m += sleep.o
|
||||||
|
|
||||||
|
all: cat_noblock module
|
||||||
|
|
||||||
|
module:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||||
|
|
||||||
|
clean:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||||
|
rm -f cat_noblock
|
||||||
|
|
|
@ -276,8 +276,15 @@ static struct inode_operations Inode_Ops_4_Our_Proc_File = {
|
||||||
|
|
||||||
int init_module()
|
int init_module()
|
||||||
{
|
{
|
||||||
int rv = 0;
|
|
||||||
Our_Proc_File = create_proc_entry(PROC_ENTRY_FILENAME, 0644, NULL);
|
Our_Proc_File = create_proc_entry(PROC_ENTRY_FILENAME, 0644, NULL);
|
||||||
|
|
||||||
|
if (Our_Proc_File == NULL) {
|
||||||
|
remove_proc_entry(PROC_ENTRY_FILENAME, &proc_root);
|
||||||
|
printk(KERN_ALERT "Error: Could not initialize /proc/test\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
Our_Proc_File->owner = THIS_MODULE;
|
Our_Proc_File->owner = THIS_MODULE;
|
||||||
Our_Proc_File->proc_iops = &Inode_Ops_4_Our_Proc_File;
|
Our_Proc_File->proc_iops = &Inode_Ops_4_Our_Proc_File;
|
||||||
Our_Proc_File->proc_fops = &File_Ops_4_Our_Proc_File;
|
Our_Proc_File->proc_fops = &File_Ops_4_Our_Proc_File;
|
||||||
|
@ -285,14 +292,10 @@ int init_module()
|
||||||
Our_Proc_File->uid = 0;
|
Our_Proc_File->uid = 0;
|
||||||
Our_Proc_File->gid = 0;
|
Our_Proc_File->gid = 0;
|
||||||
Our_Proc_File->size = 80;
|
Our_Proc_File->size = 80;
|
||||||
|
|
||||||
if (Our_Proc_File == NULL) {
|
printk(KERN_INFO "/proc/test created\n");
|
||||||
rv = -ENOMEM;
|
|
||||||
remove_proc_entry(PROC_ENTRY_FILENAME, &proc_root);
|
return 0;
|
||||||
printk(KERN_INFO "Error: Could not initialize /proc/test\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -304,4 +307,6 @@ int init_module()
|
||||||
void cleanup_module()
|
void cleanup_module()
|
||||||
{
|
{
|
||||||
remove_proc_entry(PROC_ENTRY_FILENAME, &proc_root);
|
remove_proc_entry(PROC_ENTRY_FILENAME, &proc_root);
|
||||||
|
|
||||||
|
printk(KERN_INFO "/proc/test removed\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,8 @@
|
||||||
obj-m += print_string.o
|
obj-m += print_string.o
|
||||||
obj-m += kbleds.o
|
obj-m += kbleds.o
|
||||||
|
|
||||||
|
all:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||||
|
|
||||||
|
clean:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||||
|
|
|
@ -1 +1,7 @@
|
||||||
obj-m += sched.o
|
obj-m += sched.o
|
||||||
|
|
||||||
|
all:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||||
|
|
||||||
|
clean:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||||
|
|
|
@ -76,10 +76,8 @@ procfile_read(char *buffer,
|
||||||
*/
|
*/
|
||||||
static char my_buffer[80];
|
static char my_buffer[80];
|
||||||
|
|
||||||
static int count = 1;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We give all of our information in one go, so if the anybody asks us
|
* We give all of our information in one go, so if anybody asks us
|
||||||
* if we have more information the answer should always be no.
|
* if we have more information the answer should always be no.
|
||||||
*/
|
*/
|
||||||
if (offset > 0)
|
if (offset > 0)
|
||||||
|
@ -89,7 +87,6 @@ procfile_read(char *buffer,
|
||||||
* Fill the buffer and get its length
|
* Fill the buffer and get its length
|
||||||
*/
|
*/
|
||||||
len = sprintf(my_buffer, "Timer called %d times so far\n", TimerIntrpt);
|
len = sprintf(my_buffer, "Timer called %d times so far\n", TimerIntrpt);
|
||||||
count++;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tell the function which called us where the buffer is
|
* Tell the function which called us where the buffer is
|
||||||
|
@ -107,23 +104,18 @@ procfile_read(char *buffer,
|
||||||
*/
|
*/
|
||||||
int __init init_module()
|
int __init init_module()
|
||||||
{
|
{
|
||||||
int rv = 0;
|
/*
|
||||||
/*
|
* Create our /proc file
|
||||||
* Put the task in the work_timer task queue, so it will be executed at
|
|
||||||
* next timer interrupt
|
|
||||||
*/
|
*/
|
||||||
my_workqueue = create_workqueue(MY_WORK_QUEUE_NAME);
|
|
||||||
queue_delayed_work(my_workqueue, &Task, 100);
|
|
||||||
|
|
||||||
Our_Proc_File = create_proc_entry(PROC_ENTRY_FILENAME, 0644, NULL);
|
Our_Proc_File = create_proc_entry(PROC_ENTRY_FILENAME, 0644, NULL);
|
||||||
|
|
||||||
if (Our_Proc_File == NULL) {
|
if (Our_Proc_File == NULL) {
|
||||||
rv = -ENOMEM;
|
|
||||||
remove_proc_entry(PROC_ENTRY_FILENAME, &proc_root);
|
remove_proc_entry(PROC_ENTRY_FILENAME, &proc_root);
|
||||||
printk(KERN_INFO "Error: Could not initialize /proc/%s\n",
|
printk(KERN_ALERT "Error: Could not initialize /proc/%s\n",
|
||||||
PROC_ENTRY_FILENAME);
|
PROC_ENTRY_FILENAME);
|
||||||
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Our_Proc_File->read_proc = procfile_read;
|
Our_Proc_File->read_proc = procfile_read;
|
||||||
Our_Proc_File->owner = THIS_MODULE;
|
Our_Proc_File->owner = THIS_MODULE;
|
||||||
Our_Proc_File->mode = S_IFREG | S_IRUGO;
|
Our_Proc_File->mode = S_IFREG | S_IRUGO;
|
||||||
|
@ -131,7 +123,17 @@ int __init init_module()
|
||||||
Our_Proc_File->gid = 0;
|
Our_Proc_File->gid = 0;
|
||||||
Our_Proc_File->size = 80;
|
Our_Proc_File->size = 80;
|
||||||
|
|
||||||
return rv;
|
/*
|
||||||
|
* Put the task in the work_timer task queue, so it will be executed at
|
||||||
|
* next timer interrupt
|
||||||
|
*/
|
||||||
|
my_workqueue = create_workqueue(MY_WORK_QUEUE_NAME);
|
||||||
|
queue_delayed_work(my_workqueue, &Task, 100);
|
||||||
|
|
||||||
|
|
||||||
|
printk(KERN_INFO "/proc/%s created\n", PROC_ENTRY_FILENAME);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1 +1,7 @@
|
||||||
obj-m += intrpt.o
|
obj-m += intrpt.o
|
||||||
|
|
||||||
|
all:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
|
||||||
|
|
||||||
|
clean:
|
||||||
|
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
||||||
|
|
|
@ -28,7 +28,7 @@ static struct workqueue_struct *my_workqueue;
|
||||||
*/
|
*/
|
||||||
static void got_char(void *scancode)
|
static void got_char(void *scancode)
|
||||||
{
|
{
|
||||||
printk("Scan Code %x %s.\n",
|
printk(KERN_INFO "Scan Code %x %s.\n",
|
||||||
(int)*((char *)scancode) & 0x7F,
|
(int)*((char *)scancode) & 0x7F,
|
||||||
*((char *)scancode) & 0x80 ? "Released" : "Pressed");
|
*((char *)scancode) & 0x80 ? "Released" : "Pressed");
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
</authorgroup>
|
</authorgroup>
|
||||||
|
|
||||||
<!-- year-month-day -->
|
<!-- year-month-day -->
|
||||||
<pubdate>2005-01-23 ver 2.6.1</pubdate>
|
<pubdate>2005-05-26 ver 2.6.1</pubdate>
|
||||||
|
|
||||||
|
|
||||||
<copyright>
|
<copyright>
|
||||||
|
@ -38,33 +38,29 @@
|
||||||
</copyright>
|
</copyright>
|
||||||
|
|
||||||
<legalnotice>
|
<legalnotice>
|
||||||
<para>The Linux Kernel Module Programming Guide is a free book; you may reproduce and/or
|
<para>The Linux Kernel Module Programming Guide is a free book; you may reproduce and/or modify it under the terms of the
|
||||||
modify it under the terms of the Open Software License, version 1.1. You can obtain a copy of
|
Open Software License, version 1.1. You can obtain a copy of this license at <ulink
|
||||||
this license at <ulink
|
|
||||||
url="http://opensource.org/licenses/osl.php">http://opensource.org/licenses/osl.php</ulink>.</para>
|
url="http://opensource.org/licenses/osl.php">http://opensource.org/licenses/osl.php</ulink>.</para>
|
||||||
|
|
||||||
<para>This book is distributed in the hope it will be useful, but without any warranty,
|
<para>This book is distributed in the hope it will be useful, but without any warranty, without even the implied warranty
|
||||||
without even the implied warranty of merchantability or fitness for a particular
|
of merchantability or fitness for a particular purpose.</para>
|
||||||
purpose.</para>
|
|
||||||
|
|
||||||
<para>The author encourages wide distribution of this book for personal or commercial use,
|
<para>The author encourages wide distribution of this book for personal or commercial use, provided the above copyright
|
||||||
provided the above copyright notice remains intact and the method adheres to the provisions of
|
notice remains intact and the method adheres to the provisions of the Open Software License. In summary, you may copy and
|
||||||
the Open Software License. In summary, you may copy and distribute this book free of charge
|
distribute this book free of charge or for a profit. No explicit permission is required from the author for reproduction
|
||||||
or for a profit. No explicit permission is required from the author for reproduction of this
|
of this book in any medium, physical or electronic.</para>
|
||||||
book in any medium, physical or electronic.</para>
|
|
||||||
|
|
||||||
<para>Derivative works and translations of this document must be placed under the Open
|
<para>Derivative works and translations of this document must be placed under the Open Software License, and the original
|
||||||
Software License, and the original copyright notice must remain intact. If you have
|
copyright notice must remain intact. If you have contributed new material to this book, you must make the material and
|
||||||
contributed new material to this book, you must make the material and source code available
|
source code available for your revisions. Please make revisions and updates available directly to the document
|
||||||
for your revisions. Please make revisions and updates available directly to the document
|
maintainer, Peter Jay Salzman <email>p@dirac.org</email>. This will allow for the merging of updates and provide
|
||||||
maintainer, Peter Jay Salzman <email>p@dirac.org</email>. This will allow for the merging of
|
consistent revisions to the Linux community.</para>
|
||||||
updates and provide consistent revisions to the Linux community.</para>
|
|
||||||
|
|
||||||
<para>If you publish or distribute this book commercially, donations, royalties, and/or
|
<para>If you publish or distribute this book commercially, donations, royalties, and/or printed copies are greatly
|
||||||
printed copies are greatly appreciated by the author and the <ulink
|
appreciated by the author and the <ulink url="http://www.tldp.org">Linux Documentation Project</ulink> (LDP).
|
||||||
url="http://www.tldp.org">Linux Documentation Project</ulink> (LDP). Contributing in this way
|
Contributing in this way shows your support for free software and the LDP. If you have questions or comments, please
|
||||||
shows your support for free software and the LDP. If you have questions or comments, please
|
contact the address above.</para>
|
||||||
contact the address above.</para> </legalnotice>
|
</legalnotice>
|
||||||
|
|
||||||
</bookinfo>
|
</bookinfo>
|
||||||
|
|
||||||
|
@ -91,5 +87,5 @@
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
vim: tw=100
|
vim: tw=128
|
||||||
-->
|
-->
|
||||||
|
|
Loading…
Reference in New Issue