Introduction
In this series of articles I describe how you can write a Linux loadable kernel module (LKM) for an embedded Linux device. This is the second article in the series — please read “Writing a Linux Kernel Module — Part 1: Introduction” before moving on to this article, as it explains how to build, load and unload loadable kernel modules (LKMs). Such description is not repeated in this article.
Character Device Drivers
A character device typically transfers data to and from a user application — they behave like pipes or serial ports, instantly reading or writing the byte data in a 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
This article describes 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 release
system 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.
file_operations
Data Structure of the /linux/fs.h (Segment)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// Note: __user refers to a user-space address. struct file_operations { struct module *owner; // Pointer to the LKM that owns the structure loff_t (*llseek) (struct file *, loff_t, int); // Change current read/write position in a file ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); // Used to retrieve data from the device ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); // Used to send data to the device ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); // Asynchronous read ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); // Asynchronous write ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); // possibly asynchronous read ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); // possibly asynchronous write int (*iterate) (struct file *, struct dir_context *); // called when VFS needs to read the directory contents unsigned int (*poll) (struct file *, struct poll_table_struct *); // Does a read or write block? long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); // Called by the ioctl system call long (*compat_ioctl) (struct file *, unsigned int, unsigned long); // Called by the ioctl system call int (*mmap) (struct file *, struct vm_area_struct *); // Called by mmap system call int (*mremap)(struct file *, struct vm_area_struct *); // Called by memory remap system call int (*open) (struct inode *, struct file *); // first operation performed on a device file int (*flush) (struct file *, fl_owner_t id); // called when a process closes its copy of the descriptor int (*release) (struct inode *, struct file *); // called when a file structure is being released int (*fsync) (struct file *, loff_t, loff_t, int datasync); // notify device of change in its FASYNC flag int (*aio_fsync) (struct kiocb *, int datasync); // synchronous notify device of change in its FASYNC flag int (*fasync) (int, struct file *, int); // asynchronous notify device of change in its FASYNC flag int (*lock) (struct file *, int, struct file_lock *); // used to implement file locking … }; |
For further information, there is an excellent guide on the File Operations data structure at: Kernel.org Virtual File Systems
[tagline_box backgroundcolor=”” shadow=”yes” shadowopacity=”0.7″ border=”0px” bordercolor=”” highlightposition=”top” content_alignment=”left” link=”https://github.com/derekmolloy/exploringBB/tree/master/extras/kernel/” linktarget=”_blank” modal=”” button_size=”” button_shape=”” button_type=”” buttoncolor=”” button=”Get Source Code” title=”Source Code for this Discussion” description=”” animation_type=”0″ animation_direction=”down” animation_speed=”0.1″ class=”” id=””]
All of the code for this discussion is available in the GitHub repository for the book Exploring BeagleBone. The code can be viewed publicly at: the ExploringBB GitHub Kernel Project directory, and/or you can clone the repository on your BeagleBone (or other Linux device) by typing:
1 2 |
molloyd@beaglebone:~$ sudo apt-get install git molloyd@beaglebone:~$ git clone https://github.com/derekmolloy/exploringBB.git |
The /extras/kernel/ebbchar directory is the most important resource for the next part of this article. The auto-generated Doxygen documentation for these code examples is available in HTML format and PDF format.
[/tagline_box]
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:
dev_open()
: Called each time the device is opened from user space.dev_read()
: Called when data is sent from the device to user space.dev_write()
: Called when data is sent from user space to the device.dev_release()
: Called when the device is closed in user space.
Drivers have a class name and a device name. In Listing 2, ebb (Exploring BeagleBone) is used as the class name, and ebbchar as the device name. This results in the creation of a device that appears on the file system at /sys/class/ebb/ebbchar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
/** * @file ebbchar.c * @author Derek Molloy * @date 7 April 2015 * @version 0.1 * @brief An introductory character driver to support the second article of my series on * Linux loadable kernel module (LKM) development. This module maps to /dev/ebbchar and * comes with a helper C program that can be run in Linux user space to communicate with * this the LKM. * @see http://www.derekmolloy.ie/ for a full description and follow-up descriptions. */ #include <linux/init.h> // Macros used to mark up functions e.g. __init __exit #include <linux/module.h> // Core header for loading LKMs into the kernel #include <linux/device.h> // Header to support the kernel Driver Model #include <linux/kernel.h> // Contains types, macros, functions for the kernel #include <linux/fs.h> // Header for the Linux file system support #include <linux/uaccess.h> // Required for the copy to user function #define DEVICE_NAME "ebbchar" ///< The device will appear at /dev/ebbchar using this value #define CLASS_NAME "ebb" ///< The device class -- this is a character device driver MODULE_LICENSE("GPL"); ///< The license type -- this affects available functionality MODULE_AUTHOR("Derek Molloy"); ///< The author -- visible when you use modinfo MODULE_DESCRIPTION("A simple Linux char driver for the BBB"); ///< The description -- see modinfo MODULE_VERSION("0.1"); ///< A version number to inform users static int majorNumber; ///< Stores the device number -- determined automatically static char message[256] = {0}; ///< Memory for the string that is passed from userspace static short size_of_message; ///< Used to remember the size of the string stored static int numberOpens = 0; ///< Counts the number of times the device is opened static struct class* ebbcharClass = NULL; ///< The device-driver class struct pointer static struct device* ebbcharDevice = NULL; ///< The device-driver device struct pointer // The prototype functions for the character driver -- must come before the struct definition static int dev_open(struct inode *, struct file *); static int dev_release(struct inode *, struct file *); static ssize_t dev_read(struct file *, char *, size_t, loff_t *); static ssize_t dev_write(struct file *, const char *, size_t, loff_t *); /** @brief Devices are represented as file structure in the kernel. The file_operations structure from * /linux/fs.h lists the callback functions that you wish to associated with your file operations * using a C99 syntax structure. char devices usually implement open, read, write and release calls */ static struct file_operations fops = { .open = dev_open, .read = dev_read, .write = dev_write, .release = dev_release, }; /** @brief The LKM initialization function * The static keyword restricts the visibility of the function to within this C file. The __init * macro means that for a built-in driver (not a LKM) the function is only used at initialization * time and that it can be discarded and its memory freed up after that point. * @return returns 0 if successful */ static int __init ebbchar_init(void){ printk(KERN_INFO "EBBChar: Initializing the EBBChar LKM\n"); // Try to dynamically allocate a major number for the device -- more difficult but worth it majorNumber = register_chrdev(0, DEVICE_NAME, &fops); if (majorNumber<0){ printk(KERN_ALERT "EBBChar failed to register a major number\n"); return majorNumber; } printk(KERN_INFO "EBBChar: registered correctly with major number %d\n", majorNumber); // Register the device class ebbcharClass = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(ebbcharClass)){ // Check for error and clean up if there is unregister_chrdev(majorNumber, DEVICE_NAME); printk(KERN_ALERT "Failed to register device class\n"); return PTR_ERR(ebbcharClass); // Correct way to return an error on a pointer } printk(KERN_INFO "EBBChar: device class registered correctly\n"); // Register the device driver ebbcharDevice = device_create(ebbcharClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME); if (IS_ERR(ebbcharDevice)){ // Clean up if there is an error class_destroy(ebbcharClass); // Repeated code but the alternative is goto statements unregister_chrdev(majorNumber, DEVICE_NAME); printk(KERN_ALERT "Failed to create the device\n"); return PTR_ERR(ebbcharDevice); } printk(KERN_INFO "EBBChar: device class created correctly\n"); // Made it! device was initialized return 0; } /** @brief The LKM cleanup function * Similar to the initialization function, it is static. The __exit macro notifies that if this * code is used for a built-in driver (not a LKM) that this function is not required. */ static void __exit ebbchar_exit(void){ device_destroy(ebbcharClass, MKDEV(majorNumber, 0)); // remove the device class_unregister(ebbcharClass); // unregister the device class class_destroy(ebbcharClass); // remove the device class unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number printk(KERN_INFO "EBBChar: Goodbye from the LKM!\n"); } /** @brief The device open function that is called each time the device is opened * This will only increment the numberOpens counter in this case. * @param inodep A pointer to an inode object (defined in linux/fs.h) * @param filep A pointer to a file object (defined in linux/fs.h) */ static int dev_open(struct inode *inodep, struct file *filep){ numberOpens++; printk(KERN_INFO "EBBChar: Device has been opened %d time(s)\n", numberOpens); return 0; } /** @brief This function is called whenever device is being read from user space i.e. data is * being sent from the device to the user. In this case is uses the copy_to_user() function to * send the buffer string to the user and captures any errors. * @param filep A pointer to a file object (defined in linux/fs.h) * @param buffer The pointer to the buffer to which this function writes the data * @param len The length of the b * @param offset The offset if required */ static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset){ int error_count = 0; // copy_to_user has the format ( * to, *from, size) and returns 0 on success error_count = copy_to_user(buffer, message, size_of_message); if (error_count==0){ // if true then have success printk(KERN_INFO "EBBChar: Sent %d characters to the user\n", size_of_message); return (size_of_message=0); // clear the position to the start and return 0 } else { printk(KERN_INFO "EBBChar: Failed to send %d characters to the user\n", error_count); return -EFAULT; // Failed -- return a bad address message (i.e. -14) } } /** @brief This function is called whenever the device is being written to from user space i.e. * data is sent to the device from the user. The data is copied to the message[] array in this * LKM using the sprintf() function along with the length of the string. * @param filep A pointer to a file object * @param buffer The buffer to that contains the string to write to the device * @param len The length of the array of data that is being passed in the const char buffer * @param offset The offset if required */ static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){ sprintf(message, "%s(%zu letters)", buffer, len); // appending received string with its length size_of_message = strlen(message); // store the length of the stored message printk(KERN_INFO "EBBChar: Received %zu characters from the user\n", len); return len; } /** @brief The device release function that is called whenever the device is closed/released by * the userspace program * @param inodep A pointer to an inode object (defined in linux/fs.h) * @param filep A pointer to a file object (defined in linux/fs.h) */ static int dev_release(struct inode *inodep, struct file *filep){ printk(KERN_INFO "EBBChar: Device successfully closed\n"); return 0; } /** @brief A module must use the module_init() module_exit() macros from linux/init.h, which * identify the initialization function at insertion time and the cleanup function (as * listed above) */ module_init(ebbchar_init); module_exit(ebbchar_exit); |
In addition to the points described by the comments in Listing 2, there are some additional points:
- This code has a fixed message size of 256 characters — this will be improved in later articles though the dynamic allocation of memory.
- This code is not multi-process safe — that is addressed later in this article.
- The
ebbchar_init()
function is much longer than the last article. That is because it is now automatically allocating a major number to the device, registering the device class, and registering the device driver. Importantly, you will notice that if anything goes wrong that the code carefully “backs out” of the successful operations. To achieve this I have repeated code (which I always dislike), but the alternative is to usegoto
statements, which is even less palatable (albeit slightly tidier). - The
PTR_ERR()
is a function that is defined inlinux/err.h
that retrieves the error number from the pointer. - The functions
sprintf()
andstrlen()
are available in the kernel through the inclusion of linux/kernel.h and indirectly through linux/string.h respectively. The functions in string.h are architecture dependent.
The next step is to build this code into a kernel module.
Building and Testing the LKM
A Makefile is required to build the LKM, as provided in 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.
1 2 3 4 5 6 7 8 |
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 rm test |
Listing 4 is a short program that requests a string from the user, and writes it to the /dev/ebbchar device. After a subsequent key press (ENTER) it then reads the response from the device and displays it in the terminal window.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
/** * @file testebbchar.c * @author Derek Molloy * @date 7 April 2015 * @version 0.1 * @brief A Linux user space program that communicates with the ebbchar.c LKM. It passes a * string to the LKM and reads the response from the LKM. For this example to work the device * must be called /dev/ebbchar. * @see http://www.derekmolloy.ie/ for a full description and follow-up descriptions. */ #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<fcntl.h> #include<string.h> #include<unistd.h> #define BUFFER_LENGTH 256 ///< The buffer length (crude but fine) static char receive[BUFFER_LENGTH]; ///< The receive buffer from the LKM int main(){ int ret, fd; char stringToSend[BUFFER_LENGTH]; printf("Starting device test code example...\n"); fd = open("/dev/ebbchar", O_RDWR); // Open the device with read/write access if (fd < 0){ perror("Failed to open the device..."); return errno; } printf("Type in a short string to send to the kernel module:\n"); scanf("%[^\n]%*c", stringToSend); // Read in a string (with spaces) printf("Writing message to the device [%s].\n", stringToSend); ret = write(fd, stringToSend, strlen(stringToSend)); // Send the string to the LKM if (ret < 0){ perror("Failed to write the message to the device."); return errno; } printf("Press ENTER to read back from the device...\n"); getchar(); printf("Reading from the device...\n"); ret = read(fd, receive, BUFFER_LENGTH); // Read the response from the LKM if (ret < 0){ perror("Failed to read the message from the device."); return errno; } printf("The received message is: [%s]\n", receive); printf("End of the program\n"); return 0; } |
In addition to the points described by the comments in Listing 4, there are some additional points:
- The
%[^\n]%*c
uses the scanset specifiers, which are represented by%[]
to use the^
character to stop reading after the first occurrence of the\n
character. In addition, the%*c
ignores the trailing character, ensuring that the subsequentgetchar()
function works as required. Essentially, thescanf()
code just reads in a sentence. Ifscanf()
was used with a regular%s
call then the string would terminated at the first occurrence of the space character. - The
getchar()
allows the program to pause at that point until the ENTER key is pressed. This is necessary to demonstrate a problem with the current code formulation. - The program then reads the response from the LKM and displays it in the terminal window.
All going well, the process to build the kernel module should be straightforward, provided that you have installed the Linux headers as described in the first article. The steps are as follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ make
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ls -l *.ko
-rw-r--r-- 1 molloyd molloyd 7075 Apr 8 19:04 ebbchar.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ls -l test
-rwxr-xr-x 1 molloyd molloyd 6342 Apr 8 19:23 test
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo insmod ebbchar.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ lsmod
Module Size Used by
ebbchar 2521 0
The device is now present in the /dev directory, with the following attributes:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ cd /dev
molloyd@beaglebone:/dev$ ls -l ebb*
crw------- 1 root root 240, 0 Apr 8 19:28 ebbchar
You can see that the major number is 240 — this is automatically assigned by the code in Listing 2.
We can then use the testebbchar program (Listing 4) to test that the LKM is working correctly. For the moment, the test program must be executed with root privileges — that issue is addressed shortly.
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo insmod ebbchar.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo ./test
Starting device test code example...
Type in a short string to send to the kernel module:
This is a test of the ebbchar LKM
Writing message to the device [
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$ sudo rmmod ebbchar
The printk()
output can be viewed by examining the kernel logs as follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo tail -f /var/log/kern.log
Apr 11 22:24:50 beaglebone kernel: [358664.365942] EBBChar: Initializing the EBBChar LKM
Apr 11 22:24:50 beaglebone kernel: [358664.365980] EBBChar: registered correctly with major number 240
Apr 11 22:24:50 beaglebone kernel: [358664.366061] EBBChar: device class registered correctly
Apr 11 22:24:50 beaglebone kernel: [358664.368383] EBBChar: device class created correctly
Apr 11 22:25:15 beaglebone kernel: [358689.812483] EBBChar: Device has been opened 1 time(s)
Apr 11 22:25:31 beaglebone kernel: [358705.451551] EBBChar: Received 33 characters from the user
Apr 11 22:25:32 beaglebone kernel: [358706.403818] EBBChar: Sent 45 characters to the user
Apr 11 22:25:32 beaglebone kernel: [358706.404207] EBBChar: Device successfully closed
Apr 11 22:25:44 beaglebone kernel: [358718.497000] EBBChar: Goodbye from the LKM!
You can see that 33 characters are sent to the LKM but 45 characters are returned — this is due to the addition of the 12 characters “(33 letters)
” to the string data that specifies the length of the string that was originally sent. This addition is performed as a test in order to be certain that the code is sending and receiving unique data.
There are two significant problems with the current LKM. The first is that the LKM device can only be accessed with superuser permissions, and the second is that the current LKM is not multi-process safe.
User Access to the Device using Udev Rules
Throughout this 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 udevadm command to perform this task:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ udevadm info -a -p /sys/class/ebb/ebbchar
Udevadm info starts with the device specified 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=="
SUBSYSTEM=="
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
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.
[tagline_box backgroundcolor=”” shadow=”yes” shadowopacity=”0.7″ border=”0px” bordercolor=”” highlightposition=”top” content_alignment=”left” link=”” linktarget=”” modal=”” button_size=”” button_shape=”” button_type=”” buttoncolor=”” button=”” title=”The strace Command” description=”” animation_type=”0″ animation_direction=”down” animation_speed=”0.1″ claa ss=”” id=””]
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...\n", 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\n", 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.
[/tagline_box]
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
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
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):
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
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$
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):
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
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.
There are only very minor changes required to the code in order to implement mutex locks. An outline of these changes is provided in Listing 6 below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include <linux/mutex.h> /// Required for the mutex functionality … static DEFINE_MUTEX(ebbchar_mutex); /// A macro that is used to declare a new mutex that is visible in this file /// results in a semaphore variable ebbchar_mutex with value 1 (unlocked) /// DEFINE_MUTEX_LOCKED() results in a variable with value 0 (locked) … static int __init ebbchar_init(void){ … mutex_init(&ebbchar_mutex); /// Initialize the mutex lock dynamically at runtime } static int dev_open(struct inode *inodep, struct file *filep){ if(!mutex_trylock(&ebbchar_mutex)){ /// Try to acquire the mutex (i.e., put the lock on/down) /// returns 1 if successful and 0 if there is contention printk(KERN_ALERT "EBBChar: Device in use by another process"); return -EBUSY; } … } static int dev_release(struct inode *inodep, struct file *filep){ mutex_unlock(&ebbchar_mutex); /// Releases the mutex (i.e., the lock goes up) … } static void __exit ebbchar_exit(void){ mutex_destroy(&ebbchar_mutex); /// destroy the dynamically-allocated mutex … } |
The full source code example is available in the /extras/kernel/ebbcharmutex/ directory. The final LKM is called ebbcharmutex.c. If you now perform the same test on the code that contains the mutex locks, you will observe a different behavior. For example:
Step 1: Using the first terminal window shell you can load the module and execute the test application, which results in the following output:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ sudo insmod ebbcharmutex.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ lsmod
Module Size Used by
ebbcharmutex 2754 0
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ ls -l /dev/ebb*
crw-rw-rwT 1 root root 240, 0 Apr 12 15:26 /dev/ebbchar
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ ./test
Starting device test code example...
Type in a short string to send to the kernel module:
Testing the ebbchar LKM that has mutex code
Writing message to the device
Press ENTER to read back from the device...
Step 2: Then using the second terminal window shell you can attempt to execute the test application to create the second process:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ ./test
Starting device test code example...
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$
As expected and required, the second process fails to access the device.
Step 3: Returning to the first terminal window, the program can be allowed to run to completion by pressing ENTER:
Starting device test code example...
Type in a short string to send to the kernel module:
Testing the ebbchar LKM that has mutex code
Writing message to the device
Press ENTER to read back from the device...
Reading from the device...
The received message is:
End of the program
At this point the second terminal window shell can now execute the test program, whereupon it will acquire the mutex lock and run correctly.
Conclusion
There are several different issues described in this article. The important outcomes of this article are that:
- You can now create your own device such as /dev/ebbchar, which you can write information to and read information from. This is important, as it provides a bridge between the Linux user space and the Linux kernel space. It enables you to develop advanced drivers, such as communication devices, which can be controlled by C code that is running in user space.
- You can appreciate why it is important to be aware of the synchronization issues that can arise with kernel module programming.
- You can use udev rules to alter the properties of a device as it is loaded.
The next article in this series examines how you can interface to GPIO devices, such as physical button and LED circuits, enabling you to write your own custom drivers for embedded Linux applications that interface to custom hardware. You can read that article here: Writing a Linux Kernel Module — Part 3: Interfacing to GPIOs (coming soon!). Finally, Listing 7 and Listing 8 are provided for your reference — they provide a list of the standard error states that are used in this series of articles, along with their associated error numbers and descriptions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#define EPERM 1 /* Operation not permitted */ #define ENOENT 2 /* No such file or directory */ #define ESRCH 3 /* No such process */ #define EINTR 4 /* Interrupted system call */ #define EIO 5 /* I/O error */ #define ENXIO 6 /* No such device or address */ #define E2BIG 7 /* Argument list too long */ #define ENOEXEC 8 /* Exec format error */ #define EBADF 9 /* Bad file number */ #define ECHILD 10 /* No child processes */ #define EAGAIN 11 /* Try again */ #define ENOMEM 12 /* Out of memory */ #define EACCES 13 /* Permission denied */ #define EFAULT 14 /* Bad address */ #define ENOTBLK 15 /* Block device required */ #define EBUSY 16 /* Device or resource busy */ #define EEXIST 17 /* File exists */ #define EXDEV 18 /* Cross-device link */ #define ENODEV 19 /* No such device */ #define ENOTDIR 20 /* Not a directory */ #define EISDIR 21 /* Is a directory */ #define EINVAL 22 /* Invalid argument */ #define ENFILE 23 /* File table overflow */ #define EMFILE 24 /* Too many open files */ #define ENOTTY 25 /* Not a typewriter */ #define ETXTBSY 26 /* Text file busy */ #define EFBIG 27 /* File too large */ #define ENOSPC 28 /* No space left on device */ #define ESPIPE 29 /* Illegal seek */ #define EROFS 30 /* Read-only file system */ #define EMLINK 31 /* Too many links */ #define EPIPE 32 /* Broken pipe */ #define EDOM 33 /* Math argument out of domain of func */ #define ERANGE 34 /* Math result not representable */ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
#ifndef _ASM_GENERIC_ERRNO_H #define _ASM_GENERIC_ERRNO_H #include <asm-generic/errno-base.h> #define EDEADLK 35 /* Resource deadlock would occur */ #define ENAMETOOLONG 36 /* File name too long */ #define ENOLCK 37 /* No record locks available */ #define ENOSYS 38 /* Function not implemented */ #define ENOTEMPTY 39 /* Directory not empty */ #define ELOOP 40 /* Too many symbolic links encountered */ #define EWOULDBLOCK EAGAIN /* Operation would block */ #define ENOMSG 42 /* No message of desired type */ #define EIDRM 43 /* Identifier removed */ #define ECHRNG 44 /* Channel number out of range */ #define EL2NSYNC 45 /* Level 2 not synchronized */ #define EL3HLT 46 /* Level 3 halted */ #define EL3RST 47 /* Level 3 reset */ #define ELNRNG 48 /* Link number out of range */ #define EUNATCH 49 /* Protocol driver not attached */ #define ENOCSI 50 /* No CSI structure available */ #define EL2HLT 51 /* Level 2 halted */ #define EBADE 52 /* Invalid exchange */ #define EBADR 53 /* Invalid request descriptor */ #define EXFULL 54 /* Exchange full */ #define ENOANO 55 /* No anode */ #define EBADRQC 56 /* Invalid request code */ #define EBADSLT 57 /* Invalid slot */ #define EDEADLOCK EDEADLK #define EBFONT 59 /* Bad font file format */ #define ENOSTR 60 /* Device not a stream */ #define ENODATA 61 /* No data available */ #define ETIME 62 /* Timer expired */ #define ENOSR 63 /* Out of streams resources */ #define ENONET 64 /* Machine is not on the network */ #define ENOPKG 65 /* Package not installed */ #define EREMOTE 66 /* Object is remote */ #define ENOLINK 67 /* Link has been severed */ #define EADV 68 /* Advertise error */ #define ESRMNT 69 /* Srmount error */ #define ECOMM 70 /* Communication error on send */ #define EPROTO 71 /* Protocol error */ #define EMULTIHOP 72 /* Multihop attempted */ #define EDOTDOT 73 /* RFS specific error */ #define EBADMSG 74 /* Not a data message */ #define EOVERFLOW 75 /* Value too large for defined data type */ #define ENOTUNIQ 76 /* Name not unique on network */ #define EBADFD 77 /* File descriptor in bad state */ #define EREMCHG 78 /* Remote address changed */ #define ELIBACC 79 /* Can not access a needed shared library */ #define ELIBBAD 80 /* Accessing a corrupted shared library */ #define ELIBSCN 81 /* .lib section in a.out corrupted */ #define ELIBMAX 82 /* Attempting to link in too many shared libraries */ #define ELIBEXEC 83 /* Cannot exec a shared library directly */ #define EILSEQ 84 /* Illegal byte sequence */ #define ERESTART 85 /* Interrupted system call should be restarted */ #define ESTRPIPE 86 /* Streams pipe error */ #define EUSERS 87 /* Too many users */ #define ENOTSOCK 88 /* Socket operation on non-socket */ #define EDESTADDRREQ 89 /* Destination address required */ #define EMSGSIZE 90 /* Message too long */ #define EPROTOTYPE 91 /* Protocol wrong type for socket */ #define ENOPROTOOPT 92 /* Protocol not available */ #define EPROTONOSUPPORT 93 /* Protocol not supported */ #define ESOCKTNOSUPPORT 94 /* Socket type not supported */ #define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ #define EPFNOSUPPORT 96 /* Protocol family not supported */ #define EAFNOSUPPORT 97 /* Address family not supported by protocol */ #define EADDRINUSE 98 /* Address already in use */ #define EADDRNOTAVAIL 99 /* Cannot assign requested address */ #define ENETDOWN 100 /* Network is down */ #define ENETUNREACH 101 /* Network is unreachable */ #define ENETRESET 102 /* Network dropped connection because of reset */ #define ECONNABORTED 103 /* Software caused connection abort */ #define ECONNRESET 104 /* Connection reset by peer */ #define ENOBUFS 105 /* No buffer space available */ #define EISCONN 106 /* Transport endpoint is already connected */ #define ENOTCONN 107 /* Transport endpoint is not connected */ #define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ #define ETOOMANYREFS 109 /* Too many references: cannot splice */ #define ETIMEDOUT 110 /* Connection timed out */ #define ECONNREFUSED 111 /* Connection refused */ #define EHOSTDOWN 112 /* Host is down */ #define EHOSTUNREACH 113 /* No route to host */ #define EALREADY 114 /* Operation already in progress */ #define EINPROGRESS 115 /* Operation now in progress */ #define ESTALE 116 /* Stale NFS file handle */ #define EUCLEAN 117 /* Structure needs cleaning */ #define ENOTNAM 118 /* Not a XENIX named type file */ #define ENAVAIL 119 /* No XENIX semaphores available */ #define EISNAM 120 /* Is a named type file */ #define EREMOTEIO 121 /* Remote I/O error */ #define EDQUOT 122 /* Quota exceeded */ #define ENOMEDIUM 123 /* No medium found */ #define EMEDIUMTYPE 124 /* Wrong medium type */ #define ECANCELED 125 /* Operation Canceled */ #define ENOKEY 126 /* Required key not available */ #define EKEYEXPIRED 127 /* Key has expired */ #define EKEYREVOKED 128 /* Key has been revoked */ #define EKEYREJECTED 129 /* Key was rejected by service */ /* for robust mutexes */ #define EOWNERDEAD 130 /* Owner died */ #define ENOTRECOVERABLE 131 /* State not recoverable */ #define ERFKILL 132 /* Operation not possible due to RF-kill */ #define EHWPOISON 133 /* Memory page has hardware error */ #endif |
Hi Derek,
thanks for your interesting articles. In Listing 2, you are using copy_to_user in dev_read. But I miss the corresponding copy_from_user in dev_write. So the code might work on a BBB but fail in other use cases.
Frank
Hi FrankB, I am using the sprintf() function to copy the data for the dev_write. This allows me to format the response so that I can pass back an int in the string. This should work on all platforms. Kind regards, Derek.
I am not sure if you can use the sprintf() function in the kernel space as this is the part of your standard library. Correct me if I am wrong!!
Kernel has a bunch of string functions implemented, including a bunch of useful standard library clone functions – see lib/vsprintf.c, for example.
And thanks to Derek for these articles, they’re a great starting point for all kind of fun! 😀
Hi Derek,
Thanks a lot for this post.
But I agree with FrankB: You are not sure the user memory is correctly mapped, and you should copy_from_user().
Please check O’Reilly’s “Linux Device Drivers, Third Edition”, chapter 3, page 63, section “read and write”.
http://www.oreilly.com/openbook/linuxdrive3/book/ch03.pdf
It is stated: “Let us repeat that the buff argument to the read and write methods is a user-space pointer. Therefore, it cannot be directly dereferenced by kernel code”.
So the correct code should be:
1) alloc some temp buffer, with proper length (the length provided in the call)
2) copy the user buffer to the new buffer with copy_from_user(), or similar mechanism
3) perform your sprintf() [1] from that buffer
4) free the temp buffer
If not, you could get an Oops, or even worse a panic.
[1] Using sprintf() seems dangerous, as it implies:
1) that the user buffer is followed by a zero, *after* length chars, which is not guaranteed
2) that that character memory is mappable by the kernel, which is not guaranteed
if the buffer is exactly one page size, I doubt the next character will be mapped at all, and you will also get an Oops/Panic.
Sorry, I pushed the send button without double-checking 🙂
In fact, you don’t need to allocate a new buffer, and directly copy_from_user() to your driver buffer (beside overflow control).
Hi Derek, Thanks for these tutorials, they’re very clear and informative. I had an issue when using the sprintf on my BBB. I kept getting an error:
“Unhandled fault: page domain fault…”
It seemed to be resolved when I replaced the sprintf() call with a copy_from_user() call. Anyway, I might have done something wrong, but thought I’d share that with anyone encountering this issue.
Bilal, I am getting the same error, however changing the sprintf() to copy_from_user() does not change anything. If anyone has any ideas please let me know.
use copy_from_user instead of sprintk
My Kernel Module crushes with “sprintf”, but works fine with “copy_from_user”:
Mar 30 21:25:20 beaglebone kernel: [12235.016049] Unhandled fault: page domain fault (0x01b) at 0x00d37a08
Mar 30 21:25:20 beaglebone kernel: [12235.022444] pgd = daf30000
Mar 30 21:25:20 beaglebone kernel: [12235.025160] [00d37a08] *pgd=9a10a831, *pte=944dd34f, *ppte=944dd83f
Mar 30 21:25:20 beaglebone kernel: [12235.031486] Internal error: : 1b [#1] SMP ARM
Mar 30 21:25:20 beaglebone kernel: [12235.035863] Modules linked in: sample2(O) snd_soc_hdmi_codec snd_soc_simple_card tilcdc ccm arc4 rtl8192cu rtl_usb rtl8192c_common rtlwifi mac80211 cfg80211 snd_soc_davinci_mcasp snd_soc_ed
ma snd_soc_omap snd_soc_core snd_pcm_dmaengine snd_pcm snd_timer snd soundcore evdev tda998x uio_pdrv_genirq uio usb_f_mass_storage usb_f_acm u_serial usb_f_ecm usb_f_rndis u_ether libcomposite iptable_nat nf_conntrack_ipv4 nf_
defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack iptable_mangle iptable_filter bluetooth rfkill spidev tieqep ip_tables x_tables pru_rproc pruss_intc pruss [last unloaded: sample2]
Mar 30 21:25:20 beaglebone kernel: [12235.088242] CPU: 0 PID: 1799 Comm: bash Tainted: G O 4.4.91-ti-r133 #1
Hello Derek,
You just saved me a week of work with this tutorial. All the other websites I consulted were frankly quite terrible so you’re a godsend. Thank you.
Cheers,
Dr Vitor Almeida, IST Lisbon
Hi Derek,
can you please explain what this line is doing: if (IS_ERR(ebbcharClass))
IS_ERR is a macro that tests if ebbcharClass is a valid pointer by examining its address
Couldn’t we check it by, checking if it points to a NULL pointer or not??
Superb ! a master piece i would say i have done the same article long before, if i write the article i could not at all deliver such a detail , but i did not create udev rather i used mknode command to create a node and i have assigned major and minor numbers to that, this advanced udev feature is also impressive,
Derek you are a superb teacher, the same thing from linux foundation would cost us a few hundred dollars 🙂
Please also extend your discussion with the LKM to most advanced concepts of kernel and i am eagerly waiting for your classes like these on the linux device model and linux driver model. which can be key concepts to have a knowledge
Thanks for your technical charity 🙂
Hi Derek Molloy,
I have a simple and may be stupid question that you have not written anything in dev_open but i got confuse that when this been opened. Can you give some idea about it.
thanks
Rabi
dev_open is a call back function which is being called by the kernel when you open the device, This means that all the tasks of opening a device is taken care inside the kernel itself. The call back function dev_open is giving you an opportunity to do stuffs specific to your device.
Hope this helps
Hi Mr. Derek,
I am really so inspired from this piece of great work. This is so precisely explained that I have never seen before on web or in books available on net. With the help of this, I have been able to create my own char device. I have not been used the BBB, but I am working on cubieboard 2. You know it is a Chinese board, there is lack of help about this board. One thing if you can do for further help, please write down a page for the interrupt driven device driver, and a test routine so we can be able to write a interrupt driven device driver.
Thanks in advance, also thanks for this great piece of work.
Shahid Riaz.
Thank you very well, very straightforward article and show the use of LKM in an easy and well documented way.
Great work.
Thank you for this! What an excellent example. A couple of questions if I may: 1) what controls where the device shows up under /sys/devices? On my system, this is /sys/devices/virtual/ebb/ebbchar/ – how would I go about creating a device directly under /sys/devices? Finally, if I wanted to create a generic device instead of a chrdev, is it as simple as changing register_chrdev() to device_register()? Thank you again for “showing us the way.” This is an elegant piece of work.
Hi Derek,
Your post has helped me a lot to learn about device drivers. I am trying to build similar character driver in raspberry pi raspbian OS. I have the source built and compiled in my x86 system and the image is also working fine. When i cross compile the character device driver (ebbchar.c) as loadable module in x86, it compiles without any problem and generates .ko file. When i try to insert the module in pi using sudo insmod, I get an error module cannot be loaded invalid parameters & the dmesg output as below :
ebbchar: disagrees about version of symbol device_create
[104853.869208] ebbchar: Unknown symbol device_create (err -22)
[104853.869241] ebbchar: disagrees about version of symbol device_destroy
[104853.869251] ebbchar: Unknown symbol device_destroy (err -22)
Please let me know the probable reason for this . I am trying to write kernel for the first time.
just saying thank you
Hey Derek, wonderful tut.
I am starting linux kernel programming, currently using 4.3.2 on my Ubuntu but I am facing some problems.There are almost no resources on kernel 4.x or even after 3.2, kinda lost. Can you refer to some book or some documentation that explains the stuff pretty good so that someone who never programmed the kernel can also do that.
Btw I know programming, doing it for several years.
Hi Derek,
thank you very much for this post!
In the user program, would it be safer to release the resource before exiting?
Have a good day!
Renton.
I wonder why not, once you release the device it should allow other user programs to use it, so for sure this is the right place to release the resource.
Thank you, this is a great tutoral!
Anderson.
First of all, thank you for producing this content. You present the material in a thorough, well-laid-out manner that makes it easy to digest. That is not easy to do, so again, thank you!
Separately, I think there’s a possibility to overflow the message array using the ebbchar device driver and the testebbchar code. The message array in the char driver and the stringToSend array are both defined to be 256 characters long. If 256, or even 255 + null, characters are written to /drv/ebbchar, then the dev_write function will use sprintf to put the message and then the message length into the message array. I know that you’re not using dynamic memory allocation until a later post (which I’m looking forward to by the way!), but it seems you could still use snprintf to avoid this issue. I think there’s a similar potential for overflow when the test code is reading stdin, but I’m not sure how to fix that one and still keep the unique format string you’re using. Please let me know if I have misinterpreted the code.
Thanks!
Thanks for article it is awesome, it would be great if you explain about device classes and device structure
P.S. I implemented simple character device
https://github.com/CROSP/simple-crypto-char-driver
Hi Derek,
Now, I am big fan of yours 🙂 … I am eagerly waiting for your next blog. What a simplicity and clarity!
Warm Regards,
Monark
Hi Derek,
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset){
int error_count = 0;
// copy_to_user has the format ( * to, *from, size) and returns 0 on success
error_count = copy_to_user(buffer, message, size_of_message);
if (error_count==0){ // if true then have success
printk(KERN_INFO “EBBChar: Sent %d characters to the user\n”, size_of_message);
return (size_of_message=0); // clear the position to the start and return 0
}
else {
printk(KERN_INFO “EBBChar: Failed to send %d characters to the user\n”, error_count);
return -EFAULT; // Failed — return a bad address message (i.e. -14)
}
}
return 0?
Should it not return the amount of bytes?
Dear Derek,
Thank you very much your tutorials. I read and a number of them. But I have spotted an error in your ebbchar kernel module.
The following sequence of commands causes module failure
cd /root/exploringBB/extras/kernel/ebbchar/
make
sudo insmod /root/exploringBB/extras/kernel/ebbchar/ebbchar.ko
cat /dev/ebbchar
echo ‘1’> /dev/ebbchar
cat /dev/ebbchar
sudo rmmod /root/exploringBB/extras/kernel/ebbchar/ebbchar.ko
Internal error: Oops:…
Why did this error occur?
Regards,
Alexander
I think the dev_write function have 2 problems :
– The sprintf function is wainting a c-string (array of char ending by ‘\0’) but when you send 1 to /dev/ebbchar (echo ‘1’> /dev/ebbchar) the ‘\0’ is missing. You can just copy len bytes of buffer into message yourself. There also a safer version of sprintf called snprinf.
– If you do that, the cat /dev/ebbchar will not display anything. You need to return the number of bytes written not 0.
b3dwin
Hi Derek,
With my version of the kernel (4.4.16-ti-r38) your source code for the LKM generates a kernel panic when the device is written to. The culprit is that the dev_write function uses sprintf to retrieve the buffer, when it should be using copy_from_user, analogous to the copy_to_user function.
Thanks Matthew, I am observing the same problem. I will make your suggested change.
Hi Derek,
Another minor thing – you should link to the next tutorial. This page says it is coming soon, when it already exists. Thanks for the great work!! Love your book.
Hi Derek,
Thank you for this very well structured tutorial.
I loaded the kernel module ebbchar.ko in my board ( lantiq gateway ) I can find the ebbchar device in
sys/devices/virtual/ebb/ebbchar
sys/class/ebb/ebbchar
sys/module/ebbchar
but not under /dev/
My main purpose is to communicate to the spi from the userspace, spidev does not appear in /dev/ so I though about applaying similar things of what you did to ebbchar to spi! but it is the same problem; the device does not appear in /dev/.
do you have any idea bout the source of this problem?
Thanks
Hi derek,
I want to write SPI device drivers for MCP3008 device on Beagle bone black.
from where i can start, I don’t that much knowledge in device drivers.
thanks
Hi,
thanks for the great tutorial. Based on this I implemented a character device for a keypad. This is working fine except one thing: While having the character device /dev/ebbchar open it is not detected that the kernel module is in use:
cat /sys/module/keypad/refcnt
0
So while unloading the module (this happens automatically while rebooting a nanopi) you will get a kernel oops because the character device is gone but it is still read by the daemon in userspace. Is there a way to mark the kernel module as busy?
Thanks!
You should add “.owner = THIS_MODULE,” in struct file_operations fops or you will get nasty kernel oopses when unloading or rebooting. Took me 2 weeks to find out.
Hello Derek,
I have a BeagleBone Green Wireless (Linux beaglebone 4.4.9-ti-r25 #1 SMP Thu May 5 23:08:13 UTC 2016 armv7l GNU/Linux)
I am trying to test your code from ebbchar folder and every time I am receiving internal error.
Please have a look on the next log and help me to figure out where is the problem and how to solve it:
“…..
root@beaglebone:~/exploringBB/extras/kernel/ebbchar# ls
99-ebbchar.rules ebbchar.c ebbchar.mod.dwo modules.order
Makefile ebbchar.ko ebbchar.mod.o test
Module.symvers ebbchar.mod.c ebbchar.o testebbchar.c
root@beaglebone:~/exploringBB/extras/kernel/ebbchar# rmmod ebbchar.ko
rmmod: ERROR: Module ebbchar is not currently loaded
root@beaglebone:~/exploringBB/extras/kernel/ebbchar# sudo insmod ebbchar.ko
root@beaglebone:~/exploringBB/extras/kernel/ebbchar# sudo tail -f /var/log/kern.log
Mar 26 11:51:12 beaglebone kernel: [13480.279549] [] (strnlen) from [] (string+0x2b/0xa8)
Mar 26 11:51:12 beaglebone kernel: [13480.286225] [] (string) from [] (0xffffffff)
Mar 26 11:51:12 beaglebone kernel: [13480.292291] Code: 4610 4770 bf00 b199 (7803) b19b
Mar 26 11:51:12 beaglebone kernel: [13480.297129] —[ end trace 6580beac8fcbb155 ]—
Mar 26 11:51:12 beaglebone kernel: [13480.396952] EBBChar: Device successfully closed
Mar 26 12:04:53 beaglebone kernel: [14301.433227] EBBChar: Goodbye from the LKM!
Mar 26 12:09:28 beaglebone kernel: [14576.480400] EBBChar: Initializing the EBBChar LKM
Mar 26 12:09:28 beaglebone kernel: [14576.485331] EBBChar: registered correctly with major number 245
Mar 26 12:09:28 beaglebone kernel: [14576.493203] EBBChar: device class registered correctly
Mar 26 12:09:28 beaglebone kernel: [14576.507695] EBBChar: device class created correctly
^Croot@beaglebone:~/exploringBB/extras/kernel/ebbchar# sudo ./test
Starting device test code example…
Type in a short string to send to the kernel module:
Simple test
Writing message to the device [Simple test].
Message from syslogd@beaglebone at Mar 26 12:10:45 …
kernel:[14652.492332] Internal error: : 1b [#7] SMP THUMB2
Message from syslogd@beaglebone at Mar 26 12:10:45 …
kernel:[14652.633704] Process test (pid: 11003, stack limit = 0xdc14c218)
Message from syslogd@beaglebone at Mar 26 12:10:45 …
kernel:[14652.639666] Stack: (0xdc14dea8 to 0xdc14e000)
Message from syslogd@beaglebone at Mar 26 12:10:45 …
kernel:[14652.644074] dea0: ffffffff bfc59218 ffffffff dc14df3c bfc5921a c03d18f5
Message from syslogd@beaglebone at Mar 26 12:10:45 …
kernel:[14652.652321] dec0: ffffffff ff0a0004 ffffffff ddefe800 c0b6da88 403a693b c0b6da88 bfc596c4
Message from syslogd@beaglebone at Mar 26 12:10:45 …
kernel:[14652.660566] dee0: 00000004 ff0a0004 ffffffff dc8ba609 00000000 c0b6da88 dc14df38 dc8ba609
Message from syslogd@beaglebone at Mar 26 12:10:45 …
kernel:[14652.668811] df00: dc14df78 0000000b bebe3ba0 00000000 00000000 c03d2205 dc14df38 dc8ba609
“
HI, I got the same internal error did you resolve the problem?
ok I solve the problem in function dev_write you need to change sprintf to copy_from_user(buffer,message,size_of_message). I think linux has some protection wich dont allow the kernel to read directly form user space, but Im not sure. I just know it works now. 🙂
Thank you for this great article Derek.
I believe you have an error in your ssize_t dev_read() function. The function is supposed to return the number of bytes read, instead instead it is returning true – so 1 byte read.
Try testing this app by doing:
$ sudo echo “abc” > /dev/ebbchar
then
$ cat /dev/ebbchar
It works using your test app because your read call is expecting a true of false response.
I compiled it under Linux Mint 64 on VM Ware machine.
If I try to echo a string to write on, I get this dump inside the kern.log:
May 27 00:39:48 antonio-vm kernel: [ 3484.993842] EBBChar: Device has been opened 1 time(s)
May 27 00:39:48 antonio-vm kernel: [ 3484.993878] BUG: unable to handle kernel paging request at 0000000001347008
May 27 00:39:48 antonio-vm kernel: [ 3484.993882] IP: [] strnlen+0x9/0x40
May 27 00:39:48 antonio-vm kernel: [ 3484.993889] PGD 13219067 PUD 13218067 PMD 8773067 PTE 80000000a90aa867
May 27 00:39:48 antonio-vm kernel: [ 3484.993894] Oops: 0001 [#14] SMP
May 27 00:39:48 antonio-vm kernel: [ 3484.993897] Modules linked in: ebbchar(OE) vmw_vsock_vmci_transport vsock zram lz4_compress snd_ens1371 coretemp snd_ac97_codec crct10dif_pclmul gameport crc32_pclmul ghash_clmulni_intel aesni_intel ac97_bus aes_x86_64 lrw gf128mul glue_helper snd_pcm ablk_helper cryptd snd_seq_midi snd_seq_midi_event snd_rawmidi vmw_balloon snd_seq snd_seq_device snd_timer snd soundcore input_leds joydev serio_raw i2c_piix4 shpchp vmw_vmci nfit 8250_fintek mac_hid binfmt_misc parport_pc ppdev lp parport autofs4 btrfs xor raid6_pq dm_mirror dm_region_hash dm_log vmw_pvscsi vmxnet3 hid_generic usbhid hid vmwgfx ttm drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops psmouse drm ahci e1000 libahci mptspi mptscsih mptbase scsi_transport_spi pata_acpi fjes [last unloaded: ebbchar]
May 27 00:39:48 antonio-vm kernel: [ 3484.993932] CPU: 3 PID: 16320 Comm: bash Tainted: G D W OE 4.4.0-78-generic #99-Ubuntu
May 27 00:39:48 antonio-vm kernel: [ 3484.993933] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/02/2015
May 27 00:39:48 antonio-vm kernel: [ 3484.993935] task: ffff88006e5ac600 ti: ffff880035984000 task.ti: ffff880035984000
May 27 00:39:48 antonio-vm kernel: [ 3484.993936] RIP: 0010:[] [] strnlen+0x9/0x40
May 27 00:39:48 antonio-vm kernel: [ 3484.993939] RSP: 0018:ffff880035987d90 EFLAGS: 00010286
May 27 00:39:48 antonio-vm kernel: [ 3484.993941] RAX: ffffffff81cce928 RBX: ffffffffc03ca4a0 RCX: 0000000000000000
May 27 00:39:48 antonio-vm kernel: [ 3484.993942] RDX: 0000000001347008 RSI: ffffffffffffffff RDI: 0000000001347008
May 27 00:39:48 antonio-vm kernel: [ 3484.993943] RBP: ffff880035987d90 R08: 000000000000ffff R09: 000000000000ffff
May 27 00:39:48 antonio-vm kernel: [ 3484.993944] R10: 0000000000000004 R11: 0000000000000246 R12: 0000000001347008
May 27 00:39:48 antonio-vm kernel: [ 3484.993945] R13: ffffffffffffffff R14: 00000000ffffffff R15: 0000000000000000
May 27 00:39:48 antonio-vm kernel: [ 3484.993946] FS: 00007f8c42b33700(0000) GS:ffff880114ac0000(0000) knlGS:0000000000000000
May 27 00:39:48 antonio-vm kernel: [ 3484.993948] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
May 27 00:39:48 antonio-vm kernel: [ 3484.993949] CR2: 0000000001347008 CR3: 000000002cca6000 CR4: 00000000003406e0
May 27 00:39:48 antonio-vm kernel: [ 3484.993955] Stack:
May 27 00:39:48 antonio-vm kernel: [ 3484.993956] ffff880035987dc8 ffffffff8140331b ffffffffc03ca4a0 ffffffffffffffff
May 27 00:39:48 antonio-vm kernel: [ 3484.993958] ffff880035987e48 ffffffffc03c9284 ffffffffc03c9284 ffff880035987e38
May 27 00:39:48 antonio-vm kernel: [ 3484.993960] ffffffff81404dbf ffff880021e89080 00000007d1a7110e ffff880030e7f021
May 27 00:39:48 antonio-vm kernel: [ 3484.993962] Call Trace:
May 27 00:39:48 antonio-vm kernel: [ 3484.993966] [] string.isra.4+0x3b/0xd0
May 27 00:39:48 antonio-vm kernel: [ 3484.993969] [] vsnprintf+0x12f/0x530
May 27 00:39:48 antonio-vm kernel: [ 3484.993972] [] sprintf+0x56/0x70
May 27 00:39:48 antonio-vm kernel: [ 3484.993975] [] ? apparmor_file_permission+0x1a/0x20
May 27 00:39:48 antonio-vm kernel: [ 3484.993979] [] dev_write+0x26/0x90 [ebbchar]
May 27 00:39:48 antonio-vm kernel: [ 3484.993982] [] __vfs_write+0x18/0x40
May 27 00:39:48 antonio-vm kernel: [ 3484.993984] [] vfs_write+0xa9/0x1a0
May 27 00:39:48 antonio-vm kernel: [ 3484.993986] [] ? filp_close+0x56/0x70
May 27 00:39:48 antonio-vm kernel: [ 3484.993988] [] SyS_write+0x55/0xc0
May 27 00:39:48 antonio-vm kernel: [ 3484.993992] [] entry_SYSCALL_64_fastpath+0x16/0x71
May 27 00:39:48 antonio-vm kernel: [ 3484.993993] Code: 00 00 80 3f 00 55 48 89 e5 74 11 48 89 f8 48 83 c0 01 80 38 00 75 f7 48 29 f8 5d c3 31 c0 5d c3 66 90 55 48 85 f6 48 89 e5 74 2e 3f 00 74 29 48 8d 57 01 48 8d 04 37 eb 0d 48 8d 4a 01 80 79
May 27 00:39:48 antonio-vm kernel: [ 3484.994015] RIP [] strnlen+0x9/0x40
May 27 00:39:48 antonio-vm kernel: [ 3484.994018] RSP
May 27 00:39:48 antonio-vm kernel: [ 3484.994018] CR2: 0000000001347008
May 27 00:39:48 antonio-vm kernel: [ 3484.994021] —[ end trace ed4f2b033431d87e ]—
I got the same problem with Antonio Petricca. It seems the write to the kernel got something and the reading is actually not executed.
I saw the line in the kern.log:
BUG: unable to handle kernel paging request at 0000000001347008
In the user space, the write seems not finished, and the read back never happens.
My kernel: 4.4.0-41-generic #61-Ubuntu SMP Tue Sep 27 17:27:48 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
Hello Dr. Molloy –
I’m having (essentially) the same trouble that DKazakov mentioned above. I asked about it on the beagleboard forum, and it was suggested I contact you to see whether something may have changed in 4.4.48 that may cause your example to crash.
I notice you haven’t posted here in a while; hopefully you’re still monitoring the incoming comments. Thanks for this great body of work.
(I thought I posted yesterday, but it didn’t appear, so this is possibly a repeat)
I’m having a problem similar to what DKazakov posted above. I’ve been able to trace the issue to an illegal address in the buffer argument passed to dev_write, which causes sprintf to fail.
I added a couple of printk statements, and the address of buffer is indeed well off the stack:
buffer = bef55600
address of len (for comparison) = dc14befc
I’m wondering whether I’m seeing some kind of version mismatch between OS versions or something. Any ideas?
Thanks for the great tutorials and examples.
Hi Derek,
i am trying to use ./test application after loading the ebbchar driver on BBB board but it is th roughing segmentation fault.
Could you please help me.
Log:
===
root@beaglebone:/home/exploringBB/extras/kernel/ebbchar# ./test
Starting device test code example…
Type in a short string to send to the kernel module:
abc
Writing message to the device [abc].
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12093.783394] Internal error: : 1b [#3] SMP ARM
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12093.901217] Process test (pid: 11374, stack limit = 0xdb40c218)
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12093.907166] Stack: (0xdb40de58 to 0xdb40e000)
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12093.911549] de40: db40de84 db40de68
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12093.919774] de60: c05c5b70 c05c4560 bf1e0784 bf1e02d0 ffffffff bf1e02d2 db40dee4 db40de90
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12093.927997] de80: c05c857c c05c5b44 c05c8474 ff0a0004 ffffffff c008aca8 db40ded4 bf1e0784
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12093.936223] dea0: c0f97a88 40e1f87b 00000004 ff0a0004 ffffffff dc8ba636 da92cc00 c0f97a88
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12093.944447] dec0: 00000003 bf1e0784 db40df70 beb834d0 db40c000 00000000 db40df00 db40dee8
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12093.952672] dee0: c05c93b8 c05c8400 db40df08 dc8ba636 bf1e0884 db40df2c db40df10 bf1e00a8
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12093.960896] df00: c05c938c bf1e02d0 beb834d0 00000003 db40df70 db11b000 00000003 beb834d0
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12093.969121] df20: db40df3c db40df30 c01c3098 bf1e0070 db40df6c db40df40 c01c396c c01c307c
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12093.977345] df40: 00000000 c01e3068 db11b000 c0f97a88 db11b000 00000003 beb834d0 db40c000
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12093.985570] df60: db40dfa4 db40df70 c01c4374 c01c38d8 00000000 00000000 00000022 dc8ba636
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12093.993794] df80: 00000000 00000000 40169000 000000d8 00000004 c0011024 00000000 db40dfa8
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12094.002018] dfa0: c0010e60 c01c431c 00000000 40169000 00000003 beb834d0 00000003 beb834d0
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12094.010242] dfc0: 00000000 40169000 000000d8 00000004 00000000 00000000 40067000 00000000
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12094.018466] dfe0: 00000000 beb834cc 00010645 40107f26 40000030 00000003 00000000 00000000
Message from syslogd@beaglebone at Jul 15 13:40:03 …
kernel:[12094.085452] Code: e92dd800 e24cb004 e3510000 0a000010 (e5d03000)
Segmentation fault
Thanks,
Chandan Jha
Great. Thank you Mr. Molloy !
I decided to learn how to make my own kernel-modules and stumbled upon this site here, only to be met with segfault when running the code as it’s presented here; the issue is the use of sprintf(). I fixed that and basically ended up re-writing for the dev_read() and dev_write functions. The code could still be made better, but since this is just an experiment I decided not to spend much more time on it. Anyways, here’s the code, hope it’s of help to others:
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset){
//Always return size_of_message or len bytes, whichever is less
size_t bytesToCopy = len >= size_of_message ? size_of_message: len;
size_t bytesNotCopied = 0;
//If size_of_message == 0, do nothing and return zero.
if(!bytesToCopy) return 0;
/* copy_from_user returns 0 if successful,
else it returns the number of bytes *NOT* copied */
bytesNotCopied = copy_to_user(buffer, message, bytesToCopy);
if(bytesToCopy – bytesNotCopied)
printk(KERN_INFO “EBBChar: Sent %d characters to the user\n”,
bytesToCopy – bytesNotCopied);
if(bytesNotCopied){
printk(KERN_INFO “EBBChar: Failed to send %d characters to\
the user\n”, bytesNotCopied);
return -EFAULT;
}
size_of_message = 0;
return bytesToCopy;
}
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
/* 14 is the max length of the output of sprintf() below,
and we take one more byte out of maxLen to leave room
for the terminating NULL-character. */
const size_t maxLen = 256 – 14 – 1;
size_t bytesToCopy = len >= maxLen ? maxLen: len; //If len is 255 or more, copy only 255 bytes
size_t bytesNotCopied = 0;
/* copy_from_user returns 0 if successful,
else it returns the number of bytes *NOT* copied */
bytesNotCopied = copy_from_user(message, buffer, bytesToCopy);
sprintf(message + bytesToCopy – bytesNotCopied, ” (%zu letters)”, bytesToCopy – bytesNotCopied);
size_of_message = bytesToCopy – bytesNotCopied + 1; //Include the NULL-character
printk(KERN_INFO “EBBChar: Received %zu characters from the user\n”, bytesToCopy – bytesNotCopied);
if(bytesNotCopied){
printk(KERN_INFO “EBBChar: Failed to receive %zu characters, -EFAULT!\n”, bytesNotCopied);
return -EFAULT;
}
return bytesToCopy;
}
The crash is due to trying to directly access user space memory in the dev_write() function. One must copy the data to kernel space before access. Use copy_from_user() in order to do that.
Hii derek molloy,
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
sprintf(message, “%s(%zu letters)”, buffer, len); // appending received string with its length
if user is write some data into kernal space by using dev_write function ,which buffer store the data in kernal space?
its message or *buffer?
please tell me my question is which buffer store the data in kernal space when user is write in to kernal space?
regards
Akhtar shaikh
Hi Derek, very informative post, thank you for this. I have one question: can I communicate driver A with driver B through /dev /my_device or I should use FIFO?
Can you throw some light using IOCTL Calls, Interrupts top half, bottom half concepts, Tasklets, Device Trees, Overlays, Device Probe function with some real Peripheral or for specific hardware with say Rpi3 or BBB ?
Here’s my Blog on Kernel Character device driver for GPIO Interrupts in Rpi…
http://blogsmayan.blogspot.in/p/programming-interrupts-in-raspberry-pi.html
Thanks Molloy,
This instruction is very helpful to me, and code works well.
Here it is a working dev_write() function (you should add #include for kmalloc/kfree function declaration)
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
int error_count = 0;
char* kbuffer;
kbuffer=kmalloc(len, GFP_KERNEL);
error_count = copy_from_user(kbuffer,buffer,len);
sprintf(message, “%s(%zu letters)”, kbuffer, len); // appending received string with its length
size_of_message = strlen(message); // store the length of the stored message
printk(KERN_INFO “EBBChar: Received %zu characters from the user\n”, len);
kfree(kbuffer);
return len;
}
Hey Derek, excellent links! thanks a lot for this.
One point – I couldn’t help but notice that you have used mutex and semaphores interchangeably, aren’t they two different things? Also, in this context, shouldn’t we use mutexes?
Derek,
In the ‘Major and Minor Numbers’ section I believe there may be a typo. “However, a device that idenfies a “unique” major number using this…” “Idenfies” should be “identifies.” Love the article!
Thanks you so much fThanks you so much for your articles. They are very useful for myself. There are some points I already knew before reading but u give me a better way to understand and remember them.
Best regards.
Hi fellas,
I’ve build,loaded and unloaded the device file. Yet the device file is available at /dev file? What does it means?
unoaded with : sudo rmmod
Not running in /proc/modules
yet visible in ls -l /dev/cha*
Dear Dr. Molloy,
Thanks for your great articles. Like many others, I ran into segment faults. The reason is that main() does not send the NULL character, but dev_write() expects that ‘buffer’ contains one. The intermediary buffer therefore may or may not contain a NULL at the end (most likely it will not). To fix the problem, I tried 2 solutions and they both worked well:
1) In the write call, add 1 to strlen(stringToSend);
2) Or in dev_write, replace sprintf(message, “%s(%zu letters)”, buffer, len);
with
copy_from_user(message, buffer, len);
sprintf(message+len, “(%zu letters)”, len); // appending received string with its length
Again, thanks for your great articles.
Hi Derek! Suppose I created in my driver – character device file,
and realize function for read:
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
Can I get struct device *dev; from struct file * ?
Another really excellent tutorial. I think the way you have written the article helps make a complicate subject much more accessible. I’m really enjoying working through the tutorials. Thanks Derek!!
one of the best tutorial ever seen. Starting from creating device driver and sample program from the user mode for test; helps the learning end-to-end without any confusions. If you already written books around device driver; please let me know. i would definitely buy and keep the copy. Thank you.
Hi,
I have a question.
Lets say I have kernel module. From an user space application I run the command;
echo 1 0 1 AABBCCDD > /dev/X_Driver
And when the X_Driver receives the command it does some process. Is it possible someone to read what command or data is sent to the driver?
If the driver echoes something back and it can be readable by a command cat /dev/X_Driver, can some see what is echoed before my application sees it?
If so, what is the precaution?
Thanks and have a nice day..
The changes below eliminate x64 run time problems.
diff –git a/extras/kernel/ebbchar/ebbchar.c b/extras/kernel/ebbchar/ebbchar.c
index 771b859..0f6e256 100644
— a/extras/kernel/ebbchar/ebbchar.c
+++ b/extras/kernel/ebbchar/ebbchar.c
@@ -142,10 +142,29 @@ static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *of
* @param offset The offset if required
*/
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
– sprintf(message, “%s(%zu letters)”, buffer, len); // appending received string with its length
– size_of_message = strlen(message); // store the length of the stored message
– printk(KERN_INFO “EBBChar: Received %zu characters from the user\n”, len);
– return len;
+ ssize_t res;
+ // Make sure copy length plus appended string don’t overflow message.
+ const unsigned long cpy_len = min(len, sizeof(message)-16);
+ // Copy input from user space.
+ unsigned long copy_result = copy_from_user(message, buffer, cpy_len);
+ // If copy from user space worked then
+ if ( cpy_len >= copy_result )
+ {
+ // Calculate number of copied bytes.
+ res = cpy_len – copy_result;
+ // Make sure message is null terminated.
+ message[res] = 0;
+ // This only matters if a null was included in the input.
+ size_of_message = strlen(message);
+ sprintf(&message[size_of_message], “(%zu letters)”, res); // appending received string with its length
+ size_of_message = strlen(message); // store the length of the stored message
+ printk(KERN_INFO “EBBChar: Received %zu characters from the user\n”, res);
+ }
+ else
+ {
+ res = 0;
+ }
+ return res;
}
/** @brief The device release function that is called whenever the device is closed/released by
—
2.7.4
Thank for sharing. This is a great example to help everybody understand the character device.
Dear Derek,
When I build your program about character device, and I used Android NDK and kernel source of Android to build character device on target board. After that, I executed main program to test character deivce. But I meet a error about memory below:
Writing message[ 1591.113903] Internal error: Accessing user space memory outside uaccess.h routines: 96000004 [#1] PREEMPT SMP
Could you help me to give solution for it.?
Thanks you and best regards.
Hello Derek,
Excellent article. I have seen and read many device driver books; none of them has such simple and clarity of instructions. Please let me know if you have any books return on the device driver; I would like to buy.
Thank you,
Vish.
Hello Derek,
I read through your code and couldn’t figure out the difference between the char device that was registered and the buffer message[250]. Why did you register a char device if you are just reading and writing to a buffer named message?
Thanks
I have perused your blog it is exceptionally useful for me. I need to express profound gratitude to you. I have bookmark your site for future updates.
company registration with cac
Hello,
I am getting this kernel Error log:
ebbchar: loading out-of-tree module taints kernel.
Apr 6 11:45:05 beaglebone kernel: [ 4694.281213] EBBChar: Initializing the EBBChar LKM
Apr 6 11:45:05 beaglebone kernel: [ 4694.281253] EBBChar: registered correctly with major number 241
Apr 6 11:45:05 beaglebone kernel: [ 4694.281377] EBBChar: device class registered correctly
Apr 6 11:45:05 beaglebone kernel: [ 4694.281785] EBBChar: device class created correctly
Apr 6 11:46:22 beaglebone kernel: [ 4771.214704] EBBChar: Device has been opened 1 time(s)
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.355122] Internal error: : 1b [#1] PREEMPT SMP ARM
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.471088] Process test (pid: 2083, stack limit = 0xdc56a218)
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.476949] Stack: (0xdc56be58 to 0xdc56c000)
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.481329] be40: dacd058c 00000001
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.489551] be60: 00000004 bf419884 dc56bed4 dc56be80 c075312c c074fe5c c0753018 ffffff04
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.497772] be80: ffff0a00 dc56be90 c0d407c8 bf419884 c140414c 40be677b c018beb0 ffffff04
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.505993] bea0: ffff0a00 00040952 013c602f c140414c 00000005 bf419884 dc56bf68 00000000
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.514214] bec0: bed4b4a0 00000005 dc56bef0 dc56bed8 c07539d8 c0752fa0 dc56bef8 00040952
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.522435] bee0: bf419880 dc56bf1c dc56bf00 bf4190a0 c07539ac bf419374 bed4b4a0 00000005
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.530655] bf00: bf419064 dabbb840 bed4b4a0 dc56bf68 dc56bf34 dc56bf20 c02def94 bf419070
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.538875] bf20: 00000005 dabbb840 dc56bf64 dc56bf38 c02dfe44 c02def78 00000000 00000004
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.547096] bf40: c140414c dabbb840 00000000 00000000 dabbb840 bed4b4a0 dc56bfa4 dc56bf68
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.555317] bf60: c02e1060 c02dfd9c 00000000 00000000 5ca8429f 00040952 00000400 bed4b5c8
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.563536] bf80: 00000000 00000000 00000004 c0109144 dc56a000 00000000 00000000 dc56bfa8
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.571757] bfa0: c0108f80 c02e1010 bed4b5c8 00000000 00000003 bed4b4a0 00000005 bed4b4a0
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.579977] bfc0: bed4b5c8 00000000 00000000 00000004 00000000 00000000 004a6000 00000000
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.588198] bfe0: 00000000 bed4b49c 00495805 b6ec6cf6 00070030 00000003 00000000 00000000
Message from syslogd@beaglebone at Apr 6 11:46:28 …
kernel:[ 4776.648400] Code: 3a000019 e3530000 e243e001 0a00001b (e5d2c000)
Apr 6 11:46:28 beaglebone kernel: [ 4776.339642] Unhandled fault: page domain fault (0x01b) at 0xbed4b4a0
Apr 6 11:46:28 beaglebone kernel: [ 4776.346058] pgd = da1f8000
Apr 6 11:46:28 beaglebone kernel: [ 4776.348780] [bed4b4a0] *pgd=9a1bb831, *pte=8ef4c34f, *ppte=8ef4c83f
Apr 6 11:46:28 beaglebone kernel: [ 4776.355122] Internal error: : 1b [#1] PREEMPT SMP ARM
Apr 6 11:46:28 beaglebone kernel: [ 4776.360202] Modules linked in: ebbchar(O) xt_conntrack ipt_MASQUERADE nf_nat_masquerade_ipv4 ccm arc4 wl18xx cmac wlcore bnep mac80211 hci_uart btbcm btqca bluetooth cfg80211 rfkill pruss_soc_bus wlcore_sdio evdev uio_pdrv_genirq uio usb_f_mass_storage usb_f_acm u_serial usb_f_ecm usb_f_rndis u_ether libcomposite iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack iptable_mangle iptable_filter spidev pru_rproc pruss_intc pruss tieqep ip_tables x_tables
Apr 6 11:46:28 beaglebone kernel: [ 4776.403018] CPU: 0 PID: 2083 Comm: test Tainted: G O 4.9.82-ti-r102 #1
Apr 6 11:46:28 beaglebone kernel: [ 4776.410710] Hardware name: Generic AM33XX (Flattened Device Tree)
Apr 6 11:46:28 beaglebone kernel: [ 4776.416833] task: da1ad700 task.stack: dc56a000
Apr 6 11:46:28 beaglebone kernel: [ 4776.421403] PC is at string+0x34/0xb8
Apr 6 11:46:28 beaglebone kernel: [ 4776.425086] LR is at 0xfffffffe
Apr 6 11:46:28 beaglebone kernel: [ 4776.428246] pc : [] lr : [] psr: a0070013
Apr 6 11:46:28 beaglebone kernel: [ 4776.428246] sp : dc56be58 ip : dc56be80 fp : dc56be74
Apr 6 11:46:28 beaglebone kernel: [ 4776.439779] r10: dc56be9c r9 : bf419376 r8 : dc56befc
Apr 6 11:46:28 beaglebone kernel: [ 4776.445028] r7 : 00000002 r6 : ffffffff r5 : bf419374 r4 : ffffffff
Apr 6 11:46:28 beaglebone kernel: [ 4776.451586] r3 : ffffffff r2 : bed4b4a0 r1 : ffffffff r0 : bf419884
Apr 6 11:46:28 beaglebone kernel: [ 4776.458146] Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none
Apr 6 11:46:28 beaglebone kernel: [ 4776.465315] Control: 10c5387d Table: 9a1f8019 DAC: 00000051
Apr 6 11:46:28 beaglebone kernel: [ 4776.471088] Process test (pid: 2083, stack limit = 0xdc56a218)
Apr 6 11:46:28 beaglebone kernel: [ 4776.476949] Stack: (0xdc56be58 to 0xdc56c000)
Apr 6 11:46:28 beaglebone kernel: [ 4776.481329] be40: dacd058c 00000001
Apr 6 11:46:28 beaglebone kernel: [ 4776.489551] be60: 00000004 bf419884 dc56bed4 dc56be80 c075312c c074fe5c c0753018 ffffff04
Apr 6 11:46:28 beaglebone kernel: [ 4776.497772] be80: ffff0a00 dc56be90 c0d407c8 bf419884 c140414c 40be677b c018beb0 ffffff04
Apr 6 11:46:28 beaglebone kernel: [ 4776.505993] bea0: ffff0a00 00040952 013c602f c140414c 00000005 bf419884 dc56bf68 00000000
Apr 6 11:46:28 beaglebone kernel: [ 4776.514214] bec0: bed4b4a0 00000005 dc56bef0 dc56bed8 c07539d8 c0752fa0 dc56bef8 00040952
Apr 6 11:46:28 beaglebone kernel: [ 4776.522435] bee0: bf419880 dc56bf1c dc56bf00 bf4190a0 c07539ac bf419374 bed4b4a0 00000005
Apr 6 11:46:28 beaglebone kernel: [ 4776.530655] bf00: bf419064 dabbb840 bed4b4a0 dc56bf68 dc56bf34 dc56bf20 c02def94 bf419070
Apr 6 11:46:28 beaglebone kernel: [ 4776.538875] bf20: 00000005 dabbb840 dc56bf64 dc56bf38 c02dfe44 c02def78 00000000 00000004
Apr 6 11:46:28 beaglebone kernel: [ 4776.547096] bf40: c140414c dabbb840 00000000 00000000 dabbb840 bed4b4a0 dc56bfa4 dc56bf68
Apr 6 11:46:28 beaglebone kernel: [ 4776.555317] bf60: c02e1060 c02dfd9c 00000000 00000000 5ca8429f 00040952 00000400 bed4b5c8
Apr 6 11:46:28 beaglebone kernel: [ 4776.563536] bf80: 00000000 00000000 00000004 c0109144 dc56a000 00000000 00000000 dc56bfa8
Apr 6 11:46:28 beaglebone kernel: [ 4776.571757] bfa0: c0108f80 c02e1010 bed4b5c8 00000000 00000003 bed4b4a0 00000005 bed4b4a0
Apr 6 11:46:28 beaglebone kernel: [ 4776.579977] bfc0: bed4b5c8 00000000 00000000 00000004 00000000 00000000 004a6000 00000000
Apr 6 11:46:28 beaglebone kernel: [ 4776.588198] bfe0: 00000000 bed4b49c 00495805 b6ec6cf6 00070030 00000003 00000000 00000000
Apr 6 11:46:28 beaglebone kernel: [ 4776.596436] [] (string) from [] (vsnprintf+0x198/0x404)
Apr 6 11:46:28 beaglebone kernel: [ 4776.603440] [] (vsnprintf) from [] (sprintf+0x3c/0x58)
Apr 6 11:46:28 beaglebone kernel: [ 4776.610368] [] (sprintf) from [] (dev_write+0x3c/0x64 [ebbchar])
Apr 6 11:46:28 beaglebone kernel: [ 4776.618198] [] (dev_write [ebbchar]) from [] (__vfs_write+0x28/0x48)
Apr 6 11:46:28 beaglebone kernel: [ 4776.626336] [] (__vfs_write) from [] (vfs_write+0xb4/0x1c0)
Apr 6 11:46:28 beaglebone kernel: [ 4776.633687] [] (vfs_write) from [] (SyS_write+0x5c/0xbc)
Apr 6 11:46:28 beaglebone kernel: [ 4776.640784] [] (SyS_write) from [] (ret_fast_syscall+0x0/0x48)
Apr 6 11:46:28 beaglebone kernel: [ 4776.648400] Code: 3a000019 e3530000 e243e001 0a00001b (e5d2c000)
Apr 6 11:46:28 beaglebone kernel: [ 4776.654542] —[ end trace 2006ad5b927bc779 ]—
Apr 6 11:46:28 beaglebone kernel: [ 4776.731993] EBBChar: Device successfully closed
Make Log:
make -C /lib/modules/4.9.82-ti-r102/build/ M=/home/debian/exploringBB/extras/kernel/ebbchar modules
make[1]: Entering directory ‘/usr/src/linux-headers-4.9.82-ti-r102’
CC [M] /home/debian/exploringBB/extras/kernel/ebbchar/ebbchar.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/debian/exploringBB/extras/kernel/ebbchar/ebbchar.mod.o
LD [M] /home/debian/exploringBB/extras/kernel/ebbchar/ebbchar.ko
make[1]: Leaving directory ‘/usr/src/linux-headers-4.9.82-ti-r102’
cc testebbchar.c -o test
Thank You
I’d always shied away from designing solutions involving an LKM, because I thought it was hard. Thank you for clearly, patiently proving me wrong 🙂 You have a gift for explaining things — always enough detail without ever getting lost in the weeds, and consistently addressing your intended audience — experienced non-kernel programmers. I also appreciate the more practical than theoretical approach — I know the theory of LKMs, but there’s nothing like well-documented working examples to get from theory to code.
Oh, and, while you obviously love the Beaglebone platform, you didn’t yield to the common temptation to include platform-specific code in these lessons.
in dev_write(), the use of sprintf causes a segmentation fault in the kernel. Substituting with copy_from_user() works better.
If you are struggling with it, here’s my working implementation of the function:
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){
int error_count = 0;
// copy_from_user has the format (* to, *from, size) and returns 0 on success
error_count = copy_from_user(message, buffer, len);
if (error_count==0){
msgsize = strlen(message); // store the length of the stored message
printk(KERN_INFO “SendChar: Received %d characters from the user\n”, len);
return len;
} else {
printk(KERN_INFO “SendChar: Failed to receive characters from the user\n”);
return -EFAULT;
}
}
Hi Derek,
This is the best guide to kernel modules on the internet. Seriously, you should put this behind a paywall, its that good (please don’t)