SlideShare a Scribd company logo
1 of 23
Download to read offline
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 character-by-character stream.
They provide the framework for many typical drivers, such as those that are required for
interfacing to serial communications, video capture, and audio devices. The main alternative to a
character device is a block device. Block devices behave in a similar fashion to regular files,
allowing a buffered array 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 example, the program code that is presented in this article builds to
become a device /dev/ebbchar, which appears on your Linux system as follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ lsmod
Module Size Used by
ebbchar 2754 0
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ls -l /dev/ebb*
crw-rw-rwT 1 root root 240, 0 Apr 11 15:34 /dev/ebbchar
A straightforward character driver that can be used to pass information between a Linux user-
space program and a loadable kernel module (LKM), which is running in Linux kernel space. In
this example, a C user-space application sends a string to the LKM. The LKM then responds
with the message that was sent along with the number of letters that the sent message contains.
Later in the article I describe why we need to solve synchronization problems that arise with this
approach, 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 need
to be discussed, such as device driver major and minor numbers, and the File Operations data
structure.
Major and Minor Numbers
Device drivers have an associated major and minor 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 driver when the device is accessed. The role of the minor number is device
dependent, and is handled internally within the driver. You can see the major/minor 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 Jan 1 2000 i2c-0
brw-rw---T 1 root disk 1, 0 Mar 1 20:46 ram0
brw-rw---T 1 root floppy 179, 0 Mar 1 20:46 mmcblk0
crw-rw-rw- 1 root root 1, 3 Mar 1 20:46 null
crw------- 1 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 first column of a listing, and block 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 use the i2c-0 and ttyS0 devices etc. See:
molloyd@beaglebone:/dev$ groups
molloyd dialout cdrom floppy audio video plugdev users i2c spi
The device that is developed in this article appears as a device (/dev/ebbchar) in the /dev
directory.
It is possible to manually create a block or character device file entry and later associate it with
your device (e.g., sudo mknod /dev/test c 92 1), but this approach is prone to problems. One such
problem is that you have to ensure that the number you choose (e.g., 92 in this case) is not
already in use. On the BeagleBone, you could examine the file/usr/src/linux-headers-3.8.13-
bone70/include/uapi/linux/major.h for a list of all system device major numbers. However, a
device that idenfies a “unique” major number using this approach would not be very portable, as
the major number of the device could clash with that of another device on another Linux SBC or
Linux distribution. The code that is provided in this article automatically identifies an
appropriate major number to use.
The File Operations Data Structure
The file_operations data structure that is defined in /linux/fs.h holds pointers to functions
(function pointers) within a driver that allows you to define the behavior of certain file
operations. For example, Listing 1 is a segment of the data structure from /linux/fs.h. The driver
in this article provides an implementation for the read, write, open, and releasesystem call file
operations. If you do not provide an implementation for one of the entries in this data structure
then it will simply point to NULL, making it inaccessible. Listing 1 is somewhat intimidating,
given the number of operations available. However, to build the ebbchar LKM we only need to
provide an implementation for four of the entries. Therefore, Listing 1 is provided mainly as a
reference that you can use if you need to provide additional functionality within the driver
framework.
The Device Driver Source Code
The source code for the ebbchar device driver is provided in Listing 2. Similar to the code in the
first article in this series, there is an init() function and an exit() function. However, there are
additional file_operations functions that are required for the character device:
Drivers have a class name and a device name. In Listing 2, ebb (Exploring BeagleBone) is used
as the class name, andebbchar as the device name. This results in the creation of a device that
appears on the file system at/sys/class/ebb/ebbchar.
Building and Testing the LKM
A Makefile is required to build the LKM, as provided in Listing 3. This Makefile is very similar
to the Makefile in the first article in the series, with the exception that it also builds a user-space
C program that interacts with the LKM.
Listing 3: The Makefile for the LKM and the User-space Program
(/extras/kernel/ebbchar/Makefile)
1
2
3
4
5
6
7
obj-m+=ebbchar.o
all:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
$(CC) testebbchar.c -o test
clean:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
User Access to the Device using Udev Rules
Throughout this article, the program that interfaces to the LKM device is executed using sudo. It
would be very useful to set up our LKM device so that it can be accessed by a particular user or
group, while still protecting the file system. To address this issue, you can use an advanced
feature of Linux called udev rules that enables you to customize the behavior of the udevd
service. This service gives you some user-space control over devices on your Linux system.
For example, to give user-level access to the ebbchar device, the first step is to identify the sysfs
entry for the device. You can achieve this by using a simple find:
root@beaglebone:/sys# find . -name "ebbchar"
./devices/virtual/ebb/ebbchar
./class/ebb/ebbchar
./module/ebbchar
We then need to identify the KERNEL and SUBSYSTEM values about which to write the rule.
You can use the udevadmcommand 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 by the devpath and then walks up the chain of
parent
devices. It prints for every device found, all possible attributes in the udev rules key format. A
rule to 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 contained in the /etc/udev/rules.d directory. A new rule can be added as a file using
these values, where the file begins with a priority number. Using a name such as 99-
ebbchar.rules creates a new rule with the lowest priority, so that it does not interfere with other
device rules. The rule can 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 50-spi.rules 60-omap-tty.rules 70-persistent-net.rules 99-ebbchar.rules
molloyd@beaglebone:/etc/udev/rules.d$ more 99-ebbchar.rules
#Rules file for the ebbchar device driver
KERNEL=="ebbchar", SUBSYSTEM=="ebb", MODE="0666"
1
2
#Rules file for the ebbchar device driver
KERNEL=="ebbchar", SUBSYSTEM=="ebb", MODE="0666"
Once the rules file is added to your system, you 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 see that user and group now have the permissions required to read from and write to
this device. Interestingly, the sticky bit is also set. The sticky bit usually means that write
permission is not sufficient to delete files. Therefore, in the/tmp directory any user can create
files, but no user can delete another user’s files. The sticky bit is represented by a capital T in the
final character place. This usually appears as a lower-case t unless the executable (x) bit for
others is set; however, when the x bit is not set it appears as a capital T. However, it is not
entirely clear why the sticky bit is being set by udev — it appears to be unusual to udev rules
under Debian. At this point the test application can be executed without requiring superuser
permissions.
The strace Command
The strace command is a very useful debugging tool that can execute a program in order to
intercept and record the system calls that it performs. The system call 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 not need the source code for the executable in order to view
the output of strace. For example, you can utilize strace on your user-space application in order
to view the communication between the user-space program and the kernel module, which
results in the following for the test application:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo apt-get install strace
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ strace -v
usage: strace [-dffhiqrtttTvVxx] [-a column] [-e 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 message to the device [T"..., 60Writing message …) = 60
write(3, "Testing the EBBChar device", 26) = 26
write(1, "Reading from the device... ", 27Reading from the device…) = 27
read(3, "", 100) = 0
write(1, "The received message is: [Testin"..., 66The received …) = 66
write(1, "End of the program ", 19End of the program) = 19
exit_group(0) = ?
The system call output gives us impressive insight into the communication that takes place
between the user-space program test and the /dev/ebbchar device driver.
LKM Synchronization Problems
There is a serious problem with the LKM that is described in Listing 2. In the first article in this
series I pointed out that LKMs do not execute sequentially and that they can be interrupted.
Those facts have important consequences for the code that is written in this article, which can be
demonstrated as follows:
Step 1: At the first terminal window shell you can execute the test application, but do not allow it
to run to completion (by not pressing ENTER when prompted), as follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the first terminal window
Writing message to the device [This is the message from the first terminal window].
Press ENTER to read back from the device...
Step 2: Then at a second terminal window shell you can execute the same test application
simultaneously as a second process on the Linux device. For example:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the second terminal window
Writing message to the device [This is the message from the second terminal window].
Press ENTER to read back from the device...
Step 3: You can then return to the first terminal window shell and press ENTER to run the
program to completion, which results in the following output (shaded output is the repeated
output from Step 1):
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the first terminal window
Writing message to the device [This is the message from the first terminal 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)]
End of the program
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$
You can see that the received message is actually the message that was sent by the test
application from Step 2, which is running in the second terminal window shell (not the first as
might be expected). This is because the message that was sent in Step 2 overwrote the string
message that was being stored by the LKM as a result of Step 1.
Step 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 2):
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the second terminal window
Writing message to the device [This is the message from the second terminal window].
Press 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 not storing any messages at that point in time.
It has already delivered the stored message to the first terminal 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 by multiple processes to a shared resource. The
easiest way to use this semaphore code within the kernel is to use mutexes, as there is a full set
of helper functions and macros available.
A simple way to prevent the problems described above is to prevent two processes from using
the /dev/ebbchar device at the same time. A mutex is a lock that can set (put down) before a
process begins using a shared resource. The lock can then be released (brought up) when the
process is finished using the shared resource. When the lock has been set, no other process can
access the locked code region. Once the mutex lock has been released by the process that locked
it, the shared region of code is once again available to be accessed by the other process, which in
turn locks the resource.
1
2
3
4
5
6
7
obj-m+=ebbchar.o
all:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
$(CC) testebbchar.c -o test
clean:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
User Access to the Device using Udev Rules
Throughout this article, the program that interfaces to the LKM device is executed using sudo. It
would be very useful to set up our LKM device so that it can be accessed by a particular user or
group, while still protecting the file system. To address this issue, you can use an advanced
feature of Linux called udev rules that enables you to customize the behavior of the udevd
service. This service gives you some user-space control over devices on your Linux system.
For example, to give user-level access to the ebbchar device, the first step is to identify the
sysfs entry for the device. You can achieve this by using a simple find:
root@beaglebone:/sys# find . -name "ebbchar"
./devices/virtual/ebb/ebbchar
./class/ebb/ebbchar
./module/ebbchar
We then need to identify the KERNEL and SUBSYSTEM values about which to write the rule.
You can use the udevadmcommand 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 by the devpath and then walks up the chain of
parent
devices. It prints for every device found, all possible attributes in the udev rules key format. A
rule to 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 contained in the /etc/udev/rules.d directory. A new rule can be added as a file
using these values, where the file begins with a priority number. Using a name such as 99-
ebbchar.rules creates a new rule with the lowest priority, so that it does not interfere with other
device rules. The rule can 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 50-spi.rules 60-omap-tty.rules 70-persistent-net.rules 99-ebbchar.rules
molloyd@beaglebone:/etc/udev/rules.d$ more 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 driver (/extras/kernel/ebbchar/99-ebbchar.rules)
1
2
#Rules file for the ebbchar device driver
KERNEL=="ebbchar", SUBSYSTEM=="ebb", MODE="0666"
Once the rules file is added to your system, you 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 see that user and group now have the permissions required to read from and write to
this device. Interestingly, the sticky bit is also set. The sticky bit usually means that write
permission is not sufficient to delete files. Therefore, in the/tmp directory any user can create
files, but no user can delete another user’s files. The sticky bit is represented by a capital T in
the final character place. This usually appears as a lower-case t unless the executable (x) bit for
others is set; however, when the x bit is not set it appears as a capital T. However, it is not
entirely clear why the sticky bit is being set by udev — it appears to be unusual to udev rules
under Debian. At this point the test application can be executed without requiring superuser
permissions.
The strace Command
The strace command is a very useful debugging tool that can execute a program in order to
intercept and record the system calls that it performs. The system call 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 not need the source code for the executable in order to view
the output of strace. For example, you can utilize strace on your user-space application in order
to view the communication between the user-space program and the kernel module, which
results in the following for the test application:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo apt-get install strace
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ strace -v
usage: strace [-dffhiqrtttTvVxx] [-a column] [-e 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 message to the device [T"..., 60Writing message …) = 60
write(3, "Testing the EBBChar device", 26) = 26
write(1, "Reading from the device... ", 27Reading from the device…) = 27
read(3, "", 100) = 0
write(1, "The received message is: [Testin"..., 66The received …) = 66
write(1, "End of the program ", 19End of the program) = 19
exit_group(0) = ?
The system call output gives us impressive insight into the communication that takes place
between the user-space program test and the /dev/ebbchar device driver.
LKM Synchronization Problems
There is a serious problem with the LKM that is described in Listing 2. In the first article in this
series I pointed out that LKMs do not execute sequentially and that they can be interrupted.
Those facts have important consequences for the code that is written in this article, which can be
demonstrated as follows:
Step 1: At the first terminal window shell you can execute the test application, but do not allow
it to run to completion (by not pressing ENTER when prompted), as follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the first terminal window
Writing message to the device [This is the message from the first terminal window].
Press ENTER to read back from the device...
Step 2: Then at a second terminal window shell you can execute the same test application
simultaneously as a second process on the Linux device. For example:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the second terminal window
Writing message to the device [This is the message from the second terminal window].
Press ENTER to read back from the device...
Step 3: You can then return to the first terminal window shell and press ENTER to run the
program to completion, which results in the following output (shaded output is the repeated
output from Step 1):
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the first terminal window
Writing message to the device [This is the message from the first terminal 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)]
End of the program
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$
You can see that the received message is actually the message that was sent by the test
application from Step 2, which is running in the second terminal window shell (not the first as
might be expected). This is because the message that was sent in Step 2 overwrote the string
message that was being stored by the LKM as a result of Step 1.
Step 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 2):
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the second terminal window
Writing message to the device [This is the message from the second terminal window].
Press 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 not storing any messages at that point in
time. It has already delivered the stored message to the first terminal 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 by multiple processes to a shared resource. The
easiest way to use this semaphore code within the kernel is to use mutexes, as there is a full set
of helper functions and macros available.
A simple way to prevent the problems described above is to prevent two processes from using
the /dev/ebbchar device at the same time. A mutex is a lock that can set (put down) before a
process begins using a shared resource. The lock can then be released (brought up) when the
process is finished using the shared resource. When the lock has been set, no other process can
access the locked code region. Once the mutex lock has been released by the process that locked
it, the shared region of code is once again available to be accessed by the other process, which
in turn locks the resource.
Solution
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 character-by-character stream.
They provide the framework for many typical drivers, such as those that are required for
interfacing to serial communications, video capture, and audio devices. The main alternative to a
character device is a block device. Block devices behave in a similar fashion to regular files,
allowing a buffered array 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 example, the program code that is presented in this article builds to
become a device /dev/ebbchar, which appears on your Linux system as follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ lsmod
Module Size Used by
ebbchar 2754 0
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ls -l /dev/ebb*
crw-rw-rwT 1 root root 240, 0 Apr 11 15:34 /dev/ebbchar
A straightforward character driver that can be used to pass information between a Linux user-
space program and a loadable kernel module (LKM), which is running in Linux kernel space. In
this example, a C user-space application sends a string to the LKM. The LKM then responds
with the message that was sent along with the number of letters that the sent message contains.
Later in the article I describe why we need to solve synchronization problems that arise with this
approach, 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 need
to be discussed, such as device driver major and minor numbers, and the File Operations data
structure.
Major and Minor Numbers
Device drivers have an associated major and minor 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 driver when the device is accessed. The role of the minor number is device
dependent, and is handled internally within the driver. You can see the major/minor 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 Jan 1 2000 i2c-0
brw-rw---T 1 root disk 1, 0 Mar 1 20:46 ram0
brw-rw---T 1 root floppy 179, 0 Mar 1 20:46 mmcblk0
crw-rw-rw- 1 root root 1, 3 Mar 1 20:46 null
crw------- 1 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 first column of a listing, and block 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 use the i2c-0 and ttyS0 devices etc. See:
molloyd@beaglebone:/dev$ groups
molloyd dialout cdrom floppy audio video plugdev users i2c spi
The device that is developed in this article appears as a device (/dev/ebbchar) in the /dev
directory.
It is possible to manually create a block or character device file entry and later associate it with
your device (e.g., sudo mknod /dev/test c 92 1), but this approach is prone to problems. One such
problem is that you have to ensure that the number you choose (e.g., 92 in this case) is not
already in use. On the BeagleBone, you could examine the file/usr/src/linux-headers-3.8.13-
bone70/include/uapi/linux/major.h for a list of all system device major numbers. However, a
device that idenfies a “unique” major number using this approach would not be very portable, as
the major number of the device could clash with that of another device on another Linux SBC or
Linux distribution. The code that is provided in this article automatically identifies an
appropriate major number to use.
The File Operations Data Structure
The file_operations data structure that is defined in /linux/fs.h holds pointers to functions
(function pointers) within a driver that allows you to define the behavior of certain file
operations. For example, Listing 1 is a segment of the data structure from /linux/fs.h. The driver
in this article provides an implementation for the read, write, open, and releasesystem call file
operations. If you do not provide an implementation for one of the entries in this data structure
then it will simply point to NULL, making it inaccessible. Listing 1 is somewhat intimidating,
given the number of operations available. However, to build the ebbchar LKM we only need to
provide an implementation for four of the entries. Therefore, Listing 1 is provided mainly as a
reference that you can use if you need to provide additional functionality within the driver
framework.
The Device Driver Source Code
The source code for the ebbchar device driver is provided in Listing 2. Similar to the code in the
first article in this series, there is an init() function and an exit() function. However, there are
additional file_operations functions that are required for the character device:
Drivers have a class name and a device name. In Listing 2, ebb (Exploring BeagleBone) is used
as the class name, andebbchar as the device name. This results in the creation of a device that
appears on the file system at/sys/class/ebb/ebbchar.
Building and Testing the LKM
A Makefile is required to build the LKM, as provided in Listing 3. This Makefile is very similar
to the Makefile in the first article in the series, with the exception that it also builds a user-space
C program that interacts with the LKM.
Listing 3: The Makefile for the LKM and the User-space Program
(/extras/kernel/ebbchar/Makefile)
1
2
3
4
5
6
7
obj-m+=ebbchar.o
all:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
$(CC) testebbchar.c -o test
clean:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
User Access to the Device using Udev Rules
Throughout this article, the program that interfaces to the LKM device is executed using sudo. It
would be very useful to set up our LKM device so that it can be accessed by a particular user or
group, while still protecting the file system. To address this issue, you can use an advanced
feature of Linux called udev rules that enables you to customize the behavior of the udevd
service. This service gives you some user-space control over devices on your Linux system.
For example, to give user-level access to the ebbchar device, the first step is to identify the sysfs
entry for the device. You can achieve this by using a simple find:
root@beaglebone:/sys# find . -name "ebbchar"
./devices/virtual/ebb/ebbchar
./class/ebb/ebbchar
./module/ebbchar
We then need to identify the KERNEL and SUBSYSTEM values about which to write the rule.
You can use the udevadmcommand 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 by the devpath and then walks up the chain of
parent
devices. It prints for every device found, all possible attributes in the udev rules key format. A
rule to 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 contained in the /etc/udev/rules.d directory. A new rule can be added as a file using
these values, where the file begins with a priority number. Using a name such as 99-
ebbchar.rules creates a new rule with the lowest priority, so that it does not interfere with other
device rules. The rule can 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 50-spi.rules 60-omap-tty.rules 70-persistent-net.rules 99-ebbchar.rules
molloyd@beaglebone:/etc/udev/rules.d$ more 99-ebbchar.rules
#Rules file for the ebbchar device driver
KERNEL=="ebbchar", SUBSYSTEM=="ebb", MODE="0666"
1
2
#Rules file for the ebbchar device driver
KERNEL=="ebbchar", SUBSYSTEM=="ebb", MODE="0666"
Once the rules file is added to your system, you 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 see that user and group now have the permissions required to read from and write to
this device. Interestingly, the sticky bit is also set. The sticky bit usually means that write
permission is not sufficient to delete files. Therefore, in the/tmp directory any user can create
files, but no user can delete another user’s files. The sticky bit is represented by a capital T in the
final character place. This usually appears as a lower-case t unless the executable (x) bit for
others is set; however, when the x bit is not set it appears as a capital T. However, it is not
entirely clear why the sticky bit is being set by udev — it appears to be unusual to udev rules
under Debian. At this point the test application can be executed without requiring superuser
permissions.
The strace Command
The strace command is a very useful debugging tool that can execute a program in order to
intercept and record the system calls that it performs. The system call 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 not need the source code for the executable in order to view
the output of strace. For example, you can utilize strace on your user-space application in order
to view the communication between the user-space program and the kernel module, which
results in the following for the test application:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo apt-get install strace
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ strace -v
usage: strace [-dffhiqrtttTvVxx] [-a column] [-e 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 message to the device [T"..., 60Writing message …) = 60
write(3, "Testing the EBBChar device", 26) = 26
write(1, "Reading from the device... ", 27Reading from the device…) = 27
read(3, "", 100) = 0
write(1, "The received message is: [Testin"..., 66The received …) = 66
write(1, "End of the program ", 19End of the program) = 19
exit_group(0) = ?
The system call output gives us impressive insight into the communication that takes place
between the user-space program test and the /dev/ebbchar device driver.
LKM Synchronization Problems
There is a serious problem with the LKM that is described in Listing 2. In the first article in this
series I pointed out that LKMs do not execute sequentially and that they can be interrupted.
Those facts have important consequences for the code that is written in this article, which can be
demonstrated as follows:
Step 1: At the first terminal window shell you can execute the test application, but do not allow it
to run to completion (by not pressing ENTER when prompted), as follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the first terminal window
Writing message to the device [This is the message from the first terminal window].
Press ENTER to read back from the device...
Step 2: Then at a second terminal window shell you can execute the same test application
simultaneously as a second process on the Linux device. For example:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the second terminal window
Writing message to the device [This is the message from the second terminal window].
Press ENTER to read back from the device...
Step 3: You can then return to the first terminal window shell and press ENTER to run the
program to completion, which results in the following output (shaded output is the repeated
output from Step 1):
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the first terminal window
Writing message to the device [This is the message from the first terminal 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)]
End of the program
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$
You can see that the received message is actually the message that was sent by the test
application from Step 2, which is running in the second terminal window shell (not the first as
might be expected). This is because the message that was sent in Step 2 overwrote the string
message that was being stored by the LKM as a result of Step 1.
Step 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 2):
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the second terminal window
Writing message to the device [This is the message from the second terminal window].
Press 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 not storing any messages at that point in time.
It has already delivered the stored message to the first terminal 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 by multiple processes to a shared resource. The
easiest way to use this semaphore code within the kernel is to use mutexes, as there is a full set
of helper functions and macros available.
A simple way to prevent the problems described above is to prevent two processes from using
the /dev/ebbchar device at the same time. A mutex is a lock that can set (put down) before a
process begins using a shared resource. The lock can then be released (brought up) when the
process is finished using the shared resource. When the lock has been set, no other process can
access the locked code region. Once the mutex lock has been released by the process that locked
it, the shared region of code is once again available to be accessed by the other process, which in
turn locks the resource.
1
2
3
4
5
6
7
obj-m+=ebbchar.o
all:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
$(CC) testebbchar.c -o test
clean:
make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean
User Access to the Device using Udev Rules
Throughout this article, the program that interfaces to the LKM device is executed using sudo. It
would be very useful to set up our LKM device so that it can be accessed by a particular user or
group, while still protecting the file system. To address this issue, you can use an advanced
feature of Linux called udev rules that enables you to customize the behavior of the udevd
service. This service gives you some user-space control over devices on your Linux system.
For example, to give user-level access to the ebbchar device, the first step is to identify the
sysfs entry for the device. You can achieve this by using a simple find:
root@beaglebone:/sys# find . -name "ebbchar"
./devices/virtual/ebb/ebbchar
./class/ebb/ebbchar
./module/ebbchar
We then need to identify the KERNEL and SUBSYSTEM values about which to write the rule.
You can use the udevadmcommand 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 by the devpath and then walks up the chain of
parent
devices. It prints for every device found, all possible attributes in the udev rules key format. A
rule to 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 contained in the /etc/udev/rules.d directory. A new rule can be added as a file
using these values, where the file begins with a priority number. Using a name such as 99-
ebbchar.rules creates a new rule with the lowest priority, so that it does not interfere with other
device rules. The rule can 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 50-spi.rules 60-omap-tty.rules 70-persistent-net.rules 99-ebbchar.rules
molloyd@beaglebone:/etc/udev/rules.d$ more 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 driver (/extras/kernel/ebbchar/99-ebbchar.rules)
1
2
#Rules file for the ebbchar device driver
KERNEL=="ebbchar", SUBSYSTEM=="ebb", MODE="0666"
Once the rules file is added to your system, you 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 see that user and group now have the permissions required to read from and write to
this device. Interestingly, the sticky bit is also set. The sticky bit usually means that write
permission is not sufficient to delete files. Therefore, in the/tmp directory any user can create
files, but no user can delete another user’s files. The sticky bit is represented by a capital T in
the final character place. This usually appears as a lower-case t unless the executable (x) bit for
others is set; however, when the x bit is not set it appears as a capital T. However, it is not
entirely clear why the sticky bit is being set by udev — it appears to be unusual to udev rules
under Debian. At this point the test application can be executed without requiring superuser
permissions.
The strace Command
The strace command is a very useful debugging tool that can execute a program in order to
intercept and record the system calls that it performs. The system call 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 not need the source code for the executable in order to view
the output of strace. For example, you can utilize strace on your user-space application in order
to view the communication between the user-space program and the kernel module, which
results in the following for the test application:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo apt-get install strace
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ strace -v
usage: strace [-dffhiqrtttTvVxx] [-a column] [-e 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 message to the device [T"..., 60Writing message …) = 60
write(3, "Testing the EBBChar device", 26) = 26
write(1, "Reading from the device... ", 27Reading from the device…) = 27
read(3, "", 100) = 0
write(1, "The received message is: [Testin"..., 66The received …) = 66
write(1, "End of the program ", 19End of the program) = 19
exit_group(0) = ?
The system call output gives us impressive insight into the communication that takes place
between the user-space program test and the /dev/ebbchar device driver.
LKM Synchronization Problems
There is a serious problem with the LKM that is described in Listing 2. In the first article in this
series I pointed out that LKMs do not execute sequentially and that they can be interrupted.
Those facts have important consequences for the code that is written in this article, which can be
demonstrated as follows:
Step 1: At the first terminal window shell you can execute the test application, but do not allow
it to run to completion (by not pressing ENTER when prompted), as follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the first terminal window
Writing message to the device [This is the message from the first terminal window].
Press ENTER to read back from the device...
Step 2: Then at a second terminal window shell you can execute the same test application
simultaneously as a second process on the Linux device. For example:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the second terminal window
Writing message to the device [This is the message from the second terminal window].
Press ENTER to read back from the device...
Step 3: You can then return to the first terminal window shell and press ENTER to run the
program to completion, which results in the following output (shaded output is the repeated
output from Step 1):
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the first terminal window
Writing message to the device [This is the message from the first terminal 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)]
End of the program
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$
You can see that the received message is actually the message that was sent by the test
application from Step 2, which is running in the second terminal window shell (not the first as
might be expected). This is because the message that was sent in Step 2 overwrote the string
message that was being stored by the LKM as a result of Step 1.
Step 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 2):
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is the message from the second terminal window
Writing message to the device [This is the message from the second terminal window].
Press 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 not storing any messages at that point in
time. It has already delivered the stored message to the first terminal 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 by multiple processes to a shared resource. The
easiest way to use this semaphore code within the kernel is to use mutexes, as there is a full set
of helper functions and macros available.
A simple way to prevent the problems described above is to prevent two processes from using
the /dev/ebbchar device at the same time. A mutex is a lock that can set (put down) before a
process begins using a shared resource. The lock can then be released (brought up) when the
process is finished using the shared resource. When the lock has been set, no other process can
access the locked code region. Once the mutex lock has been released by the process that locked
it, the shared region of code is once again available to be accessed by the other process, which
in turn locks the resource.

