Page Contents

  • 1 Introduction
  • 2 Character Device Drivers
    • 2.1 Major and Minor Numbers
    • two.2 The File Operations Data Structure
  • 3 Source Code for this Discussion
  • 4 The Device Driver Source Code
  • 5 Building and Testing the LKM
  • half-dozen User Access to the Device using Udev Rules
  • seven The strace Command
  • 8 LKM Synchronization Bug
  • 9 Adding Mutex Locks
  • x Determination

Introduction

In this series of articles I depict how you tin can write a Linux loadable kernel module (LKM) for an embedded Linux device. This is the 2d article in the series — please read "Writing a Linux Kernel Module — Part 1: Introduction" before moving on to this article, as information technology explains how to build, load and unload loadable kernel modules (LKMs). Such description is non repeated in this article.

Character Device Drivers

A character device typically transfers data to and from a user application — they behave like pipes or serial ports, instantly reading or writing the byte data in a graphic symbol-past-character stream. They provide the framework for many typical drivers, such equally those that are required for interfacing to serial communications, video capture, and sound devices. The main alternative to a character device is a block device. Cake devices behave in a similar fashion to regular files, allowing a buffered assortment of cached data to be viewed or manipulated with operations such as reads, writes, and seeks. Both device types can be accessed through device files that are attached to the file system tree. For case, the program lawmaking that is presented in this article builds to become a device /dev/ebbchar, which appears on your Linux system every bit follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ lsmod
Module Size Used past
ebbchar 2754 0
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ls -l /dev/ebb*
crw-rw-rwT 1 root root 240, 0 Apr eleven 15:34 /dev/ebbchar

This article describes a straightforward character commuter that tin can be used to pass data betwixt a Linux user-space program and a loadable kernel module (LKM), which is running in Linux kernel space. In this example, a C user-infinite awarding sends a string to the LKM. The LKM then responds with the message that was sent forth with the number of letters that the sent message contains. Later in the article I describe why we need to solve synchronization bug that arise with this arroyo, and I provide a version of the program that uses mutex locks to provide a solution.

Before describing the source code for the driver in this article, there are a few concepts that demand to be discussed, such equally device driver major and minor numbers, and the File Operations data construction.

Major and Minor Numbers

Device drivers take an associated major and pocket-sized number. For example, /dev/ram0 and /dev/null are associated with a driver with major number 1, and /dev/tty0 and /dev/ttyS0 are associated with a driver with major number 4. The major number is used by the kernel to identify the correct device commuter when the device is accessed. The function of the pocket-size number is device dependent, and is handled internally inside the driver. You tin see the major/pocket-size number pair for each device if you perform a listing in the /dev directory. For example:
molloyd@beaglebone:/dev$ ls -l
crw-rw---T 1 root i2c 89, 0 January one 2000 i2c-0
brw-rw---T ane root disk 1, 0 Mar ane 20:46 ram0
brw-rw---T one root floppy 179, 0 Mar 1 20:46 mmcblk0
crw-rw-rw- 1 root root 1, 3 Mar 1 twenty:46 naught
crw------- i root root 4, 0 Mar 1 20:46 tty0
crw-rw---T 1 root dialout 4, 64 Mar 1 20:46 ttyS0

Character devices are identified by a 'c' in the starting time column of a listing, and cake devices are identified by a 'b'. The access permissions, owner, and group of the device is provided for each device. Regular user accounts on the BeagleBone are members of some of these groups and therefore have permissions to utilize the i2c-0 and ttyS0 devices etc. Come across:
molloyd@beaglebone:/dev$ groups
molloyd dialout cdrom floppy audio video plugdev users i2c spi

The device that is developed in this commodity appears equally a device (/dev/ebbchar) in the /dev directory.

Information technology is possible to manually create a block or grapheme device file entry and later acquaintance information technology with your device (e.g., sudo mknod /dev/exam c 92 1 ), but this approach is prone to problems. One such trouble is that y'all have to ensure that the number you choose (e.g., 92 in this example) is not already in utilise. On the BeagleBone, y'all could examine the file /usr/src/linux-headers-three.viii.13-bone70/include/uapi/linux/major.h for a list of all organization device major numbers. All the same, a device that idenfies a "unique" major number using this approach would not exist very portable, every bit the major number of the device could clash with that of another device on another Linux SBC or Linux distribution. The lawmaking that is provided in this commodity automatically identifies an appropriate major number to utilize.

The File Operations Information Structure

