Difference between revisions of "EBC Exercise 26 Device Drivers"
m (→Driver Methods: Made last part optional) |
m (→Part 2: A Character Device: Updating for 5.10 kernel) |
||
(29 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
[[Category:ECE497]] | [[Category:ECE497]] | ||
+ | {{YoderHead}} | ||
− | + | Derek Molloy's excellent book Exploring BeagleBone [http://exploringbeaglebone.com/] has an Extra Content section [http://exploringbeaglebone.com/kernelprogramming/] on Linux Kernel Programming. Part 1 [http://derekmolloy.ie/writing-a-linux-kernel-module-part-1-introduction/] is a nice example of a writing a minimal kernel module. | |
− | + | Here are instructions for compiling the example on the Bone. | |
− | == | + | == Cloning Source and Compiling == |
+ | You need to load the correct kernel headers on the bone before you can compile the driver. | ||
− | + | bone$ '''time sudo apt install linux-headers-`uname -r`''' | |
− | Note: | + | Note: Those are back quotes (top left on the keyboard, above the TAB key) around '''uname -r'''. |
− | + | The '''uname''' command looks up the number of the kernel that's currently running. The back quotes | |
+ | take that number and past it after '''linux-headers-''' and does an '''apt install''' on it. A couple minutes later you have all the headers loaded. | ||
− | + | Now load the examples. | |
− | + | bone$ '''git clone https://github.com/derekmolloy/exploringBB.git''' | |
− | + | Now you are ready to run the examples. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | === | + | == Part 1: Introduction - Minimal Device Driver Example == |
+ | Change to the correct directory and make. | ||
− | + | bone$ '''cd exploringBB/extras/kernel/hello''' | |
− | + | bone$ '''make''' | |
− | / | + | make -C /lib/modules/5.10.145-ti-r55/build/ M=/home/debian/exploringBB/extras/kernel/hello modules |
+ | make[1]: Entering directory '/usr/src/linux-headers-5.10.145-ti-r55' | ||
+ | CC [M] /home/Debian/exploringBB/extras/kernel/hello/hello.o | ||
+ | MODPOST /home/Debian/exploringBB/extras/kernel/hello/Module.symvers | ||
+ | CC [M] /home/Debian/exploringBB/extras/kernel/hello/hello.mod.o | ||
+ | LD [M] /home/Debian/exploringBB/extras/kernel/hello/hello.ko | ||
+ | make[1]: Leaving directory '/usr/src/linux-headers-5.10.145-ti-r55' | ||
+ | |||
+ | real 0m11.890s | ||
+ | user 0m7.320s | ||
+ | sys 0m3.373s | ||
+ | bone$ '''ls''' | ||
+ | hello.c hello.mod hello.mod.o Makefile Module.symvers | ||
+ | hello.ko hello.mod.c hello.o modules.order | ||
− | + | Your newly compiled kernel module is in '''hello.ko'''. | |
− | + | === Inserting your module === | |
− | |||
− | + | See if your module is there | |
+ | bone$ '''modinfo hello.ko''' | ||
+ | filename: /home/debian/exploringBB/extras/kernel/hello/hello.ko | ||
+ | version: 0.1 | ||
+ | description: A simple Linux driver for the BBB. | ||
+ | author: Derek Molloy | ||
+ | license: GPL | ||
+ | srcversion: 0DD9FE0DE42157F9221E608 | ||
+ | depends: | ||
+ | name: hello | ||
+ | vermagic: 5.10.145-ti-r55 SMP preempt mod_unload modversions ARMv7 p2v8 | ||
+ | parm: name: The name to display in /var/log/kern.log (charp) | ||
− | + | That looks good, now '''insmod''' the module and check the log file. | |
− | + | bone$ '''sudo insmod hello.ko''' | |
− | + | bone$ dmesg -H | tail -1 | |
+ | [ +2.857480] EBB: Hello world from the BBB LKM! | ||
You should see your Init message. And then... | You should see your Init message. And then... | ||
− | + | bone$ '''sudo rmmod hello''' | |
− | + | bone$ '''dmesg -H | tail -2''' | |
− | + | [ +4.182591] EBB: Hello world from the BBB LKM! | |
+ | [ +3.542350] EBB: Goodbye world from the BBB LKM! | ||
should show your Exit message. | should show your Exit message. | ||
− | == | + | === Passing Parameters === |
+ | You can pass parameters when inserting a module. | ||
− | + | bone$ '''sudo insmod hello.ko name="Prof.Yoder"''' | |
+ | bone$ '''dmesg -H | tail -1''' | ||
+ | [Sep24 16:53] EBB: Hello Prof.Yoder from the BBB LKM! | ||
+ | bone$ '''sudo rmmod hello''' | ||
+ | bone$ '''dmesg -H | tail -2''' | ||
+ | [Sep24 16:53] EBB: Hello Prof.Yoder from the BBB LKM! | ||
+ | [Sep24 16:54] EBB: Goodbye Prof.Yoder from the BBB LKM! | ||
− | + | == Part 2: A Character Device == | |
− | + | Part 2 of Molloy's example [http://derekmolloy.ie/writing-a-linux-kernel-module-part-2-a-character-device/] is a character device. The code needs a slight change before it will run. | |
− | + | bone$ '''cd exploringBB/extras/kernel/ebbchar''' | |
− | + | Edit '''ebbchar.c''' and make the following changes. | |
− | + | bone$ '''git diff ebbchar.c''' | |
+ | diff --git a/extras/kernel/ebbchar/ebbchar.c b/extras/kernel/ebbchar/ebbchar.c | ||
+ | index 771b859..f869859 100644 | ||
+ | --- a/extras/kernel/ebbchar/ebbchar.c | ||
+ | +++ b/extras/kernel/ebbchar/ebbchar.c | ||
+ | @@ -142,7 +142,9 @@ static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *of | ||
+ | * @param offset The offset if required | ||
+ | */ | ||
+ | static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){ | ||
+ | - sprintf(message, "%s(%zu letters)", buffer, len); // appending received string with its length | ||
+ | + unsigned long ret; | ||
+ | + // sprintf(message, "%s(%zu letters)", buffer, len); // appending received string with its length | ||
+ | + ret = copy_from_user(message, buffer, len); | ||
+ | + size_of_message = len; // store the length of the stored message | ||
+ | printk(KERN_INFO "EBBChar: Received %zu characters from the user\n", len); | ||
+ | return len; | ||
− | + | Add the lines starting with '''+''' and comment out the '''sprintf''' starting with '''-'''. | |
− | |||
− | + | Now make and insert. | |
− | + | bone$ '''make''' | |
− | + | make -C /lib/modules/5.10.145-ti-r55/build/ M=/home/debian/exploringBB/extras/kernel/ebbchar modules | |
− | + | make[1]: Entering directory '/usr/src/linux-headers-5.10.145-ti-r55' | |
− | + | CC [M] /home/Debian/exploringBB/extras/kernel/ebbchar/ebbchar.o | |
− | + | MODPOST /home/Debian/exploringBB/extras/kernel/ebbchar/Module.symvers | |
+ | CC [M] /home/Debian/exploringBB/extras/kernel/ebbchar/ebbchar.mod.o | ||
+ | LD [M] /home/Debian/exploringBB/extras/kernel/ebbchar/ebbchar.ko | ||
+ | make[1]: Leaving directory '/usr/src/linux-headers-5.10.145-ti-r55' | ||
+ | cc testebbchar.c -o test | ||
+ | |||
+ | bone$ '''sudo insmod ebbchar.ko''' | ||
+ | bone$ '''dmesg -H | tail -4 | ||
+ | [Sep24 17:08] EBBChar: Initializing the EBBChar LKM | ||
+ | [ +0.011910] EBBChar: registered correctly with major number 241 | ||
+ | [ +0.016113] EBBChar: device class registered correctly | ||
+ | [ +0.010024] EBBChar: device class created correctly | ||
− | + | Now test it. | |
+ | bone$ '''sudo ./test''' | ||
+ | Starting device test code example... | ||
+ | Type in a short string to send to the kernel module: | ||
+ | '''This is a test!''' | ||
+ | Writing message to the device [This is a test!]. | ||
+ | Press ENTER to read back from the device... | ||
+ | |||
+ | Reading from the device... | ||
+ | The received message is: [This is a test!] | ||
+ | End of the program | ||
+ | bone$ '''dmesg -H | tail -8''' | ||
+ | [Sep24 17:08] EBBChar: Initializing the EBBChar LKM | ||
+ | [ +0.011910] EBBChar: registered correctly with major number 241 | ||
+ | [ +0.016113] EBBChar: device class registered correctly | ||
+ | [ +0.010024] EBBChar: device class created correctly | ||
+ | [Sep24 17:09] EBBChar: Device has been opened 1 time(s) | ||
+ | [ +9.333771] EBBChar: Received 15 characters from the user | ||
+ | [ +1.185798] EBBChar: Sent 15 characters to the user | ||
+ | [ +0.009778] EBBChar: Device successfully closed | ||
− | + | Look over '''ebbchar.c''' and '''testebbchar.c''' to see how the user space and the kernel interact. | |
− | |||
− | |||
− | + | == 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}} | |
− | |||
− |
Latest revision as of 09:48, 26 December 2022
Embedded Linux Class by Mark A. Yoder
Derek Molloy's excellent book Exploring BeagleBone [1] has an Extra Content section [2] on Linux Kernel Programming. Part 1 [3] is a nice example of a writing a minimal kernel module.
Here are instructions for compiling the example on the Bone.
Contents
Cloning Source and Compiling
You need to load the correct kernel headers on the bone before you can compile the driver.
bone$ time sudo apt install linux-headers-`uname -r`
Note: Those are back quotes (top left on the keyboard, above the TAB key) around uname -r.
The uname command looks up the number of the kernel that's currently running. The back quotes take that number and past it after linux-headers- and does an apt install on it. A couple minutes later you have all the headers loaded.
Now load the examples.
bone$ git clone https://github.com/derekmolloy/exploringBB.git
Now you are ready to run the examples.
Part 1: Introduction - Minimal Device Driver Example
Change to the correct directory and make.
bone$ cd exploringBB/extras/kernel/hello bone$ make make -C /lib/modules/5.10.145-ti-r55/build/ M=/home/debian/exploringBB/extras/kernel/hello modules make[1]: Entering directory '/usr/src/linux-headers-5.10.145-ti-r55' CC [M] /home/Debian/exploringBB/extras/kernel/hello/hello.o MODPOST /home/Debian/exploringBB/extras/kernel/hello/Module.symvers CC [M] /home/Debian/exploringBB/extras/kernel/hello/hello.mod.o LD [M] /home/Debian/exploringBB/extras/kernel/hello/hello.ko make[1]: Leaving directory '/usr/src/linux-headers-5.10.145-ti-r55' real 0m11.890s user 0m7.320s sys 0m3.373s bone$ ls hello.c hello.mod hello.mod.o Makefile Module.symvers hello.ko hello.mod.c hello.o modules.order
Your newly compiled kernel module is in hello.ko.
Inserting your module
See if your module is there
bone$ modinfo hello.ko filename: /home/debian/exploringBB/extras/kernel/hello/hello.ko version: 0.1 description: A simple Linux driver for the BBB. author: Derek Molloy license: GPL srcversion: 0DD9FE0DE42157F9221E608 depends: name: hello vermagic: 5.10.145-ti-r55 SMP preempt mod_unload modversions ARMv7 p2v8 parm: name: The name to display in /var/log/kern.log (charp)
That looks good, now insmod the module and check the log file.
bone$ sudo insmod hello.ko bone$ dmesg -H | tail -1
[ +2.857480] EBB: Hello world from the BBB LKM!
You should see your Init message. And then...
bone$ sudo rmmod hello bone$ dmesg -H | tail -2 [ +4.182591] EBB: Hello world from the BBB LKM! [ +3.542350] EBB: Goodbye world from the BBB LKM!
should show your Exit message.
Passing Parameters
You can pass parameters when inserting a module.
bone$ sudo insmod hello.ko name="Prof.Yoder" bone$ dmesg -H | tail -1 [Sep24 16:53] EBB: Hello Prof.Yoder from the BBB LKM! bone$ sudo rmmod hello bone$ dmesg -H | tail -2 [Sep24 16:53] EBB: Hello Prof.Yoder from the BBB LKM! [Sep24 16:54] EBB: Goodbye Prof.Yoder from the BBB LKM!
Part 2: A Character Device
Part 2 of Molloy's example [4] is a character device. The code needs a slight change before it will run.
bone$ cd exploringBB/extras/kernel/ebbchar
Edit ebbchar.c and make the following changes.
bone$ git diff ebbchar.c diff --git a/extras/kernel/ebbchar/ebbchar.c b/extras/kernel/ebbchar/ebbchar.c index 771b859..f869859 100644 --- a/extras/kernel/ebbchar/ebbchar.c +++ b/extras/kernel/ebbchar/ebbchar.c @@ -142,7 +142,9 @@ static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *of * @param offset The offset if required */ static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){ - sprintf(message, "%s(%zu letters)", buffer, len); // appending received string with its length + unsigned long ret; + // sprintf(message, "%s(%zu letters)", buffer, len); // appending received string with its length + ret = copy_from_user(message, buffer, len); + size_of_message = len; // store the length of the stored message printk(KERN_INFO "EBBChar: Received %zu characters from the user\n", len); return len;
Add the lines starting with + and comment out the sprintf starting with -.
Now make and insert.
bone$ make make -C /lib/modules/5.10.145-ti-r55/build/ M=/home/debian/exploringBB/extras/kernel/ebbchar modules make[1]: Entering directory '/usr/src/linux-headers-5.10.145-ti-r55' CC [M] /home/Debian/exploringBB/extras/kernel/ebbchar/ebbchar.o MODPOST /home/Debian/exploringBB/extras/kernel/ebbchar/Module.symvers CC [M] /home/Debian/exploringBB/extras/kernel/ebbchar/ebbchar.mod.o LD [M] /home/Debian/exploringBB/extras/kernel/ebbchar/ebbchar.ko make[1]: Leaving directory '/usr/src/linux-headers-5.10.145-ti-r55' cc testebbchar.c -o test bone$ sudo insmod ebbchar.ko bone$ dmesg -H | tail -4 [Sep24 17:08] EBBChar: Initializing the EBBChar LKM [ +0.011910] EBBChar: registered correctly with major number 241 [ +0.016113] EBBChar: device class registered correctly [ +0.010024] EBBChar: device class created correctly
Now test it.
bone$ sudo ./test Starting device test code example... Type in a short string to send to the kernel module: This is a test! Writing message to the device [This is a test!]. Press ENTER to read back from the device... Reading from the device... The received message is: [This is a test!] End of the program bone$ dmesg -H | tail -8 [Sep24 17:08] EBBChar: Initializing the EBBChar LKM [ +0.011910] EBBChar: registered correctly with major number 241 [ +0.016113] EBBChar: device class registered correctly [ +0.010024] EBBChar: device class created correctly [Sep24 17:09] EBBChar: Device has been opened 1 time(s) [ +9.333771] EBBChar: Received 15 characters from the user [ +1.185798] EBBChar: Sent 15 characters to the user [ +0.009778] EBBChar: Device successfully closed
Look over ebbchar.c and testebbchar.c to see how the user space and the kernel interact.
Reference
How to Write and Submit a Linux Kernel Patch
Embedded Linux Class by Mark A. Yoder