More Related Content

Similar to A character device typically transfers data to and from a user appli.pdf

Similar to A character device typically transfers data to and from a user appli.pdf (20)

The sysfs Filesystem
The sysfs FilesystemThe sysfs Filesystem
The sysfs Filesystem
 
The sysfs Filesystem
The sysfs FilesystemThe sysfs Filesystem
The sysfs Filesystem
 
Linux
LinuxLinux
Linux
 
Linux
LinuxLinux
Linux
 
Edubooktraining
EdubooktrainingEdubooktraining
Edubooktraining
 
Edubooktraining
EdubooktrainingEdubooktraining
Edubooktraining
 
linux installation.pdf
linux installation.pdflinux installation.pdf
linux installation.pdf
 
linux installation.pdf
linux installation.pdflinux installation.pdf
linux installation.pdf
 
Ch07 system administration
Ch07 system administration Ch07 system administration
Ch07 system administration
 
Ch07 system administration
Ch07 system administration Ch07 system administration
Ch07 system administration
 
Introduction to char device driver
Introduction to char device driverIntroduction to char device driver
Introduction to char device driver
 
Introduction to char device driver
Introduction to char device driverIntroduction to char device driver
Introduction to char device driver
 
Studienarb linux kernel-dev
Studienarb linux kernel-devStudienarb linux kernel-dev
Studienarb linux kernel-dev
 