The file_operations data construction that is defined in /linux/fs.h holds pointers to functions (role pointers) inside a commuter that allows you to ascertain the behavior of certain file operations. For case, Listing i is a segment of the information structure from /linux/fs.h. The driver in this commodity provides an implementation for the read, write, open up, and release system phone call file operations. If y'all do not provide an implementation for ane of the entries in this data structure and so it will merely betoken to Aught, making it inaccessible. Listing 1 is somewhat intimidating, given the number of operations available. However, to build the ebbchar LKM we just need to provide an implementation for 4 of the entries. Therefore, Listing one is provided mainly equally a reference that you can use if yous need to provide additional functionality inside the driver framework.

Listing ane: The file_operations Data Structure of the /linux/fs.h (Segment)

For further data, in that location is an splendid guide on the File Operations data structure at: Kernel.org Virtual File Systems

Get Source Code

Source Code for this Discussion

Become Source Lawmaking

All of the code for this discussion is available in the GitHub repository for the book Exploring BeagleBone. The code tin be viewed publicly at: the ExploringBB GitHub Kernel Project directory, and/or you tin clone the repository on your BeagleBone (or other Linux device) by typing:

The /extras/kernel/ebbchar directory is the almost important resources for the adjacent function of this article. The automobile-generated Doxygen documentation for these lawmaking examples is available in HTML format and PDF format.

The Device Driver Source Lawmaking

The source code for the ebbchar device commuter is provided in List two. Like to the code in the first article in this serial, there is an init() function and an exit() function. However, there are additional file_operations functions that are required for the graphic symbol device:

  • dev_open(): Called each time the device is opened from user space.
  • dev_read(): Called when information is sent from the device to user space.
  • dev_write(): Chosen when data is sent from user infinite to the device.
  • dev_release(): Called when the device is airtight in user space.

Drivers have a class proper name and a device name. In Listing ii, ebb (Exploring BeagleBone) is used equally the course name, and ebbchar as the device proper noun. This results in the cosmos of a device that appears on the file system at /sys/course/ebb/ebbchar.

List 2: The ebbchar LKM (/extras/kernel/ebbchar/ebbchar.c)

In addition to the points described by the comments in Listing 2, in that location are some additional points:

  • This code has a fixed message size of 256 characters — this will exist improved in afterwards manufactures though the dynamic allotment of retentiveness.
  • This code is non multi-procedure safe — that is addressed later in this commodity.
  • The ebbchar_init() function is much longer than the last article. That is because it is now automatically allocating a major number to the device, registering the device class, and registering the device driver. Importantly, y'all will notice that if annihilation goes incorrect that the code carefully "backs out" of the successful operations. To attain this I have repeated code (which I always dislike), but the alternative is to use goto statements, which is even less palatable (albeit slightly tidier).
  • The PTR_ERR() is a function that is defined in linux/err.h that retrieves the fault number from the pointer.
  • The functions sprintf() and strlen() are available in the kernel through the inclusion of linux/kernel.h and indirectly through linux/string.h respectively. The functions in string.h are architecture dependent.

The next step is to build this code into a kernel module.

Building and Testing the LKM

A Makefile is required to build the LKM, as provided in List 3. This Makefile is very similar to the Makefile in the first commodity in the series, with the exception that it also builds a user-space C program that interacts with the LKM.

List 3: The Makefile for the LKM and the User-infinite Program (/extras/kernel/ebbchar/Makefile)

Listing 4 is a brusque program that requests a string from the user, and writes it to the /dev/ebbchar device. After a subsequent fundamental press (ENTER) it then reads the response from the device and displays it in the final window.

Listing 4: The User-space Programme for Testing the LKM (/extras/kernel/ebbchar/testebbchar.c)

In improver to the points described by the comments in Listing four, there are some additional points:

  • The %[^\n]%*c uses the scanset specifiers, which are represented by %[] to apply the ^ character to terminate reading after the start occurrence of the \n graphic symbol. In addition, the %*c ignores the trailing grapheme, ensuring that the subsequent getchar() role works as required. Essentially, the scanf() code just reads in a sentence. If scanf() was used with a regular %s telephone call then the cord would terminated at the kickoff occurrence of the space character.
  • The getchar() allows the plan to pause at that signal until the ENTER key is pressed. This is necessary to demonstrate a problem with the current lawmaking formulation.
  • The programme then reads the response from the LKM and displays it in the terminal window.

