Difference between revisions of "EBC Exercise 26 Device Drivers"
m (→Moving to Beagle) |
m |
||
(18 intermediate revisions by 6 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 12: | Line 13: | ||
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: | ||
− | + | ||
− | $ make modules | + | 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 31 seconds. | Mine took a while the first time as it compiles all the modules. The second it only took 31 seconds. | ||
− | = | + | 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. | ||
− | + | ====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 will fail with an error like this: | ||
<pre> | <pre> | ||
− | + | ERROR GOES HERE | |
</pre> | </pre> | ||
− | + | 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*); | |
− | |||
</pre> | </pre> | ||
− | + | 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 | |
− | |||
− | |||
<pre> | <pre> | ||
− | + | unregister_chrdev(MAJOR_NUMBER, NAME); | |
− | |||
</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. | |
+ | <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: | ||
<pre> | <pre> | ||
− | + | struct file_operations hello_fops = { | |
− | + | owner: THIS_MODULE, | |
+ | read: hello_read, | ||
+ | write: hello_write, | ||
+ | unlocked_ioctl: hello_ioctl, | ||
+ | open: hello_open, | ||
+ | release: hello_release, | ||
+ | }; | ||
</pre> | </pre> | ||
+ | |||
+ | === Moving to Beagle === | ||
+ | |||
+ | On the beagle edit <code>/lib/modules/2.6.32/modules.dep</code> and add | ||
+ | |||
+ | /lib/modules/2.6.32/kernel/drivers/char/examples/hello1.ko: | ||
+ | |||
+ | 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. | ||
+ | |||
+ | host$ '''cd …/drivers/char/examples''' | ||
+ | host$ '''scp hello1.ko root@beagle:/lib/modules/2.6.32/kernel/drivers/char/examples''' | ||
+ | |||
+ | 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, add the module to '''modules.dep''' | ||
+ | |||
+ | beagle$ '''cd /lib/modules/2.6.32''' | ||
+ | beagle$ '''gedit modules.dep''' | ||
+ | |||
+ | Add the path to your new module and save. You will also have to move the '''modules.dep.bin''', otherwise the beagle will read from it rather than the modules.dep. | ||
+ | |||
+ | beagle$ '''mv modules.dep.bin modules.dep.bin.orig''' | ||
+ | |||
+ | Now, modprobe the module and check the log file. | ||
+ | |||
+ | beagle$ '''modprobe hello1''' | ||
+ | beagle$ '''dmesg | tail -4''' | ||
+ | |||
+ | You should see your Init message. And then... | ||
+ | |||
+ | beagle$ '''modprobe -r hello1''' | ||
+ | beagle$ '''dmesg | tail -4''' | ||
+ | |||
should show your Exit message. | should show your Exit message. | ||
== Module Parameters == | == Module Parameters == | ||
− | Section 8.1.7 on page 211 of the text shows how to pass a parameter to a module. Modify your hello1.c to take a parameter as shown. | + | 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 == | == Module Utilities == | ||
Line 54: | Line 101: | ||
== 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. When compiling the <code>use-hello</code> command be sure you are using the cross compiler for the ARM rather than the x86 compiler. If 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). When compiling the <code>use-hello</code> command be sure you are using the cross compiler for the ARM rather than the x86 compiler. If you sourced the '''crossCompilerEnv.sh''' file this should work: |
− | + | ||
− | $ ${CROSS_COMPILE}gcc | + | host$ '''${CROSS_COMPILE}gcc use-hello.c -o use-hello''' |
− | $ file use-hello | + | host$ '''file use-hello''' |
− | + | ||
− | The <code>file</code> command will tell you if you got the right compiler. | + | 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>. |
Some questions... | Some questions... | ||
Line 67: | Line 114: | ||
* 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 ( | + | === Optional Driver Work === |
− | * Convert the example in | + | |
− | * Modify the ''scull_load'' script (call it ''hello_load'') on page 47 to load your module. Hint: the back quotes are missing in this line in the text: | + | 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. | |
− | major=`awk "\\$2==\"$module\" {print \\$1}" /proc/devices)` | + | * 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 <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 80: | Line 129: | ||
* 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 10:44, 3 June 2013
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.
Contents
Minimal Device Driver Example
Compiling
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 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.
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$ 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 31 seconds.
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.
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 will fail with an error like this:
ERROR GOES HERE
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, };
Moving to Beagle
On the beagle edit /lib/modules/2.6.32/modules.dep
and add
/lib/modules/2.6.32/kernel/drivers/char/examples/hello1.ko:
Then copy the file …/drivers/char/examples/hello1.ko
on the host computer to /lib/modules/2.6.32/kernel/drivers/char/examples/
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.
host$ cd …/drivers/char/examples host$ scp hello1.ko root@beagle:/lib/modules/2.6.32/kernel/drivers/char/examples
I suggest putting the scp
command in the Makefile
since you may use it several times while developing your code.
Now, on the Beagle, add the module to modules.dep
beagle$ cd /lib/modules/2.6.32 beagle$ gedit modules.dep
Add the path to your new module and save. You will also have to move the modules.dep.bin, otherwise the beagle will read from it rather than the modules.dep.
beagle$ mv modules.dep.bin modules.dep.bin.orig
Now, modprobe the module and check the log file.
beagle$ modprobe hello1 beagle$ dmesg | tail -4
You should see your Init message. And then...
beagle$ modprobe -r hello1 beagle$ dmesg | tail -4
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 hello1.c
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
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). When compiling the use-hello
command be sure you are using the cross compiler for the ARM rather than the x86 compiler. If you sourced the crossCompilerEnv.sh file this should work:
host$ ${CROSS_COMPILE}gcc use-hello.c -o use-hello host$ file use-hello
The file
command will tell you if you got the right compiler. Modify your Makefile
to make use-hello
.
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
Embedded Linux Class by Mark A. Yoder