Studienarb linux kernel-dev
Studienarb linux kernel-devStudienarb linux kernel-dev
Studienarb linux kernel-dev
 
Tutorial 2
Tutorial 2Tutorial 2
Tutorial 2
 
Tutorial 2
Tutorial 2Tutorial 2
Tutorial 2
 
Android memory analysis Debug slides.pdf
Android memory analysis Debug slides.pdfAndroid memory analysis Debug slides.pdf
Android memory analysis Debug slides.pdf
 
Android memory analysis Debug slides.pdf
Android memory analysis Debug slides.pdfAndroid memory analysis Debug slides.pdf
Android memory analysis Debug slides.pdf
 
Driver Programming Report
Driver Programming ReportDriver Programming Report
Driver Programming Report
 
Driver Programming Report
Driver Programming ReportDriver Programming Report
Driver Programming Report
 

More from aptind

               CLOUD COMPUTING -----------------------------------.pdf
               CLOUD COMPUTING -----------------------------------.pdf               CLOUD COMPUTING -----------------------------------.pdf
               CLOUD COMPUTING -----------------------------------.pdf
aptind
 
ViVi is universally available on Unix systems. It has been around.pdf
ViVi is universally available on Unix systems. It has been around.pdfViVi is universally available on Unix systems. It has been around.pdf
ViVi is universally available on Unix systems. It has been around.pdf
aptind
 