All going well, the process to build the kernel module should be straightforward, provided that you have installed the Linux headers as described in the first article. The steps are as follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ brand
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ls -l *.ko
-rw-r--r-- i molloyd molloyd 7075 April 8 19:04 ebbchar.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ls -l test
-rwxr-xr-x one molloyd molloyd 6342 April eight 19:23 test
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo insmod ebbchar.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ lsmod
Module Size Used past
ebbchar 2521 0

The device is now present in the /dev directory, with the post-obit attributes:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ cd /dev
molloyd@beaglebone:/dev$ ls -l ebb*
crw------- 1 root root 240, 0 Apr 8 xix:28 ebbchar

You tin can come across that the major number is 240 — this is automatically assigned by the code in Listing two.

We can and so use the testebbchar programme (List 4) to test that the LKM is working correctly. For the moment, the test program must be executed with root privileges — that outcome is addressed shortly.
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo insmod ebbchar.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo ./test
Starting device examination lawmaking case...
Blazon in a brusk cord to transport to the kernel module:
This is a examination of the ebbchar LKM
Writing message to the device [This is a test of the ebbchar LKM].
Press ENTER to read back from the device...
Reading from the device...
The received message is: [This is a exam of the ebbchar LKM(33 letters)]
End of the program
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo rmmod ebbchar

The printk() output tin can exist viewed by examining the kernel logs as follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo tail -f /var/log/kern.log
Apr 11 22:24:50 beaglebone kernel: [358664.365942] EBBChar: Initializing the EBBChar LKM
Apr xi 22:24:50 beaglebone kernel: [358664.365980] EBBChar: registered correctly with major number 240
April 11 22:24:50 beaglebone kernel: [358664.366061] EBBChar: device class registered correctly
Apr xi 22:24:50 beaglebone kernel: [358664.368383] EBBChar: device course created correctly
Apr 11 22:25:15 beaglebone kernel: [358689.812483] EBBChar: Device has been opened 1 time(s)
Apr eleven 22:25:31 beaglebone kernel: [358705.451551] EBBChar: Received 33 characters from the user
Apr xi 22:25:32 beaglebone kernel: [358706.403818] EBBChar: Sent 45 characters to the user
Apr 11 22:25:32 beaglebone kernel: [358706.404207] EBBChar: Device successfully closed
Apr 11 22:25:44 beaglebone kernel: [358718.497000] EBBChar: Adieu from the LKM!

You can see that 33 characters are sent to the LKM only 45 characters are returned — this is due to the addition of the 12 characters "(33 letters)" to the cord data that specifies the length of the string that was originally sent. This add-on is performed as a examination in social club to be certain that the code is sending and receiving unique data.

There are two significant problems with the current LKM. The first is that the LKM device can only be accessed with superuser permissions, and the second is that the current LKM is not multi-process safe.

User Access to the Device using Udev Rules

Throughout this commodity, the plan that interfaces to the LKM device is executed using sudo. It would be very useful to set up our LKM device and so that it can be accessed by a particular user or grouping, while still protecting the file system. To address this upshot, you lot can employ an advanced feature of Linux called udev rules that enables you lot to customize the beliefs of the udevd service. This service gives you some user-infinite control over devices on your Linux system.

For instance, to give user-level access to the ebbchar device, the commencement step is to place the sysfs entry for the device. You lot tin can accomplish this past using a simple find:
root@beaglebone:/sys# find . -name "ebbchar"
./devices/virtual/ebb/ebbchar
./grade/ebb/ebbchar
./module/ebbchar

Nosotros then demand to identify the KERNEL and SUBSYSTEM values about which to write the rule. Y'all can use the udevadm command to perform this task:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ udevadm info -a -p /sys/class/ebb/ebbchar
Udevadm info starts with the device specified past the devpath and then walks upward the concatenation of parent
devices. It prints for every device found, all possible attributes in the udev rules cardinal format. A
rule to friction match, can be composed by the attributes of the device and the attributes from one single
parent device.
looking at device '/devices/virtual/ebb/ebbchar':
KERNEL=="ebbchar"
SUBSYSTEM=="ebb"
DRIVER==""

The rules are independent in the /etc/udev/rules.d directory. A new rule tin can be added every bit a file using these values, where the file begins with a priority number. Using a name such every bit 99-ebbchar.rules creates a new rule with the lowest priority, so that it does non interfere with other device rules. The rule tin be written as in Listing 5 and placed in the /etc/udev/rules.d directory as follows:
molloyd@beaglebone:/etc/udev/rules.d$ ls
50-hidraw.rules l-spi.rules 60-omap-tty.rules 70-persistent-net.rules 99-ebbchar.rules
molloyd@beaglebone:/etc/udev/rules.d$ more than 99-ebbchar.rules
#Rules file for the ebbchar device driver
KERNEL=="ebbchar", SUBSYSTEM=="ebb", MODE="0666"

