Difference between revisions of "EBC Exercise 26 Device Drivers"

From eLinux.org
Jump to: navigation, search
m (Compiling)
m (Driver Methods: Added more detailed instructions)
(20 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
[[Category:ECE497]]
 
[[Category:ECE497]]
 +
{{YoderHead}}
  
 
Chapter 8 of the text [http://www.amazon.com/Embedded-Linux-Primer-Practical-Real-World/dp/0137017839] gives a nice example of a minimal device driver.  The purpose of this lab is to implement that driver.
 
Chapter 8 of the text [http://www.amazon.com/Embedded-Linux-Primer-Practical-Real-World/dp/0137017839] gives a nice example of a minimal device driver.  The purpose of this lab is to implement that driver.
Line 6: Line 7:
  
 
=== Compiling ===
 
=== Compiling ===
 +
Since you are going to make several changes, it's a good idea to use git to help keep track of them. First check the current status
 +
host$ '''git status'''
 +
# On branch v3.8.13-bone28
 +
nothing to commit (working directory clean)
 +
Good, so far no changes have been made. Now create a new branch.
 +
host$ '''git checkout -b hello1'''
 +
host$ '''git status'''
 +
# On branch hello1
 +
nothing to commit (working directory clean)
  
Follow the 5 steps given in Section 8.1.4 on page 205. You can get a copy of Listing 8-1 here ([[ECE497_Listings_for_Embedded_Linux_Primer_Chapter_8]]).  Once finished you will have a file called <code>hello1.c</code> in <code>.../drivers/char/examples</code> and have the kernel configure file and Makefile updated for the new driver.  See Section 4.4 on page 89 for help with modifying the config files.
+
This creates a new branch called '''hello1''' and checks it out.
 +
 
 +
Follow the 5 steps given in Section 8.1.4 on page 205. Hint: details are given in the pages that follow 205. '''exercises/modules''' has a copy of hello1.c.  Once finished you will have a file called <code>hello1.c</code> in <code>.../drivers/char/examples</code> and have the kernel configure file and Makefile updated for the new driver.  See Section 4.4 on page 89 for help with modifying the config files.
  
 
Note:  There is a typo in Listing 8-2.
 
Note:  There is a typo in Listing 8-2.
 +
 +
Assuming you have done the steps from the book, check git's status
 +
host$ '''cd linux-dev/KERNEL/drivers/char/'''
 +
host$ '''git status'''
 +
# On branch hello1
 +
# Changes not staged for commit:
 +
#  (use "git add <file>..." to update what will be committed)
 +
#  (use "git checkout -- <file>..." to discard changes in working directory)
 +
#
 +
# modified:  Kconfig
 +
# modified:  Makefile
 +
#
 +
# Untracked files:
 +
#  (use "git add <file>..." to include in what will be committed)
 +
#
 +
# examples/
 +
no changes added to commit (use "git add" and/or "git commit -a")
 +
Now add the files you just edited/created.
 +
host$ '''git add Kconfig Makefile examples/Makefile examples/hello1.c
 +
host$ '''git status'''
 +
# On branch hello1
 +
# Changes to be committed:
 +
#  (use "git reset HEAD <file>..." to unstage)
 +
#
 +
# modified:  Kconfig
 +
# modified:  Makefile
 +
# new file:  examples/Makefile
 +
# new file:  examples/hello1.c
 +
#
 +
host$ '''git commit -m "Files for hello1 kernel module"'''
 +
[hello1 99346d5] Files for hello1 kernel module
 +
  4 files changed, 33 insertions(+)
 +
  create mode 100644 drivers/char/examples/Makefile
 +
  create mode 100644 drivers/char/examples/hello1.c
  
 
If you have created the '''crossCompileEnv.sh''' file and sourced it, all you have to do to make the modules is cd to the top of the kernel directory and then:
 
If you have created the '''crossCompileEnv.sh''' file and sourced it, all you have to do to make the modules is cd to the top of the kernel directory and then:
  
  host$ '''make modules'''
+
host$ '''cd ''Your path''/linix-dev/KERNEL'''
 +
host$ '''source ~/crossCompileEnv.sh'''  (Only need to run once per terminal session.)
 +
  host$ '''make modules''' (or make -j''X'' modules, where ''X'' = number of cores on host.)
 +
 
 +
Mine took a while the first time as it compiles all the modules. The second it only took 34 seconds.
 +
 
 +
=== Moving to Beagle ===
 +
Here are two choices for moving your module to the Beagle.  The first is the complete way, the second is the fast way.
 +
 
 +
==== Option 1, copy everything ====
 +
If you want to install all the modules that are created
 +
 
 +
host$ '''make INSTALL_MOD_PATH=~/BeagleBoard modules_install'''
 +
 
 +
This will create '''lib''' directory in '''~/BeagleBoard''' with everything that goes in '''/lib''' on the Beagle. Then
 +
 
 +
host$ '''rsync --progress -avhe ssh ~/BeagleBoard/lib root@beagle:/'''
 +
 
 +
Could take a while to transfer.
  
Mine took a while the first time as it compiles all the modules. The second it only took 31 seconds.
+
==== Option 2, copy just what you need ====
 +
Here's a faster way, just copy the new file you created
 +
host$ '''scp …/drivers/char/examples/hello1.ko root@bone:.'''
 +
On the Beagle
 +
beagle$ '''cd /lib/modules/3.8.13-bone28/kernel/drivers/char/'''
 +
beagle$ '''mkdir examples'''
 +
beagle$ '''cd examples'''
 +
beagle$ '''mv ~/hello1.ko  .'''
 +
Now build a new dependencies file
 +
beagle$ '''depmod -a'''
  
====Improvements to the Code in the Book====
+
I suggest putting the <code>scp</code> command in the <code>Makefile</code> since you may use it several times while developing your code.
The code in Listing 8-10 is incomplete.  If you compile it as is and load the module, it will work, but if you then remove the module and try to reinsert it it will fail with an error like this:
+
 
<pre>
+
==== Inserting your module ====
ERROR GOES HERE
+
See if your module is there
</pre>
+
beagle$ '''modinfo hello1'''
The reason for this is that the module is registered with the kernel on load with a command like this:
+
filename:      /lib/modules/3.8.13-bone28/kernel/drivers/char/hello1.ko
 +
license:        GPL
 +
description:    Hello World Example
 +
author:        Chris Hallinan
 +
srcversion:    602AF803EAAB89A5CB5DAD7
 +
depends:       
 +
intree:        Y
 +
vermagic:      3.8.13+ SMP mod_unload modversions ARMv7 p2v8
 +
That looks good, now modprobe the module and check the log file.
 +
 
 +
beagle$ '''modprobe hello1'''
 +
beagle$ dmesg | tail -4
 +
[    9.106206] snd-usb-audio 1-1:1.0: usb_probe_interface
 +
[    9.106244] snd-usb-audio 1-1:1.0: usb_probe_interface - got id
 +
[    9.813239] usbcore: registered new interface driver snd-usb-audio
 +
[  109.308551] Hello Example Init
 +
You should see your Init message.  And then...
 +
beagle$ '''rmmod hello1'''
 +
beagle$ '''dmesg | tail -4'''
 +
[    9.106244] snd-usb-audio 1-1:1.0: usb_probe_interface - got id
 +
[    9.813239] usbcore: registered new interface driver snd-usb-audio
 +
[  109.308551] Hello Example Init
 +
[  241.037368] Hello Example Exit
 +
 
 +
should show your Exit message.
 +
 
 +
=== Documenting your work with a patch file ===
 +
If you set up a branch on git as shown above, capturing all your changes in a patch file is easy.  First be sure you have checked in everything.
 +
host$ '''cd linux-dev/KERNEL/drivers/char'''
 +
host$ '''git status'''
 +
# On branch hello1
 +
nothing to commit (working directory clean)
 +
Good, everything is committed. Now just
 +
host$ '''git format-patch v3.8.13-bone28 --stdout > hello1.patch'''
 +
This creates a file, '''hello1.patch''', that captures all the changes you made in installing your hello1.c module. [http://ariejan.net/2009/10/26/how-to-create-and-apply-a-patch-with-git/ how-to-create-and-apply-a-patch-with-git] shows how someone can take your patch file and apply it to their kernel, therefore getting your hello1.c kernel module.
 +
 
 +
== Improvements to the Code in the Book==
 +
The code in Listing 8-10 is incomplete.  If you compile it as is and load the module, it will work, but if you then remove the module and try to reinsert it it may fail with an error. The reason for this is that the module is registered with the kernel on load with a command like this:
 
<pre>
 
<pre>
 
register_chrdev(MAJOR_NUMBER, NAME, FILE_OPERATIONS*);
 
register_chrdev(MAJOR_NUMBER, NAME, FILE_OPERATIONS*);
Line 31: Line 141:
 
</pre>
 
</pre>
 
This will properly unregister the module from the kernel and allow it to be inserted and removed from the kernel at will without restarting your system in between.
 
This will properly unregister the module from the kernel and allow it to be inserted and removed from the kernel at will without restarting your system in between.
 
+
<br /><br />Listing 8-10 also uses the ioctl field in the file_operations struct. Newer kernels have removed this. If the code from the listing complains about ioctl being an unknown field, use unlocked_ioctl in its place:
=== Moving to Beagle ===
 
 
 
On the beagle edit <code>/lib/modules/2.6.32/modules.dep</code> and add
 
<pre>
 
/lib/modules/2.6.32/kernel/drivers/char/examples/hello1.ko:
 
</pre>
 
Then copy the file <code>…/drivers/char/examples/hello1.ko</code> on the host computer to <code>/lib/modules/2.6.32/kernel/drivers/char/examples/</code> on your Beagle. This can be done with a single command though you may have to mkdir the char/examples directory on the Beagle first.
 
<pre>
 
$ cd …/drivers/char/examples
 
$ scp hello1.ko root@beagle:/lib/modules/2.6.32/kernel/drivers/char/examples
 
</pre>
 
I suggest putting the <code>scp</code> command in the <code>Makefile</code> since you may use it several times while developing your code.
 
 
 
Now, on the Beagle, modprobe the module and check the log file.
 
<pre>
 
# /sbin/modprobe hello1
 
# dmesg | tail -4
 
</pre>
 
You should see your Init message.  And then...
 
 
<pre>
 
<pre>
# /sbin/modprobe -r hello1
+
struct file_operations hello_fops = {
# dmesg | tail -4
+
    owner:          THIS_MODULE,
 +
    read:            hello_read,
 +
    write:          hello_write,
 +
    unlocked_ioctl:  hello_ioctl,
 +
    open:            hello_open,
 +
    release:        hello_release,
 +
};
 
</pre>
 
</pre>
should show your Exit message.
 
 
== Module Parameters ==
 
 
Section 8.1.7 on page 211 of the text shows how to pass a parameter to a module.  Modify your <code>hello1.c</code> to take a parameter as shown.
 
 
Modify it so you can pass two parameters.
 
 
== Module Utilities ==
 
 
Play with the mod utilities listed in section 8.2 on page 212.
 
  
 
== Driver Methods ==
 
== Driver Methods ==
  
Section 8.3 on page 217 gives a longer example of how to use the file interface with modules.  Implement the example.  Be sure to fix the ''unsigned int'' format error, and make sure your <code>exit</code> function unregisters the device (unlike the Listing).  When compiling the <code>use-hello</code> command be sure you are using the cross compiler for the ARM rather than the x86 compilerIf you sourced the '''crossCompilerEnv.sh''' file this should work:
+
Section 8.3 on page 217 gives a longer example of how to use the file interface with modules.  Implement the example.  Be sure to fix the ''unsigned int'' format error, and make sure your <code>exit</code> function unregisters the device (unlike the Listing).   
<pre>
+
I've created a build.sh script based on Section 8.5 of the text that makes building much faster.
$ ${CROSS_COMPILE}gcc use-hello.c -o use-hello
+
host$ '''cd exercises/modules'''
$ file use-hello
+
host$ '''./build.sh'''
</pre>
+
make: Entering directory `/home/yoder/BeagleBoard/linux-dev/KERNEL'
The <code>file</code> command will tell you if you got the right compiler. Modify your <code>Makefile</code> to make <code>use-hello</code>.
+
  CC [M]  /home/yoder/BeagleBoard/exercises/modules/hello1.o
 +
  CC [M]  /home/yoder/BeagleBoard/exercises/modules/hello2.o
 +
  CC [M]  /home/yoder/BeagleBoard/exercises/modules/hello3.o
 +
  Building modules, stage 2.
 +
  MODPOST 3 modules
 +
  CC      /home/yoder/BeagleBoard/exercises/modules/hello1.mod.o
 +
  LD [M]  /home/yoder/BeagleBoard/exercises/modules/hello1.ko
 +
  CC      /home/yoder/BeagleBoard/exercises/modules/hello2.mod.o
 +
  LD [M]  /home/yoder/BeagleBoard/exercises/modules/hello2.ko
 +
  CC      /home/yoder/BeagleBoard/exercises/modules/hello3.mod.o
 +
  LD [M]  /home/yoder/BeagleBoard/exercises/modules/hello3.ko
 +
make: Leaving directory `/home/yoder/BeagleBoard/linux-dev/KERNEL'
 +
Warning: Permanently added 'bone,192.168.7.2' (RSA) to the list of known hosts.
 +
hello1.ko                                    100% 3571    3.5KB/s  00:00   
 +
hello2.ko                                    100% 4344    4.2KB/s  00:00   
 +
hello3.ko                                    100% 6097    6.0KB/s  00:00
 +
It just compiled all three modules and scp'ed them to the bone. A quick listing shows many intermediate files were created.
 +
host$ '''ls -a'''
 +
.              .hello1.mod.o.cmd  .hello2.mod.o.cmd  .hello3.mod.o.cmd
 +
..              hello1.o          hello2.o          hello3.o
 +
build.sh        .hello1.o.cmd      .hello2.o.cmd      .hello3.o.cmd
 +
hello1.c        hello2.c          hello3.c          Makefile
 +
hello1.ko      hello2.ko          hello3.ko          modules.order
 +
.hello1.ko.cmd  .hello2.ko.cmd    .hello3.ko.cmd    Module.symvers
 +
hello1.mod.c    hello2.mod.c      hello3.mod.c      .tmp_versions
 +
  hello1.mod.o    hello2.mod.o      hello3.mod.o
 +
Clean the extra files up with
 +
host$ '''./clean.sh'''
 +
Now go to the Beagle and move the .ko file to the right place.
 +
beagle$ '''cd /lib/modules/3.8.13-bone28/kernel/drivers/char/examples'''
 +
beagle$ '''cp ~/hello3.ko .'''
 +
Rebuild the dependency file and insert the module.
 +
beagle$ '''depmod -a'''
 +
beagle$ '''modprobe hello3'''
 +
Create a node for the module and test it.
 +
beagle$ '''mknod /dev/hello3 c 234 0
 +
beagle$ '''cat /dev/hello3
 +
beagle$ '''dmesg | tail -4
 +
[  71.219651] [drm:output_poll_execute], [CONNECTOR:5:HDMI-A-1] status updated from 2 to 2
 +
[  76.099158] hello_open: successful
 +
[  76.099260] hello_read: returning zero bytes
 +
[  76.099294] hello_release: successful
 +
It working!
  
Some questions...
+
=== Some Questions ===
 
* The major device number 234 is part of a range of unassigned numbers.  What is the range?
 
* The major device number 234 is part of a range of unassigned numbers.  What is the range?
 
* What's the new line added to <code>hello_init</code> do?
 
* What's the new line added to <code>hello_init</code> do?
Line 82: Line 210:
 
* Once your device is running try <code>$ cat /proc/devices</code>.  Do you see your device?
 
* Once your device is running try <code>$ cat /proc/devices</code>.  Do you see your device?
  
Chapter 3 of ''Linux Device Drivers'' by Corbet, Rubini and Kroah-Hartman (on dfs) gives some more details on device drivers. Our text uses an older, static, method for major device number allocation.  The book, referenced above, uses the newer dynamic allocation.   
+
=== Optional Driver Work ===
 +
 
 +
Chapter 3 of ''Linux Device Drivers'' by Corbet, Rubini and Kroah-Hartman ([http://www.rose-hulman.edu/~yoder/Beagle/]) gives some more details on device drivers. Our text uses an older, static, method for major device number allocation.  The book, referenced above, uses the newer dynamic allocation.   
 
* Convert the example in our text to use the newer method.  It's only a couple of additional lines, but you will have to read the book to know how to do it.   
 
* Convert the example in our text to use the newer method.  It's only a couple of additional lines, but you will have to read the book to know how to do it.   
 
* Modify the ''scull_load'' script (call it ''hello_load'') on page 47, of chapter 3, to load your module.  Hint:  the back quotes are missing in this line in the text:
 
* Modify the ''scull_load'' script (call it ''hello_load'') on page 47, of chapter 3, to load your module.  Hint:  the back quotes are missing in this line in the text:
<pre>
+
 
major=`awk "\\$2==\"$module\" {print \\$1}" /proc/devices)`
+
major=`awk "\\$2==\"$module\" {print \\$1}" /proc/devices)`
</pre>
+
 
* Test it with <code>use-hello.c</code> from page 222 of ''Embedded Linux Primer''.
 
* Test it with <code>use-hello.c</code> from page 222 of ''Embedded Linux Primer''.
 
* Write a ''hello_unload'' script that will rmmod the driver and remove the nodes in /dev
 
* Write a ''hello_unload'' script that will rmmod the driver and remove the nodes in /dev
Line 95: Line 225:
 
* How can your driver find what the minor device number is?
 
* How can your driver find what the minor device number is?
 
* Modify the driver to return some characters when <code>/dev/hello1</code> is read.
 
* Modify the driver to return some characters when <code>/dev/hello1</code> is read.
 +
 +
== Reference ==
 +
 +
[http://www.cnx-software.com/2011/08/19/how-to-write-and-submit-a-linux-kernel-patch/ How to Write and Submit a Linux Kernel Patch]
 +
 +
{{YoderFoot}}

Revision as of 08:21, 24 October 2013

thumb‎ Embedded Linux Class by Mark A. Yoder


Chapter 8 of the text [1] gives a nice example of a minimal device driver. The purpose of this lab is to implement that driver.

Minimal Device Driver Example

Compiling

Since you are going to make several changes, it's a good idea to use git to help keep track of them. First check the current status

host$ git status
# On branch v3.8.13-bone28
nothing to commit (working directory clean)

Good, so far no changes have been made. Now create a new branch.

host$ git checkout -b hello1
host$ git status
# On branch hello1
nothing to commit (working directory clean)

This creates a new branch called hello1 and checks it out.

Follow the 5 steps given in Section 8.1.4 on page 205. Hint: details are given in the pages that follow 205. exercises/modules has a copy of hello1.c. Once finished you will have a file called hello1.c in .../drivers/char/examples and have the kernel configure file and Makefile updated for the new driver. See Section 4.4 on page 89 for help with modifying the config files.

Note: There is a typo in Listing 8-2.

Assuming you have done the steps from the book, check git's status

host$ cd linux-dev/KERNEL/drivers/char/
host$ git status
# On branch hello1
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   Kconfig
#	modified:   Makefile
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#	examples/
no changes added to commit (use "git add" and/or "git commit -a")

Now add the files you just edited/created.

host$ git add Kconfig Makefile examples/Makefile examples/hello1.c
host$ git status
# On branch hello1
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	modified:   Kconfig
#	modified:   Makefile
#	new file:   examples/Makefile
#	new file:   examples/hello1.c
#
host$ git commit -m "Files for hello1 kernel module"
[hello1 99346d5] Files for hello1 kernel module
 4 files changed, 33 insertions(+)
 create mode 100644 drivers/char/examples/Makefile
 create mode 100644 drivers/char/examples/hello1.c

If you have created the crossCompileEnv.sh file and sourced it, all you have to do to make the modules is cd to the top of the kernel directory and then:

host$ cd Your path/linix-dev/KERNEL
host$ source ~/crossCompileEnv.sh  (Only need to run once per terminal session.)
host$ make modules  (or make -jX modules, where X = number of cores on host.)

Mine took a while the first time as it compiles all the modules. The second it only took 34 seconds.

Moving to Beagle

Here are two choices for moving your module to the Beagle. The first is the complete way, the second is the fast way.

Option 1, copy everything

If you want to install all the modules that are created

host$ make INSTALL_MOD_PATH=~/BeagleBoard modules_install

This will create lib directory in ~/BeagleBoard with everything that goes in /lib on the Beagle. Then

host$ rsync --progress -avhe ssh ~/BeagleBoard/lib root@beagle:/

Could take a while to transfer.

Option 2, copy just what you need

Here's a faster way, just copy the new file you created

host$ scp …/drivers/char/examples/hello1.ko root@bone:.

On the Beagle

beagle$ cd /lib/modules/3.8.13-bone28/kernel/drivers/char/
beagle$ mkdir examples
beagle$ cd examples
beagle$ mv ~/hello1.ko  .

Now build a new dependencies file

beagle$ depmod -a

I suggest putting the scp command in the Makefile since you may use it several times while developing your code.

Inserting your module

See if your module is there

beagle$ modinfo hello1
filename:       /lib/modules/3.8.13-bone28/kernel/drivers/char/hello1.ko
license:        GPL
description:    Hello World Example
author:         Chris Hallinan
srcversion:     602AF803EAAB89A5CB5DAD7
depends:        
intree:         Y
vermagic:       3.8.13+ SMP mod_unload modversions ARMv7 p2v8 

That looks good, now modprobe the module and check the log file.

beagle$ modprobe hello1
beagle$ dmesg | tail -4
[    9.106206] snd-usb-audio 1-1:1.0: usb_probe_interface
[    9.106244] snd-usb-audio 1-1:1.0: usb_probe_interface - got id
[    9.813239] usbcore: registered new interface driver snd-usb-audio
[  109.308551] Hello Example Init

You should see your Init message. And then...

beagle$ rmmod hello1
beagle$ dmesg | tail -4
[    9.106244] snd-usb-audio 1-1:1.0: usb_probe_interface - got id
[    9.813239] usbcore: registered new interface driver snd-usb-audio
[  109.308551] Hello Example Init
[  241.037368] Hello Example Exit

should show your Exit message.

Documenting your work with a patch file

If you set up a branch on git as shown above, capturing all your changes in a patch file is easy. First be sure you have checked in everything.

host$ cd linux-dev/KERNEL/drivers/char
host$ git status
# On branch hello1
nothing to commit (working directory clean)

Good, everything is committed. Now just

host$ git format-patch v3.8.13-bone28 --stdout > hello1.patch

This creates a file, hello1.patch, that captures all the changes you made in installing your hello1.c module. how-to-create-and-apply-a-patch-with-git shows how someone can take your patch file and apply it to their kernel, therefore getting your hello1.c kernel module.

Improvements to the Code in the Book

The code in Listing 8-10 is incomplete. If you compile it as is and load the module, it will work, but if you then remove the module and try to reinsert it it may fail with an error. The reason for this is that the module is registered with the kernel on load with a command like this:

register_chrdev(MAJOR_NUMBER, NAME, FILE_OPERATIONS*);

But it is never unregistered when the module is unloaded. To properly unload the module, add this line of code to your module's exit function

unregister_chrdev(MAJOR_NUMBER, NAME);

This will properly unregister the module from the kernel and allow it to be inserted and removed from the kernel at will without restarting your system in between.

Listing 8-10 also uses the ioctl field in the file_operations struct. Newer kernels have removed this. If the code from the listing complains about ioctl being an unknown field, use unlocked_ioctl in its place:

struct file_operations hello_fops = {
    owner:           THIS_MODULE,
    read:            hello_read,
    write:           hello_write,
    unlocked_ioctl:  hello_ioctl,
    open:            hello_open,
    release:         hello_release,
};

Driver Methods

Section 8.3 on page 217 gives a longer example of how to use the file interface with modules. Implement the example. Be sure to fix the unsigned int format error, and make sure your exit function unregisters the device (unlike the Listing). I've created a build.sh script based on Section 8.5 of the text that makes building much faster.

host$ cd exercises/modules
host$ ./build.sh
make: Entering directory `/home/yoder/BeagleBoard/linux-dev/KERNEL'
  CC [M]  /home/yoder/BeagleBoard/exercises/modules/hello1.o
  CC [M]  /home/yoder/BeagleBoard/exercises/modules/hello2.o
  CC [M]  /home/yoder/BeagleBoard/exercises/modules/hello3.o
  Building modules, stage 2.
  MODPOST 3 modules
  CC      /home/yoder/BeagleBoard/exercises/modules/hello1.mod.o
  LD [M]  /home/yoder/BeagleBoard/exercises/modules/hello1.ko
  CC      /home/yoder/BeagleBoard/exercises/modules/hello2.mod.o
  LD [M]  /home/yoder/BeagleBoard/exercises/modules/hello2.ko
  CC      /home/yoder/BeagleBoard/exercises/modules/hello3.mod.o
  LD [M]  /home/yoder/BeagleBoard/exercises/modules/hello3.ko
make: Leaving directory `/home/yoder/BeagleBoard/linux-dev/KERNEL'
Warning: Permanently added 'bone,192.168.7.2' (RSA) to the list of known hosts.
hello1.ko                                     100% 3571     3.5KB/s   00:00    
hello2.ko                                     100% 4344     4.2KB/s   00:00    
hello3.ko                                     100% 6097     6.0KB/s   00:00 

It just compiled all three modules and scp'ed them to the bone. A quick listing shows many intermediate files were created.

host$ ls -a
.               .hello1.mod.o.cmd  .hello2.mod.o.cmd  .hello3.mod.o.cmd
..              hello1.o           hello2.o           hello3.o
build.sh        .hello1.o.cmd      .hello2.o.cmd      .hello3.o.cmd
hello1.c        hello2.c           hello3.c           Makefile
hello1.ko       hello2.ko          hello3.ko          modules.order
.hello1.ko.cmd  .hello2.ko.cmd     .hello3.ko.cmd     Module.symvers
hello1.mod.c    hello2.mod.c       hello3.mod.c       .tmp_versions
hello1.mod.o    hello2.mod.o       hello3.mod.o

Clean the extra files up with

host$ ./clean.sh

Now go to the Beagle and move the .ko file to the right place.

beagle$ cd /lib/modules/3.8.13-bone28/kernel/drivers/char/examples
beagle$ cp ~/hello3.ko .

Rebuild the dependency file and insert the module.

beagle$ depmod -a
beagle$ modprobe hello3

Create a node for the module and test it.

beagle$ mknod /dev/hello3 c 234 0
beagle$ cat /dev/hello3 
beagle$ dmesg | tail -4
[   71.219651] [drm:output_poll_execute], [CONNECTOR:5:HDMI-A-1] status updated from 2 to 2
[   76.099158] hello_open: successful
[   76.099260] hello_read: returning zero bytes
[   76.099294] hello_release: successful

It working!

Some Questions

  • The major device number 234 is part of a range of unassigned numbers. What is the range?
  • What's the new line added to hello_init do?
  • What does mknod do?
  • Once your device is running try $ cat /proc/devices. Do you see your device?

Optional Driver Work

Chapter 3 of Linux Device Drivers by Corbet, Rubini and Kroah-Hartman ([2]) gives some more details on device drivers. Our text uses an older, static, method for major device number allocation. The book, referenced above, uses the newer dynamic allocation.

  • Convert the example in our text to use the newer method. It's only a couple of additional lines, but you will have to read the book to know how to do it.
  • Modify the scull_load script (call it hello_load) on page 47, of chapter 3, to load your module. Hint: the back quotes are missing in this line in the text:
major=`awk "\\$2==\"$module\" {print \\$1}" /proc/devices)`

  • Test it with use-hello.c from page 222 of Embedded Linux Primer.
  • Write a hello_unload script that will rmmod the driver and remove the nodes in /dev
  • Modify hello.c to pass the major device number in as a parameter during insmod.

Optional: Stretch time, I though these would be easy, but after reading up on them, they look rather involved.

  • How can your driver find what the minor device number is?
  • Modify the driver to return some characters when /dev/hello1 is read.

Reference

How to Write and Submit a Linux Kernel Patch




thumb‎ Embedded Linux Class by Mark A. Yoder