Waterfall methodThe model consists of various phases based on the.pdf
Waterfall methodThe model consists of various phases based on the.pdfWaterfall methodThe model consists of various phases based on the.pdf
Waterfall methodThe model consists of various phases based on the.pdf
aptind
 
The main function of cerebellum is to control the motor movements. H.pdf
The main function of cerebellum is to control the motor movements. H.pdfThe main function of cerebellum is to control the motor movements. H.pdf
The main function of cerebellum is to control the motor movements. H.pdf
aptind
 
Starting with Main.java, where I tested everythingimport College..pdf
Starting with Main.java, where I tested everythingimport College..pdfStarting with Main.java, where I tested everythingimport College..pdf
Starting with Main.java, where I tested everythingimport College..pdf
aptind
 
solution of question no.6inputPresent stateNext stateoutput.pdf
solution of question no.6inputPresent stateNext stateoutput.pdfsolution of question no.6inputPresent stateNext stateoutput.pdf
solution of question no.6inputPresent stateNext stateoutput.pdf
aptind
 
Sexual reproduction has played the most crucial role in evolution of.pdf
Sexual reproduction has played the most crucial role in evolution of.pdfSexual reproduction has played the most crucial role in evolution of.pdf
Sexual reproduction has played the most crucial role in evolution of.pdf
aptind
 
package com.java2novice.ds.linkedlist;import java.util.NoSuchEleme.pdf
package com.java2novice.ds.linkedlist;import java.util.NoSuchEleme.pdfpackage com.java2novice.ds.linkedlist;import java.util.NoSuchEleme.pdf
package com.java2novice.ds.linkedlist;import java.util.NoSuchEleme.pdf
aptind
 
Hi please find my code.import java.util.HashMap;import java.util.pdf
Hi please find my code.import java.util.HashMap;import java.util.pdfHi please find my code.import java.util.HashMap;import java.util.pdf
Hi please find my code.import java.util.HashMap;import java.util.pdf
aptind
 