Listing 5: Udev rules file for the ebbchar device commuter (/extras/kernel/ebbchar/99-ebbchar.rules)

In one case the rules file is added to your arrangement, yous can test it using:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo insmod ebbchar.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ls -l /dev/ebbchar
crw-rw-rwT 1 root root 240, 0 Apr 9 00:44 /dev/ebbchar

You can come across that user and group at present have the permissions required to read from and write to this device. Interestingly, the sticky scrap is also set. The sticky bit usually means that write permission is not sufficient to delete files. Therefore, in the /tmp directory whatever user can create files, but no user can delete another user's files. The mucilaginous chip is represented by a capital T in the terminal grapheme place. This ordinarily appears as a lower-case t unless the executable (x) bit for others is prepare; even so, when the 10 scrap is non fix it appears as a capital T. However, it is non entirely clear why the gummy bit is beingness set by udev — information technology appears to be unusual to udev rules under Debian. At this indicate the test application can exist executed without requiring superuser permissions.

The strace Command

The strace command is a very useful debugging tool that can execute a plan in order to intercept and tape the system calls that it performs. The system call proper name, the arguments passed, and the resulting return value are all visible, which makes it a valuable tool for solving runtime issues. Importantly, you do non need the source code for the executable in order to view the output of strace. For instance, y'all can utilize strace on your user-infinite application in order to view the advice between the user-infinite program and the kernel module, which results in the following for the exam application:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo apt-go install strace
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ strace -v
usage: strace [-dffhiqrtttTvVxx] [-a cavalcade] [-east expr] … [-o file]
[-p pid] … [-s strsize] [-u username] [-E var=val] …
[command [arg …]]
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo strace ./test
execve("./test", ["./test"], [/* 15 vars */]) = 0

write(1, "Starting device test code exampl"..., 37Starting de…) = 37
open("/dev/ebbchar", O_RDWR) = 3
write(1, "Writing bulletin to the device [T"..., 60Writing message …) = 60
write(three, "Testing the EBBChar device", 26) = 26
write(one, "Reading from the device...\northward", 27Reading from the device…) = 27
read(3, "", 100) = 0
write(one, "The received bulletin is: [Testin"..., 66The received …) = 66
write(one, "Terminate of the program\due north", 19End of the program) = 19
exit_group(0) = ?

The arrangement telephone call output gives us impressive insight into the advice that takes place between the user-space programme test and the /dev/ebbchar device driver.

LKM Synchronization Issues

In that location is a serious problem with the LKM that is described in Listing 2. In the first commodity in this series I pointed out that LKMs practice not execute sequentially and that they can be interrupted. Those facts accept important consequences for the code that is written in this article, which tin be demonstrated as follows:

Step 1: At the outset terminal window shell you can execute the test application, but do not allow information technology to run to completion (past not pressing ENTER when prompted), as follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./exam
Starting device exam code example...
Blazon in a short string to transport to the kernel module:
This is the message from the commencement concluding window
Writing bulletin to the device [This is the message from the outset terminal window].
Press ENTER to read back from the device...

Footstep 2: Then at a second terminal window beat you lot tin can execute the same test awarding simultaneously as a second procedure on the Linux device. For example:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code case...
Type in a short cord to send to the kernel module:
This is the bulletin from the 2nd terminal window
Writing bulletin to the device [This is the message from the second concluding window].
Printing ENTER to read back from the device...

Stride 3: You can then return to the start concluding window shell and press ENTER to run the program to completion, which results in the following output (shaded output is the repeated output from Pace 1):
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a curt string to ship to the kernel module:
This is the message from the first final window
Writing bulletin to the device [This is the bulletin from the first concluding window].
Press ENTER to read back from the device...

Reading from the device...
The received message is: [This is the message from the second terminal window(51 letters)]
Terminate of the program
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$

You can run across that the received message is actually the bulletin that was sent past the examination application from Stride two, which is running in the second terminal window shell (non the first as might be expected). This is considering the bulletin that was sent in Stride ii overwrote the cord bulletin that was being stored by the LKM equally a result of Pace 1.