Given below is the code for the question. Since the test files (ment.pdf
Given below is the code for the question. Since the test files (ment.pdfGiven below is the code for the question. Since the test files (ment.pdf
Given below is the code for the question. Since the test files (ment.pdf
aptind
 
Cisco Systems, Inc Acquisition Integration for manufacturing at.pdf
Cisco Systems, Inc Acquisition Integration for manufacturing at.pdfCisco Systems, Inc Acquisition Integration for manufacturing at.pdf
Cisco Systems, Inc Acquisition Integration for manufacturing at.pdf
aptind
 
As we understand, when soil particles binds to each other more stron.pdf
As we understand, when soil particles binds to each other more stron.pdfAs we understand, when soil particles binds to each other more stron.pdf
As we understand, when soil particles binds to each other more stron.pdf
aptind
 
24. Accomodation - n. Ability of lens to chhange shape diminishes as.pdf
24. Accomodation - n. Ability of lens to chhange shape diminishes as.pdf24. Accomodation - n. Ability of lens to chhange shape diminishes as.pdf
24. Accomodation - n. Ability of lens to chhange shape diminishes as.pdf
aptind
 

More from aptind (20)

ssian chemist, Dmitri Mendeleev is often consider.pdf
                     ssian chemist, Dmitri Mendeleev is often consider.pdf                     ssian chemist, Dmitri Mendeleev is often consider.pdf
ssian chemist, Dmitri Mendeleev is often consider.pdf
 
moles of HCl = 0.1106 x 10 millimoles = 1.106 mil.pdf
                     moles of HCl = 0.1106 x 10 millimoles = 1.106 mil.pdf                     moles of HCl = 0.1106 x 10 millimoles = 1.106 mil.pdf
moles of HCl = 0.1106 x 10 millimoles = 1.106 mil.pdf
 
               CLOUD COMPUTING -----------------------------------.pdf
               CLOUD COMPUTING -----------------------------------.pdf               CLOUD COMPUTING -----------------------------------.pdf
               CLOUD COMPUTING -----------------------------------.pdf
 
You cannot.SolutionYou cannot..pdf
You cannot.SolutionYou cannot..pdfYou cannot.SolutionYou cannot..pdf
You cannot.SolutionYou cannot..pdf
 
ViVi is universally available on Unix systems. It has been around.pdf
ViVi is universally available on Unix systems. It has been around.pdfViVi is universally available on Unix systems. It has been around.pdf
ViVi is universally available on Unix systems. It has been around.pdf
 
Waterfall methodThe model consists of various phases based on the.pdf
Waterfall methodThe model consists of various phases based on the.pdfWaterfall methodThe model consists of various phases based on the.pdf
Waterfall methodThe model consists of various phases based on the.pdf
 
Hi, I am unable to understand the terminology in .pdf
                     Hi, I am unable to understand the terminology in .pdf                     Hi, I am unable to understand the terminology in .pdf
Hi, I am unable to understand the terminology in .pdf
 
The main function of cerebellum is to control the motor movements. H.pdf
The main function of cerebellum is to control the motor movements. H.pdfThe main function of cerebellum is to control the motor movements. H.pdf
The main function of cerebellum is to control the motor movements. H.pdf
 
Starting with Main.java, where I tested everythingimport College..pdf
Starting with Main.java, where I tested everythingimport College..pdfStarting with Main.java, where I tested everythingimport College..pdf
Starting with Main.java, where I tested everythingimport College..pdf
 
solution of question no.6inputPresent stateNext stateoutput.pdf
solution of question no.6inputPresent stateNext stateoutput.pdfsolution of question no.6inputPresent stateNext stateoutput.pdf
solution of question no.6inputPresent stateNext stateoutput.pdf
 
Sexual reproduction has played the most crucial role in evolution of.pdf
Sexual reproduction has played the most crucial role in evolution of.pdfSexual reproduction has played the most crucial role in evolution of.pdf
Sexual reproduction has played the most crucial role in evolution of.pdf
 
package com.java2novice.ds.linkedlist;import java.util.NoSuchEleme.pdf
package com.java2novice.ds.linkedlist;import java.util.NoSuchEleme.pdfpackage com.java2novice.ds.linkedlist;import java.util.NoSuchEleme.pdf
package com.java2novice.ds.linkedlist;import java.util.NoSuchEleme.pdf
 
And is option DIf variable interest rate decrease , asset value wi.pdf
And is option DIf variable interest rate decrease , asset value wi.pdfAnd is option DIf variable interest rate decrease , asset value wi.pdf
And is option DIf variable interest rate decrease , asset value wi.pdf
 
import java.util.Scanner;public class Factorial { method usi.pdf
import java.util.Scanner;public class Factorial { method usi.pdfimport java.util.Scanner;public class Factorial { method usi.pdf
import java.util.Scanner;public class Factorial { method usi.pdf
 
Hi please find my code.import java.util.HashMap;import java.util.pdf
Hi please find my code.import java.util.HashMap;import java.util.pdfHi please find my code.import java.util.HashMap;import java.util.pdf
Hi please find my code.import java.util.HashMap;import java.util.pdf
 
Given below is the code for the question. Since the test files (ment.pdf
Given below is the code for the question. Since the test files (ment.pdfGiven below is the code for the question. Since the test files (ment.pdf
Given below is the code for the question. Since the test files (ment.pdf
 
Cisco Systems, Inc Acquisition Integration for manufacturing at.pdf
Cisco Systems, Inc Acquisition Integration for manufacturing at.pdfCisco Systems, Inc Acquisition Integration for manufacturing at.pdf
Cisco Systems, Inc Acquisition Integration for manufacturing at.pdf
 
As we understand, when soil particles binds to each other more stron.pdf
As we understand, when soil particles binds to each other more stron.pdfAs we understand, when soil particles binds to each other more stron.pdf
As we understand, when soil particles binds to each other more stron.pdf
 
Amount deposited (base amount) = 2000Rate of interest = 5Amount.pdf
Amount deposited (base amount) = 2000Rate of interest = 5Amount.pdfAmount deposited (base amount) = 2000Rate of interest = 5Amount.pdf
Amount deposited (base amount) = 2000Rate of interest = 5Amount.pdf
 
24. Accomodation - n. Ability of lens to chhange shape diminishes as.pdf
24. Accomodation - n. Ability of lens to chhange shape diminishes as.pdf24. Accomodation - n. Ability of lens to chhange shape diminishes as.pdf
24. Accomodation - n. Ability of lens to chhange shape diminishes as.pdf
 

Recently uploaded

Recently uploaded (20)

Python Notes for mca i year students osmania university.docx
Python Notes for mca i year students osmania university.docxPython Notes for mca i year students osmania university.docx
Python Notes for mca i year students osmania university.docx
 
Exploring_the_Narrative_Style_of_Amitav_Ghoshs_Gun_Island.pptx
Exploring_the_Narrative_Style_of_Amitav_Ghoshs_Gun_Island.pptxExploring_the_Narrative_Style_of_Amitav_Ghoshs_Gun_Island.pptx
Exploring_the_Narrative_Style_of_Amitav_Ghoshs_Gun_Island.pptx
 
Basic Intentional Injuries Health Education
Basic Intentional Injuries Health EducationBasic Intentional Injuries Health Education
Basic Intentional Injuries Health Education
 
How to setup Pycharm environment for Odoo 17.pptx
How to setup Pycharm environment for Odoo 17.pptxHow to setup Pycharm environment for Odoo 17.pptx
How to setup Pycharm environment for Odoo 17.pptx
 
Accessible Digital Futures project (20/03/2024)
Accessible Digital Futures project (20/03/2024)Accessible Digital Futures project (20/03/2024)
Accessible Digital Futures project (20/03/2024)
 
On National Teacher Day, meet the 2024-25 Kenan Fellows
On National Teacher Day, meet the 2024-25 Kenan FellowsOn National Teacher Day, meet the 2024-25 Kenan Fellows
On National Teacher Day, meet the 2024-25 Kenan Fellows
 
dusjagr & nano talk on open tools for agriculture research and learning
dusjagr & nano talk on open tools for agriculture research and learningdusjagr & nano talk on open tools for agriculture research and learning
dusjagr & nano talk on open tools for agriculture research and learning
 
Single or Multiple melodic lines structure
Single or Multiple melodic lines structureSingle or Multiple melodic lines structure
Single or Multiple melodic lines structure
 
21st_Century_Skills_Framework_Final_Presentation_2.pptx
21st_Century_Skills_Framework_Final_Presentation_2.pptx21st_Century_Skills_Framework_Final_Presentation_2.pptx
21st_Century_Skills_Framework_Final_Presentation_2.pptx
 
Simple, Complex, and Compound Sentences Exercises.pdf
Simple, Complex, and Compound Sentences Exercises.pdfSimple, Complex, and Compound Sentences Exercises.pdf
Simple, Complex, and Compound Sentences Exercises.pdf
 
How to Add New Custom Addons Path in Odoo 17
How to Add New Custom Addons Path in Odoo 17How to Add New Custom Addons Path in Odoo 17
How to Add New Custom Addons Path in Odoo 17
 
This PowerPoint helps students to consider the concept of infinity.
This PowerPoint helps students to consider the concept of infinity.This PowerPoint helps students to consider the concept of infinity.
This PowerPoint helps students to consider the concept of infinity.
 
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptxHMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
HMCS Max Bernays Pre-Deployment Brief (May 2024).pptx
 
Food safety_Challenges food safety laboratories_.pdf
Food safety_Challenges food safety laboratories_.pdfFood safety_Challenges food safety laboratories_.pdf
Food safety_Challenges food safety laboratories_.pdf
 
Beyond_Borders_Understanding_Anime_and_Manga_Fandom_A_Comprehensive_Audience_...
Beyond_Borders_Understanding_Anime_and_Manga_Fandom_A_Comprehensive_Audience_...Beyond_Borders_Understanding_Anime_and_Manga_Fandom_A_Comprehensive_Audience_...
Beyond_Borders_Understanding_Anime_and_Manga_Fandom_A_Comprehensive_Audience_...
 
AIM of Education-Teachers Training-2024.ppt
AIM of Education-Teachers Training-2024.pptAIM of Education-Teachers Training-2024.ppt
AIM of Education-Teachers Training-2024.ppt
 
Understanding Accommodations and Modifications
Understanding  Accommodations and ModificationsUnderstanding  Accommodations and Modifications
Understanding Accommodations and Modifications
 
Mehran University Newsletter Vol-X, Issue-I, 2024
Mehran University Newsletter Vol-X, Issue-I, 2024Mehran University Newsletter Vol-X, Issue-I, 2024
Mehran University Newsletter Vol-X, Issue-I, 2024
 
How to Add a Tool Tip to a Field in Odoo 17
How to Add a Tool Tip to a Field in Odoo 17How to Add a Tool Tip to a Field in Odoo 17
How to Add a Tool Tip to a Field in Odoo 17
 
latest AZ-104 Exam Questions and Answers
latest AZ-104 Exam Questions and Answerslatest AZ-104 Exam Questions and Answers
latest AZ-104 Exam Questions and Answers
 

A character device typically transfers data to and from a user appli.pdf

  • 1. 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 character-by-character stream. They provide the framework for many typical drivers, such as those that are required for interfacing to serial communications, video capture, and audio devices. The main alternative to a character device is a block device. Block devices behave in a similar fashion to regular files, allowing a buffered array 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 example, the program code that is presented in this article builds to become a device /dev/ebbchar, which appears on your Linux system as follows: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ lsmod Module Size Used by ebbchar 2754 0 molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ls -l /dev/ebb* crw-rw-rwT 1 root root 240, 0 Apr 11 15:34 /dev/ebbchar A straightforward character driver that can be used to pass information between a Linux user- space program and a loadable kernel module (LKM), which is running in Linux kernel space. In this example, a C user-space application sends a string to the LKM. The LKM then responds with the message that was sent along with the number of letters that the sent message contains. Later in the article I describe why we need to solve synchronization problems that arise with this approach, 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 need to be discussed, such as device driver major and minor numbers, and the File Operations data structure. Major and Minor Numbers Device drivers have an associated major and minor 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 driver when the device is accessed. The role of the minor number is device dependent, and is handled internally within the driver. You can see the major/minor 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 Jan 1 2000 i2c-0 brw-rw---T 1 root disk 1, 0 Mar 1 20:46 ram0 brw-rw---T 1 root floppy 179, 0 Mar 1 20:46 mmcblk0 crw-rw-rw- 1 root root 1, 3 Mar 1 20:46 null
  • 2. crw------- 1 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 first column of a listing, and block 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 use the i2c-0 and ttyS0 devices etc. See: molloyd@beaglebone:/dev$ groups molloyd dialout cdrom floppy audio video plugdev users i2c spi The device that is developed in this article appears as a device (/dev/ebbchar) in the /dev directory. It is possible to manually create a block or character device file entry and later associate it with your device (e.g., sudo mknod /dev/test c 92 1), but this approach is prone to problems. One such problem is that you have to ensure that the number you choose (e.g., 92 in this case) is not already in use. On the BeagleBone, you could examine the file/usr/src/linux-headers-3.8.13- bone70/include/uapi/linux/major.h for a list of all system device major numbers. However, a device that idenfies a “unique” major number using this approach would not be very portable, as the major number of the device could clash with that of another device on another Linux SBC or Linux distribution. The code that is provided in this article automatically identifies an appropriate major number to use. The File Operations Data Structure The file_operations data structure that is defined in /linux/fs.h holds pointers to functions (function pointers) within a driver that allows you to define the behavior of certain file operations. For example, Listing 1 is a segment of the data structure from /linux/fs.h. The driver in this article provides an implementation for the read, write, open, and releasesystem call file operations. If you do not provide an implementation for one of the entries in this data structure then it will simply point to NULL, making it inaccessible. Listing 1 is somewhat intimidating, given the number of operations available. However, to build the ebbchar LKM we only need to provide an implementation for four of the entries. Therefore, Listing 1 is provided mainly as a reference that you can use if you need to provide additional functionality within the driver framework. The Device Driver Source Code The source code for the ebbchar device driver is provided in Listing 2. Similar to the code in the first article in this series, there is an init() function and an exit() function. However, there are additional file_operations functions that are required for the character device: Drivers have a class name and a device name. In Listing 2, ebb (Exploring BeagleBone) is used
  • 3. as the class name, andebbchar as the device name. This results in the creation of a device that appears on the file system at/sys/class/ebb/ebbchar. Building and Testing the LKM A Makefile is required to build the LKM, as provided in Listing 3. This Makefile is very similar to the Makefile in the first article in the series, with the exception that it also builds a user-space C program that interacts with the LKM. Listing 3: The Makefile for the LKM and the User-space Program (/extras/kernel/ebbchar/Makefile) 1 2 3 4 5 6 7 obj-m+=ebbchar.o all: make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules $(CC) testebbchar.c -o test clean: make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean User Access to the Device using Udev Rules Throughout this article, the program that interfaces to the LKM device is executed using sudo. It would be very useful to set up our LKM device so that it can be accessed by a particular user or group, while still protecting the file system. To address this issue, you can use an advanced feature of Linux called udev rules that enables you to customize the behavior of the udevd service. This service gives you some user-space control over devices on your Linux system. For example, to give user-level access to the ebbchar device, the first step is to identify the sysfs entry for the device. You can achieve this by using a simple find: root@beaglebone:/sys# find . -name "ebbchar" ./devices/virtual/ebb/ebbchar ./class/ebb/ebbchar ./module/ebbchar We then need to identify the KERNEL and SUBSYSTEM values about which to write the rule. You can use the udevadmcommand to perform this task: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ udevadm info -a -p
  • 4. /sys/class/ebb/ebbchar Udevadm info starts with the device specified by the devpath and then walks up the chain of parent devices. It prints for every device found, all possible attributes in the udev rules key format. A rule to 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 contained in the /etc/udev/rules.d directory. A new rule can be added as a file using these values, where the file begins with a priority number. Using a name such as 99- ebbchar.rules creates a new rule with the lowest priority, so that it does not interfere with other device rules. The rule can 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 50-spi.rules 60-omap-tty.rules 70-persistent-net.rules 99-ebbchar.rules molloyd@beaglebone:/etc/udev/rules.d$ more 99-ebbchar.rules #Rules file for the ebbchar device driver KERNEL=="ebbchar", SUBSYSTEM=="ebb", MODE="0666" 1 2 #Rules file for the ebbchar device driver KERNEL=="ebbchar", SUBSYSTEM=="ebb", MODE="0666" Once the rules file is added to your system, you 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 see that user and group now have the permissions required to read from and write to this device. Interestingly, the sticky bit is also set. The sticky bit usually means that write permission is not sufficient to delete files. Therefore, in the/tmp directory any user can create files, but no user can delete another user’s files. The sticky bit is represented by a capital T in the final character place. This usually appears as a lower-case t unless the executable (x) bit for others is set; however, when the x bit is not set it appears as a capital T. However, it is not entirely clear why the sticky bit is being set by udev — it appears to be unusual to udev rules under Debian. At this point the test application can be executed without requiring superuser
  • 5. permissions. The strace Command The strace command is a very useful debugging tool that can execute a program in order to intercept and record the system calls that it performs. The system call 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 not need the source code for the executable in order to view the output of strace. For example, you can utilize strace on your user-space application in order to view the communication between the user-space program and the kernel module, which results in the following for the test application: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo apt-get install strace molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ strace -v usage: strace [-dffhiqrtttTvVxx] [-a column] [-e 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 message to the device [T"..., 60Writing message …) = 60 write(3, "Testing the EBBChar device", 26) = 26 write(1, "Reading from the device... ", 27Reading from the device…) = 27 read(3, "", 100) = 0 write(1, "The received message is: [Testin"..., 66The received …) = 66 write(1, "End of the program ", 19End of the program) = 19 exit_group(0) = ? The system call output gives us impressive insight into the communication that takes place between the user-space program test and the /dev/ebbchar device driver. LKM Synchronization Problems There is a serious problem with the LKM that is described in Listing 2. In the first article in this series I pointed out that LKMs do not execute sequentially and that they can be interrupted. Those facts have important consequences for the code that is written in this article, which can be demonstrated as follows: Step 1: At the first terminal window shell you can execute the test application, but do not allow it to run to completion (by not pressing ENTER when prompted), as follows: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
  • 6. Starting device test code example... Type in a short string to send to the kernel module: This is the message from the first terminal window Writing message to the device [This is the message from the first terminal window]. Press ENTER to read back from the device... Step 2: Then at a second terminal window shell you can execute the same test application simultaneously as a second process on the Linux device. For example: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the second terminal window Writing message to the device [This is the message from the second terminal window]. Press ENTER to read back from the device... Step 3: You can then return to the first terminal window shell and press ENTER to run the program to completion, which results in the following output (shaded output is the repeated output from Step 1): molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the first terminal window Writing message to the device [This is the message from the first terminal 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)] End of the program molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ You can see that the received message is actually the message that was sent by the test application from Step 2, which is running in the second terminal window shell (not the first as might be expected). This is because the message that was sent in Step 2 overwrote the string message that was being stored by the LKM as a result of Step 1. Step 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 2): molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the second terminal window
  • 7. Writing message to the device [This is the message from the second terminal window]. Press 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 not storing any messages at that point in time. It has already delivered the stored message to the first terminal 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 by multiple processes to a shared resource. The easiest way to use this semaphore code within the kernel is to use mutexes, as there is a full set of helper functions and macros available. A simple way to prevent the problems described above is to prevent two processes from using the /dev/ebbchar device at the same time. A mutex is a lock that can set (put down) before a process begins using a shared resource. The lock can then be released (brought up) when the process is finished using the shared resource. When the lock has been set, no other process can access the locked code region. Once the mutex lock has been released by the process that locked it, the shared region of code is once again available to be accessed by the other process, which in turn locks the resource. 1 2 3 4 5 6 7 obj-m+=ebbchar.o all: make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules $(CC) testebbchar.c -o test clean: make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean User Access to the Device using Udev Rules
  • 8. Throughout this article, the program that interfaces to the LKM device is executed using sudo. It would be very useful to set up our LKM device so that it can be accessed by a particular user or group, while still protecting the file system. To address this issue, you can use an advanced feature of Linux called udev rules that enables you to customize the behavior of the udevd service. This service gives you some user-space control over devices on your Linux system. For example, to give user-level access to the ebbchar device, the first step is to identify the sysfs entry for the device. You can achieve this by using a simple find: root@beaglebone:/sys# find . -name "ebbchar" ./devices/virtual/ebb/ebbchar ./class/ebb/ebbchar ./module/ebbchar We then need to identify the KERNEL and SUBSYSTEM values about which to write the rule. You can use the udevadmcommand 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 by the devpath and then walks up the chain of parent devices. It prints for every device found, all possible attributes in the udev rules key format. A rule to 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 contained in the /etc/udev/rules.d directory. A new rule can be added as a file using these values, where the file begins with a priority number. Using a name such as 99- ebbchar.rules creates a new rule with the lowest priority, so that it does not interfere with other device rules. The rule can 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 50-spi.rules 60-omap-tty.rules 70-persistent-net.rules 99-ebbchar.rules molloyd@beaglebone:/etc/udev/rules.d$ more 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 driver (/extras/kernel/ebbchar/99-ebbchar.rules)
  • 9. 1 2 #Rules file for the ebbchar device driver KERNEL=="ebbchar", SUBSYSTEM=="ebb", MODE="0666" Once the rules file is added to your system, you 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 see that user and group now have the permissions required to read from and write to this device. Interestingly, the sticky bit is also set. The sticky bit usually means that write permission is not sufficient to delete files. Therefore, in the/tmp directory any user can create files, but no user can delete another user’s files. The sticky bit is represented by a capital T in the final character place. This usually appears as a lower-case t unless the executable (x) bit for others is set; however, when the x bit is not set it appears as a capital T. However, it is not entirely clear why the sticky bit is being set by udev — it appears to be unusual to udev rules under Debian. At this point the test application can be executed without requiring superuser permissions. The strace Command The strace command is a very useful debugging tool that can execute a program in order to intercept and record the system calls that it performs. The system call 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 not need the source code for the executable in order to view the output of strace. For example, you can utilize strace on your user-space application in order to view the communication between the user-space program and the kernel module, which results in the following for the test application: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo apt-get install strace molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ strace -v usage: strace [-dffhiqrtttTvVxx] [-a column] [-e 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 message to the device [T"..., 60Writing message …) = 60
  • 10. write(3, "Testing the EBBChar device", 26) = 26 write(1, "Reading from the device... ", 27Reading from the device…) = 27 read(3, "", 100) = 0 write(1, "The received message is: [Testin"..., 66The received …) = 66 write(1, "End of the program ", 19End of the program) = 19 exit_group(0) = ? The system call output gives us impressive insight into the communication that takes place between the user-space program test and the /dev/ebbchar device driver. LKM Synchronization Problems There is a serious problem with the LKM that is described in Listing 2. In the first article in this series I pointed out that LKMs do not execute sequentially and that they can be interrupted. Those facts have important consequences for the code that is written in this article, which can be demonstrated as follows: Step 1: At the first terminal window shell you can execute the test application, but do not allow it to run to completion (by not pressing ENTER when prompted), as follows: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the first terminal window Writing message to the device [This is the message from the first terminal window]. Press ENTER to read back from the device... Step 2: Then at a second terminal window shell you can execute the same test application simultaneously as a second process on the Linux device. For example: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the second terminal window Writing message to the device [This is the message from the second terminal window]. Press ENTER to read back from the device... Step 3: You can then return to the first terminal window shell and press ENTER to run the program to completion, which results in the following output (shaded output is the repeated output from Step 1): molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the first terminal window
  • 11. Writing message to the device [This is the message from the first terminal 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)] End of the program molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ You can see that the received message is actually the message that was sent by the test application from Step 2, which is running in the second terminal window shell (not the first as might be expected). This is because the message that was sent in Step 2 overwrote the string message that was being stored by the LKM as a result of Step 1. Step 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 2): molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the second terminal window Writing message to the device [This is the message from the second terminal window]. Press 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 not storing any messages at that point in time. It has already delivered the stored message to the first terminal 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 by multiple processes to a shared resource. The easiest way to use this semaphore code within the kernel is to use mutexes, as there is a full set of helper functions and macros available. A simple way to prevent the problems described above is to prevent two processes from using the /dev/ebbchar device at the same time. A mutex is a lock that can set (put down) before a process begins using a shared resource. The lock can then be released (brought up) when the process is finished using the shared resource. When the lock has been set, no other process can access the locked code region. Once the mutex lock has been released by the process that locked it, the shared region of code is once again available to be accessed by the other process, which
  • 12. in turn locks the resource. Solution 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 character-by-character stream. They provide the framework for many typical drivers, such as those that are required for interfacing to serial communications, video capture, and audio devices. The main alternative to a character device is a block device. Block devices behave in a similar fashion to regular files, allowing a buffered array 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 example, the program code that is presented in this article builds to become a device /dev/ebbchar, which appears on your Linux system as follows: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ lsmod Module Size Used by ebbchar 2754 0 molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ls -l /dev/ebb* crw-rw-rwT 1 root root 240, 0 Apr 11 15:34 /dev/ebbchar A straightforward character driver that can be used to pass information between a Linux user- space program and a loadable kernel module (LKM), which is running in Linux kernel space. In this example, a C user-space application sends a string to the LKM. The LKM then responds with the message that was sent along with the number of letters that the sent message contains. Later in the article I describe why we need to solve synchronization problems that arise with this approach, 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 need to be discussed, such as device driver major and minor numbers, and the File Operations data structure. Major and Minor Numbers Device drivers have an associated major and minor 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 driver when the device is accessed. The role of the minor number is device dependent, and is handled internally within the driver. You can see the major/minor number pair for each device if you perform a listing in the /dev directory. For example: molloyd@beaglebone:/dev$ ls -l
  • 13. crw-rw---T 1 root i2c 89, 0 Jan 1 2000 i2c-0 brw-rw---T 1 root disk 1, 0 Mar 1 20:46 ram0 brw-rw---T 1 root floppy 179, 0 Mar 1 20:46 mmcblk0 crw-rw-rw- 1 root root 1, 3 Mar 1 20:46 null crw------- 1 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 first column of a listing, and block 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 use the i2c-0 and ttyS0 devices etc. See: molloyd@beaglebone:/dev$ groups molloyd dialout cdrom floppy audio video plugdev users i2c spi The device that is developed in this article appears as a device (/dev/ebbchar) in the /dev directory. It is possible to manually create a block or character device file entry and later associate it with your device (e.g., sudo mknod /dev/test c 92 1), but this approach is prone to problems. One such problem is that you have to ensure that the number you choose (e.g., 92 in this case) is not already in use. On the BeagleBone, you could examine the file/usr/src/linux-headers-3.8.13- bone70/include/uapi/linux/major.h for a list of all system device major numbers. However, a device that idenfies a “unique” major number using this approach would not be very portable, as the major number of the device could clash with that of another device on another Linux SBC or Linux distribution. The code that is provided in this article automatically identifies an appropriate major number to use. The File Operations Data Structure The file_operations data structure that is defined in /linux/fs.h holds pointers to functions (function pointers) within a driver that allows you to define the behavior of certain file operations. For example, Listing 1 is a segment of the data structure from /linux/fs.h. The driver in this article provides an implementation for the read, write, open, and releasesystem call file operations. If you do not provide an implementation for one of the entries in this data structure then it will simply point to NULL, making it inaccessible. Listing 1 is somewhat intimidating, given the number of operations available. However, to build the ebbchar LKM we only need to provide an implementation for four of the entries. Therefore, Listing 1 is provided mainly as a reference that you can use if you need to provide additional functionality within the driver framework. The Device Driver Source Code
  • 14. The source code for the ebbchar device driver is provided in Listing 2. Similar to the code in the first article in this series, there is an init() function and an exit() function. However, there are additional file_operations functions that are required for the character device: Drivers have a class name and a device name. In Listing 2, ebb (Exploring BeagleBone) is used as the class name, andebbchar as the device name. This results in the creation of a device that appears on the file system at/sys/class/ebb/ebbchar. Building and Testing the LKM A Makefile is required to build the LKM, as provided in Listing 3. This Makefile is very similar to the Makefile in the first article in the series, with the exception that it also builds a user-space C program that interacts with the LKM. Listing 3: The Makefile for the LKM and the User-space Program (/extras/kernel/ebbchar/Makefile) 1 2 3 4 5 6 7 obj-m+=ebbchar.o all: make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules $(CC) testebbchar.c -o test clean: make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean User Access to the Device using Udev Rules Throughout this article, the program that interfaces to the LKM device is executed using sudo. It would be very useful to set up our LKM device so that it can be accessed by a particular user or group, while still protecting the file system. To address this issue, you can use an advanced feature of Linux called udev rules that enables you to customize the behavior of the udevd service. This service gives you some user-space control over devices on your Linux system. For example, to give user-level access to the ebbchar device, the first step is to identify the sysfs entry for the device. You can achieve this by using a simple find: root@beaglebone:/sys# find . -name "ebbchar" ./devices/virtual/ebb/ebbchar ./class/ebb/ebbchar
  • 15. ./module/ebbchar We then need to identify the KERNEL and SUBSYSTEM values about which to write the rule. You can use the udevadmcommand 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 by the devpath and then walks up the chain of parent devices. It prints for every device found, all possible attributes in the udev rules key format. A rule to 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 contained in the /etc/udev/rules.d directory. A new rule can be added as a file using these values, where the file begins with a priority number. Using a name such as 99- ebbchar.rules creates a new rule with the lowest priority, so that it does not interfere with other device rules. The rule can 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 50-spi.rules 60-omap-tty.rules 70-persistent-net.rules 99-ebbchar.rules molloyd@beaglebone:/etc/udev/rules.d$ more 99-ebbchar.rules #Rules file for the ebbchar device driver KERNEL=="ebbchar", SUBSYSTEM=="ebb", MODE="0666" 1 2 #Rules file for the ebbchar device driver KERNEL=="ebbchar", SUBSYSTEM=="ebb", MODE="0666" Once the rules file is added to your system, you 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 see that user and group now have the permissions required to read from and write to this device. Interestingly, the sticky bit is also set. The sticky bit usually means that write permission is not sufficient to delete files. Therefore, in the/tmp directory any user can create files, but no user can delete another user’s files. The sticky bit is represented by a capital T in the
  • 16. final character place. This usually appears as a lower-case t unless the executable (x) bit for others is set; however, when the x bit is not set it appears as a capital T. However, it is not entirely clear why the sticky bit is being set by udev — it appears to be unusual to udev rules under Debian. At this point the test application can be executed without requiring superuser permissions. The strace Command The strace command is a very useful debugging tool that can execute a program in order to intercept and record the system calls that it performs. The system call 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 not need the source code for the executable in order to view the output of strace. For example, you can utilize strace on your user-space application in order to view the communication between the user-space program and the kernel module, which results in the following for the test application: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo apt-get install strace molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ strace -v usage: strace [-dffhiqrtttTvVxx] [-a column] [-e 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 message to the device [T"..., 60Writing message …) = 60 write(3, "Testing the EBBChar device", 26) = 26 write(1, "Reading from the device... ", 27Reading from the device…) = 27 read(3, "", 100) = 0 write(1, "The received message is: [Testin"..., 66The received …) = 66 write(1, "End of the program ", 19End of the program) = 19 exit_group(0) = ? The system call output gives us impressive insight into the communication that takes place between the user-space program test and the /dev/ebbchar device driver. LKM Synchronization Problems There is a serious problem with the LKM that is described in Listing 2. In the first article in this series I pointed out that LKMs do not execute sequentially and that they can be interrupted. Those facts have important consequences for the code that is written in this article, which can be
  • 17. demonstrated as follows: Step 1: At the first terminal window shell you can execute the test application, but do not allow it to run to completion (by not pressing ENTER when prompted), as follows: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the first terminal window Writing message to the device [This is the message from the first terminal window]. Press ENTER to read back from the device... Step 2: Then at a second terminal window shell you can execute the same test application simultaneously as a second process on the Linux device. For example: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the second terminal window Writing message to the device [This is the message from the second terminal window]. Press ENTER to read back from the device... Step 3: You can then return to the first terminal window shell and press ENTER to run the program to completion, which results in the following output (shaded output is the repeated output from Step 1): molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the first terminal window Writing message to the device [This is the message from the first terminal 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)] End of the program molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ You can see that the received message is actually the message that was sent by the test application from Step 2, which is running in the second terminal window shell (not the first as might be expected). This is because the message that was sent in Step 2 overwrote the string message that was being stored by the LKM as a result of Step 1. Step 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 2):
  • 18. molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the second terminal window Writing message to the device [This is the message from the second terminal window]. Press 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 not storing any messages at that point in time. It has already delivered the stored message to the first terminal 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 by multiple processes to a shared resource. The easiest way to use this semaphore code within the kernel is to use mutexes, as there is a full set of helper functions and macros available. A simple way to prevent the problems described above is to prevent two processes from using the /dev/ebbchar device at the same time. A mutex is a lock that can set (put down) before a process begins using a shared resource. The lock can then be released (brought up) when the process is finished using the shared resource. When the lock has been set, no other process can access the locked code region. Once the mutex lock has been released by the process that locked it, the shared region of code is once again available to be accessed by the other process, which in turn locks the resource. 1 2 3 4 5 6 7 obj-m+=ebbchar.o all: make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
  • 19. $(CC) testebbchar.c -o test clean: make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) clean User Access to the Device using Udev Rules Throughout this article, the program that interfaces to the LKM device is executed using sudo. It would be very useful to set up our LKM device so that it can be accessed by a particular user or group, while still protecting the file system. To address this issue, you can use an advanced feature of Linux called udev rules that enables you to customize the behavior of the udevd service. This service gives you some user-space control over devices on your Linux system. For example, to give user-level access to the ebbchar device, the first step is to identify the sysfs entry for the device. You can achieve this by using a simple find: root@beaglebone:/sys# find . -name "ebbchar" ./devices/virtual/ebb/ebbchar ./class/ebb/ebbchar ./module/ebbchar We then need to identify the KERNEL and SUBSYSTEM values about which to write the rule. You can use the udevadmcommand 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 by the devpath and then walks up the chain of parent devices. It prints for every device found, all possible attributes in the udev rules key format. A rule to 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 contained in the /etc/udev/rules.d directory. A new rule can be added as a file using these values, where the file begins with a priority number. Using a name such as 99- ebbchar.rules creates a new rule with the lowest priority, so that it does not interfere with other device rules. The rule can 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 50-spi.rules 60-omap-tty.rules 70-persistent-net.rules 99-ebbchar.rules
  • 20. molloyd@beaglebone:/etc/udev/rules.d$ more 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 driver (/extras/kernel/ebbchar/99-ebbchar.rules) 1 2 #Rules file for the ebbchar device driver KERNEL=="ebbchar", SUBSYSTEM=="ebb", MODE="0666" Once the rules file is added to your system, you 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 see that user and group now have the permissions required to read from and write to this device. Interestingly, the sticky bit is also set. The sticky bit usually means that write permission is not sufficient to delete files. Therefore, in the/tmp directory any user can create files, but no user can delete another user’s files. The sticky bit is represented by a capital T in the final character place. This usually appears as a lower-case t unless the executable (x) bit for others is set; however, when the x bit is not set it appears as a capital T. However, it is not entirely clear why the sticky bit is being set by udev — it appears to be unusual to udev rules under Debian. At this point the test application can be executed without requiring superuser permissions. The strace Command The strace command is a very useful debugging tool that can execute a program in order to intercept and record the system calls that it performs. The system call 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 not need the source code for the executable in order to view the output of strace. For example, you can utilize strace on your user-space application in order to view the communication between the user-space program and the kernel module, which results in the following for the test application: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo apt-get install strace molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ strace -v usage: strace [-dffhiqrtttTvVxx] [-a column] [-e 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
  • 21. … write(1, "Starting device test code exampl"..., 37Starting de…) = 37 open("/dev/ebbchar", O_RDWR) = 3 write(1, "Writing message to the device [T"..., 60Writing message …) = 60 write(3, "Testing the EBBChar device", 26) = 26 write(1, "Reading from the device... ", 27Reading from the device…) = 27 read(3, "", 100) = 0 write(1, "The received message is: [Testin"..., 66The received …) = 66 write(1, "End of the program ", 19End of the program) = 19 exit_group(0) = ? The system call output gives us impressive insight into the communication that takes place between the user-space program test and the /dev/ebbchar device driver. LKM Synchronization Problems There is a serious problem with the LKM that is described in Listing 2. In the first article in this series I pointed out that LKMs do not execute sequentially and that they can be interrupted. Those facts have important consequences for the code that is written in this article, which can be demonstrated as follows: Step 1: At the first terminal window shell you can execute the test application, but do not allow it to run to completion (by not pressing ENTER when prompted), as follows: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the first terminal window Writing message to the device [This is the message from the first terminal window]. Press ENTER to read back from the device... Step 2: Then at a second terminal window shell you can execute the same test application simultaneously as a second process on the Linux device. For example: molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the second terminal window Writing message to the device [This is the message from the second terminal window]. Press ENTER to read back from the device... Step 3: You can then return to the first terminal window shell and press ENTER to run the program to completion, which results in the following output (shaded output is the repeated output from Step 1):
  • 22. molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the first terminal window Writing message to the device [This is the message from the first terminal 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)] End of the program molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ You can see that the received message is actually the message that was sent by the test application from Step 2, which is running in the second terminal window shell (not the first as might be expected). This is because the message that was sent in Step 2 overwrote the string message that was being stored by the LKM as a result of Step 1. Step 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 2): molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test Starting device test code example... Type in a short string to send to the kernel module: This is the message from the second terminal window Writing message to the device [This is the message from the second terminal window]. Press 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 not storing any messages at that point in time. It has already delivered the stored message to the first terminal 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 by multiple processes to a shared resource. The easiest way to use this semaphore code within the kernel is to use mutexes, as there is a full set of helper functions and macros available. A simple way to prevent the problems described above is to prevent two processes from using the /dev/ebbchar device at the same time. A mutex is a lock that can set (put down) before a
  • 23. process begins using a shared resource. The lock can then be released (brought up) when the process is finished using the shared resource. When the lock has been set, no other process can access the locked code region. Once the mutex lock has been released by the process that locked it, the shared region of code is once again available to be accessed by the other process, which in turn locks the resource.