Pace 4: You can return to the second terminal shell and run it to completion by pressing ENTER, which results in (the shaded output is the repeated output from Step ii):
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device exam lawmaking example...
Type in a short string to ship to the kernel module:
This is the bulletin from the second terminal window
Writing message to the device [This is the message from the 2d terminal window].
Printing ENTER to read back from the device...

Reading from the device...
The received message is: []
End of the program
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$

No string is received. That is because the LKM is non storing any messages at that point in time. It has already delivered the stored message to the showtime concluding window test application and reset the buffer index to 0.

Adding Mutex Locks

The Linux kernel provides a full implementation of semaphores — a data type (struct semaphore) that is used for controlling access past multiple processes to a shared resource. The easiest way to use this semaphore lawmaking inside the kernel is to apply mutexes, equally at that place is a full prepare of helper functions and macros available.

A elementary style to prevent the problems described above is to prevent 2 processes from using the /dev/ebbchar device at the same fourth dimension. A mutex is a lock that can set (put downwardly) before a process begins using a shared resource. The lock can and so be released (brought up) when the procedure is finished using the shared resources. When the lock has been set, no other procedure tin admission the locked lawmaking region. Once the mutex lock has been released by the procedure that locked it, the shared region of lawmaking is in one case once again available to exist accessed by the other process, which in turn locks the resource.

In that location are simply very minor changes required to the code in club to implement mutex locks. An outline of these changes is provided in Listing 6 beneath:

Listing half-dozen: Outline of the Changes to the ebbchar.c Program Code to Introduce mutex Locks

The full source code case is bachelor in the /extras/kernel/ebbcharmutex/ directory. The final LKM is chosen ebbcharmutex.c. If you now perform the same test on the code that contains the mutex locks, y'all will detect a different behavior. For example:

Step 1: Using the start terminal window shell yous can load the module and execute the test application, which results in the following output:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ sudo insmod ebbcharmutex.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ lsmod
Module Size Used by
ebbcharmutex 2754 0
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ ls -50 /dev/ebb*
crw-rw-rwT i root root 240, 0 Apr 12 15:26 /dev/ebbchar
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ ./exam
Starting device test code example...
Blazon in a short string to send to the kernel module:
Testing the ebbchar LKM that has mutex lawmaking
Writing message to the device [Testing the ebbchar LKM that has mutex code].
Printing ENTER to read back from the device...

Pace 2: So using the second last window beat out you can attempt to execute the test application to create the 2nd process:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ ./test
Starting device test code example...
Failed to open the device...: Device or resources busy
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$

Equally expected and required, the 2d process fails to access the device.

Footstep three: Returning to the starting time final window, the plan can be allowed to run to completion by pressing ENTER:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ ./examination
Starting device test code case...
Blazon in a short cord to send to the kernel module:
Testing the ebbchar LKM that has mutex lawmaking
Writing message to the device [Testing the ebbchar LKM that has mutex code].
Press ENTER to read back from the device...

Reading from the device...
The received message is: [Testing the ebbchar LKM that has mutex code(43 letters)]
Cease of the plan

At this point the second terminal window beat tin can now execute the test program, whereupon it will acquire the mutex lock and run correctly.

Conclusion

DoxygenHTML DoxygenPDF
Click for the HTML and PDF version of the auto-generated Doxygen code documentation

At that place are several different issues described in this article. The important outcomes of this commodity are that:

  • You tin at present create your own device such as /dev/ebbchar, which you lot can write information to and read information from. This is important, as it provides a bridge between the Linux user space and the Linux kernel space. Information technology enables you to develop advanced drivers, such equally communication devices, which tin can be controlled by C lawmaking that is running in user space.
  • You tin can appreciate why it is of import to exist aware of the synchronization issues that can arise with kernel module programming.
  • You can use udev rules to alter the properties of a device equally it is loaded.

The adjacent article in this series examines how y'all can interface to GPIO devices, such every bit physical button and LED circuits, enabling y'all to write your ain custom drivers for embedded Linux applications that interface to custom hardware. You tin read that article here: Writing a Linux Kernel Module — Part 3: Interfacing to GPIOs (coming shortly!). Finally, List 7 and Listing 8 are provided for your reference — they provide a list of the standard error states that are used in this serial of articles, along with their associated error numbers and descriptions.

Listing 7: Mistake States for LKM Development (/usr/include/asm-generic/errno-base.h) Listing 8: Error States for LKM Development (/usr/include/asm-generic/errno.h)