Lkm Driver Read, Write, Open, Release, Llseek and Github
Page Contents
- 1 Introduction
- 2 Character Device Drivers
- 2.1 Major and Minor Numbers
- two.2 The File Operations Data Structure
- 3 Source Code for this Discussion
- 4 The Device Driver Source Code
- 5 Building and Testing the LKM
- half-dozen User Access to the Device using Udev Rules
- seven The strace Command
- 8 LKM Synchronization Bug
- 9 Adding Mutex Locks
- x Determination
Introduction
In this series of articles I depict how you tin can write a Linux loadable kernel module (LKM) for an embedded Linux device. This is the 2d article in the series — please read "Writing a Linux Kernel Module — Part 1: Introduction" before moving on to this article, as information technology explains how to build, load and unload loadable kernel modules (LKMs). Such description is non repeated in this article.
Character Device Drivers
A character device typically transfers data to and from a user application — they behave like pipes or serial ports, instantly reading or writing the byte data in a graphic symbol-past-character stream. They provide the framework for many typical drivers, such equally those that are required for interfacing to serial communications, video capture, and sound devices. The main alternative to a character device is a block device. Cake devices behave in a similar fashion to regular files, allowing a buffered assortment of cached data to be viewed or manipulated with operations such as reads, writes, and seeks. Both device types can be accessed through device files that are attached to the file system tree. For case, the program lawmaking that is presented in this article builds to become a device /dev/ebbchar, which appears on your Linux system every bit follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ lsmod
Module Size Used past
ebbchar 2754 0
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ls -l /dev/ebb*
crw-rw-rwT 1 root root 240, 0 Apr eleven 15:34 /dev/ebbchar
This article describes a straightforward character commuter that tin can be used to pass data betwixt a Linux user-space program and a loadable kernel module (LKM), which is running in Linux kernel space. In this example, a C user-infinite awarding sends a string to the LKM. The LKM then responds with the message that was sent forth with the number of letters that the sent message contains. Later in the article I describe why we need to solve synchronization bug that arise with this arroyo, and I provide a version of the program that uses mutex locks to provide a solution.
Before describing the source code for the driver in this article, there are a few concepts that demand to be discussed, such equally device driver major and minor numbers, and the File Operations data construction.
Major and Minor Numbers
Device drivers take an associated major and pocket-sized number. For example, /dev/ram0 and /dev/null are associated with a driver with major number 1, and /dev/tty0 and /dev/ttyS0 are associated with a driver with major number 4. The major number is used by the kernel to identify the correct device commuter when the device is accessed. The function of the pocket-size number is device dependent, and is handled internally inside the driver. You tin see the major/pocket-size number pair for each device if you perform a listing in the /dev directory. For example:
molloyd@beaglebone:/dev$ ls -l
crw-rw---T 1 root i2c 89, 0 January one 2000 i2c-0
brw-rw---T ane root disk 1, 0 Mar ane 20:46 ram0
brw-rw---T one root floppy 179, 0 Mar 1 20:46 mmcblk0
crw-rw-rw- 1 root root 1, 3 Mar 1 twenty:46 naught
crw------- i root root 4, 0 Mar 1 20:46 tty0
crw-rw---T 1 root dialout 4, 64 Mar 1 20:46 ttyS0
…
Character devices are identified by a 'c' in the starting time column of a listing, and cake devices are identified by a 'b'. The access permissions, owner, and group of the device is provided for each device. Regular user accounts on the BeagleBone are members of some of these groups and therefore have permissions to utilize the i2c-0 and ttyS0 devices etc. Come across:
molloyd@beaglebone:/dev$ groups
molloyd dialout cdrom floppy audio video plugdev users i2c spi
The device that is developed in this commodity appears equally a device (/dev/ebbchar) in the /dev directory.
Information technology is possible to manually create a block or grapheme device file entry and later acquaintance information technology with your device (e.g., sudo mknod /dev/exam c 92 1
), but this approach is prone to problems. One such trouble is that y'all have to ensure that the number you choose (e.g., 92 in this example) is not already in utilise. On the BeagleBone, y'all could examine the file /usr/src/linux-headers-three.viii.13-bone70/include/uapi/linux/major.h for a list of all organization device major numbers. All the same, a device that idenfies a "unique" major number using this approach would not exist very portable, every bit the major number of the device could clash with that of another device on another Linux SBC or Linux distribution. The lawmaking that is provided in this commodity automatically identifies an appropriate major number to utilize.
The File Operations Information Structure
The file_operations
data construction that is defined in /linux/fs.h holds pointers to functions (role pointers) inside a commuter that allows you to ascertain the behavior of certain file operations. For case, Listing i is a segment of the information structure from /linux/fs.h. The driver in this commodity provides an implementation for the read
, write
, open up
, and release
system phone call file operations. If y'all do not provide an implementation for ane of the entries in this data structure and so it will merely betoken to Aught
, making it inaccessible. Listing 1 is somewhat intimidating, given the number of operations available. However, to build the ebbchar LKM we just need to provide an implementation for 4 of the entries. Therefore, Listing one is provided mainly equally a reference that you can use if yous need to provide additional functionality inside the driver framework.
file_operations
Data Structure of the /linux/fs.h (Segment) 1 2 3 4 five 6 vii 8 9 10 11 12 xiii 14 15 sixteen 17 18 xix 20 21 22 23 24 25 | // Note: __user refers to a user-space address. struct file_operations { struct module * possessor ; // Pointer to the LKM that owns the construction loff_t ( * llseek ) ( struct file * , loff_t , int ) ; // Change electric current read/write position in a file ssize_t ( * read ) ( struct file * , char __user * , size_t , loff_t * ) ; // Used to recollect information 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 * ) ; // perhaps 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 ) ; // Chosen by the ioctl system call long ( * compat_ioctl ) ( struct file * , unsigned int , unsigned long ) ; // Chosen past the ioctl organisation phone call int ( * mmap ) ( struct file * , struct vm_area_struct * ) ; // Called by mmap system call int ( * mremap ) ( struct file * , struct vm_area_struct * ) ; // Chosen past retentiveness remap system call int ( * open up ) ( struct inode * , struct file * ) ; // first operation performed on a device file int ( * affluent ) ( struct file * , fl_owner_t id ) ; // chosen when a process closes its copy of the descriptor int ( * release ) ( struct inode * , struct file * ) ; // chosen when a file construction is existence 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 data, in that location is an splendid guide on the File Operations data structure at: Kernel.org Virtual File Systems
Get Source Code
Source Code for this Discussion
Become Source Lawmaking
All of the code for this discussion is available in the GitHub repository for the book Exploring BeagleBone. The code tin be viewed publicly at: the ExploringBB GitHub Kernel Project directory, and/or you tin clone the repository on your BeagleBone (or other Linux device) by typing:
molloyd@beaglebone:~$ sudo apt-get install git molloyd@beaglebone:~$ git clone https://github.com/derekmolloy/exploringBB.git |
The /extras/kernel/ebbchar directory is the almost important resources for the adjacent function of this article. The automobile-generated Doxygen documentation for these lawmaking examples is available in HTML format and PDF format.
The Device Driver Source Lawmaking
The source code for the ebbchar device commuter is provided in List two. Like to the code in the first article in this serial, there is an init()
function and an exit()
function. However, there are additional file_operations
functions that are required for the graphic symbol device:
-
dev_open()
: Called each time the device is opened from user space. -
dev_read()
: Called when information is sent from the device to user space. -
dev_write()
: Chosen when data is sent from user infinite to the device. -
dev_release()
: Called when the device is airtight in user space.
Drivers have a class proper name and a device name. In Listing ii, ebb (Exploring BeagleBone) is used equally the course name, and ebbchar as the device proper noun. This results in the cosmos of a device that appears on the file system at /sys/course/ebb/ebbchar.
i 2 3 4 v half dozen 7 8 9 10 11 12 xiii 14 15 16 17 xviii nineteen xx 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 xl 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.ane * @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 exist 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 organisation support #include <linux/uaccess.h> // Required for the copy to user function #ascertain DEVICE_NAME "ebbchar" ///< The device will appear at /dev/ebbchar using this value #define CLASS_NAME "ebb" ///< The device course -- 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 employ modinfo MODULE_DESCRIPTION ( "A unproblematic 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 } ; ///< Retentivity for the cord that is passed from userspace static brusk 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 = Zippo ; ///< The device-driver class struct pointer static struct device * ebbcharDevice = Nil ; ///< The device-driver device struct arrow // 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 construction in the kernel. The file_operations structure from * /linux/fs.h lists the callback functions that you lot wish to associated with your file operations * using a C99 syntax structure. char devices unremarkably implement open, read, write and release calls */ static struct file_operations fops = { . open = dev_open , . read = dev_read , . write = dev_write , . release = dev_release , } ; /** @cursory The LKM initialization role * The static keyword restricts the visibility of the function to within this C file. The __init * macro means that for a congenital-in driver (not a LKM) the function is only used at initialization * time and that it can be discarded and its retentiveness freed up afterward that signal. * @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 than hard merely worth it majorNumber = register_chrdev ( 0 , DEVICE_NAME , & fops ) ; if ( majorNumber < 0 ) { printk ( KERN _Alarm "EBBChar failed to annals a major number\n" ) ; return majorNumber ; } printk ( KERN _INFO "EBBChar: registered correctly with major number %d\due north" , majorNumber ) ; // Annals the device class ebbcharClass = class_create ( THIS_MODULE , CLASS_NAME ) ; if ( IS_ERR ( ebbcharClass ) ) { // Bank check for fault and clean up if at that place is unregister_chrdev ( majorNumber , DEVICE_NAME ) ; printk ( KERN _ALERT "Failed to register device class\n" ) ; return PTR_ERR ( ebbcharClass ) ; // Correct way to render an error on a arrow } printk ( KERN _INFO "EBBChar: device class registered correctly\due north" ) ; // Annals the device driver ebbcharDevice = device_create ( ebbcharClass , NULL , MKDEV ( majorNumber , 0 ) , NULL , DEVICE_NAME ) ; if ( IS_ERR ( ebbcharDevice ) ) { // Clean upwardly if there is an error class_destroy ( ebbcharClass ) ; // Repeated lawmaking just the alternative is goto statements unregister_chrdev ( majorNumber , DEVICE_NAME ) ; printk ( KERN _ALERT "Failed to create the device\n" ) ; render 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 role, it is static. The __exit macro notifies that if this * code is used for a built-in driver (not a LKM) that this part is non 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 form unregister_chrdev ( majorNumber , DEVICE_NAME ) ; // unregister the major number printk ( KERN _INFO "EBBChar: Goodbye from the LKM!\north" ) ; } /** @brief The device open function that is called each time the device is opened * This will merely 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 fourth dimension(southward)\north" , numberOpens ) ; render 0 ; } /** @brief This function is chosen whenever device is being read from user infinite i.e. data is * existence sent from the device to the user. In this case is uses the copy_to_user() function to * transport 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 kickoff The kickoff 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\due north" , size_of_message ) ; return ( size_of_message = 0 ) ; // clear the position to the commencement and render 0 } else { printk ( KERN _INFO "EBBChar: Failed to send %d characters to the user\north" , error_count ) ; return - EFAULT ; // Failed -- return a bad address message (i.e. -14) } } /** @brief This part is called whenever the device is being written to from user space i.e. * data is sent to the device from the user. The information 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 assortment of data that is being passed in the const char buffer * @param beginning The get-go if required */ static ssize_t dev_write ( struct file * filep , const char * buffer , size_t len , loff_t * offset ) { sprintf ( bulletin , "%s(%zu letters)" , buffer , len ) ; // appending received string with its length size_of_message = strlen ( bulletin ) ; // shop the length of the stored message printk ( KERN _INFO "EBBChar: Received %zu characters from the user\northward" , len ) ; return len ; } /** @brief The device release function that is called whenever the device is airtight/released by * the userspace programme * @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 airtight\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 higher up) */ module_init ( ebbchar_init ) ; module_exit ( ebbchar_exit ) ; |
In addition to the points described by the comments in Listing 2, in that location are some additional points:
- This code has a fixed message size of 256 characters — this will exist improved in afterwards manufactures though the dynamic allotment of retentiveness.
- This code is non multi-procedure safe — that is addressed later in this commodity.
- The
ebbchar_init()
function is much longer than the last article. That is because it is now automatically allocating a major number to the device, registering the device class, and registering the device driver. Importantly, y'all will notice that if annihilation goes incorrect that the code carefully "backs out" of the successful operations. To attain this I have repeated code (which I always dislike), but the alternative is to 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 fault 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 List 3. This Makefile is very similar to the Makefile in the first commodity in the series, with the exception that it also builds a user-space C program that interacts with the LKM.
obj - 1000 += ebbchar . o all : make - C / lib / modules / $ ( shell uname - r ) / build / Yard = $ ( PWD ) modules $ ( CC ) testebbchar . c - o test clean : brand - C / lib / modules / $ ( shell uname - r ) / build / M = $ ( PWD ) make clean rm test |
Listing 4 is a brusque program that requests a string from the user, and writes it to the /dev/ebbchar device. After a subsequent fundamental press (ENTER) it then reads the response from the device and displays it in the final window.
1 two 3 4 five six 7 8 9 ten 11 12 thirteen xiv fifteen 16 17 18 19 xx 21 22 23 24 25 26 27 28 29 thirty 31 32 33 34 35 36 37 38 39 xl 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 infinite 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-upwardly 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 simply 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 lawmaking example...\northward" ) ; fd = open ( "/dev/ebbchar" , O_RDWR ) ; // Open the device with read/write access if ( fd < 0 ) { perror ( "Failed to open up the device..." ) ; render errno ; } printf ( "Blazon in a brusque string to ship to the kernel module:\n" ) ; scanf ( "%[^\north]%*c" , stringToSend ) ; // Read in a string (with spaces) printf ( "Writing message to the device [%south].\due north" , stringToSend ) ; ret = write ( fd , stringToSend , strlen ( stringToSend ) ) ; // Send the cord to the LKM if ( ret < 0 ) { perror ( "Failed to write the message to the device." ) ; render 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 programme\n" ) ; return 0 ; } |
In improver to the points described by the comments in Listing four, there are some additional points:
- The
%[^\n]%*c
uses the scanset specifiers, which are represented by%[]
to apply the^
character to terminate reading after the start occurrence of the\n
graphic symbol. In addition, the%*c
ignores the trailing grapheme, ensuring that the subsequentgetchar()
role works as required. Essentially, thescanf()
code just reads in a sentence. Ifscanf()
was used with a regular%s
telephone call then the cord would terminated at the kickoff occurrence of the space character. - The
getchar()
allows the plan to pause at that signal until the ENTER key is pressed. This is necessary to demonstrate a problem with the current lawmaking formulation. - The programme then reads the response from the LKM and displays it in the terminal window.
All going well, the process to build the kernel module should be straightforward, provided that you have installed the Linux headers as described in the first article. The steps are as follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ brand
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ls -l *.ko
-rw-r--r-- i molloyd molloyd 7075 April 8 19:04 ebbchar.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ls -l test
-rwxr-xr-x one molloyd molloyd 6342 April eight 19:23 test
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo insmod ebbchar.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ lsmod
Module Size Used past
ebbchar 2521 0
The device is now present in the /dev directory, with the post-obit attributes:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ cd /dev
molloyd@beaglebone:/dev$ ls -l ebb*
crw------- 1 root root 240, 0 Apr 8 xix:28 ebbchar
You tin can come across that the major number is 240 — this is automatically assigned by the code in Listing two.
We can and so use the testebbchar programme (List 4) to test that the LKM is working correctly. For the moment, the test program must be executed with root privileges — that outcome is addressed shortly.
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo insmod ebbchar.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo ./test
Starting device examination lawmaking case...
Blazon in a brusk cord to transport to the kernel module:
This is a examination of the ebbchar LKM
Writing message to the device [
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 tin can exist viewed by examining the kernel logs as follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo tail -f /var/log/kern.log
Apr 11 22:24:50 beaglebone kernel: [358664.365942] EBBChar: Initializing the EBBChar LKM
Apr xi 22:24:50 beaglebone kernel: [358664.365980] EBBChar: registered correctly with major number 240
April 11 22:24:50 beaglebone kernel: [358664.366061] EBBChar: device class registered correctly
Apr xi 22:24:50 beaglebone kernel: [358664.368383] EBBChar: device course created correctly
Apr 11 22:25:15 beaglebone kernel: [358689.812483] EBBChar: Device has been opened 1 time(s)
Apr eleven 22:25:31 beaglebone kernel: [358705.451551] EBBChar: Received 33 characters from the user
Apr xi 22:25:32 beaglebone kernel: [358706.403818] EBBChar: Sent 45 characters to the user
Apr 11 22:25:32 beaglebone kernel: [358706.404207] EBBChar: Device successfully closed
Apr 11 22:25:44 beaglebone kernel: [358718.497000] EBBChar: Adieu from the LKM!
You can see that 33 characters are sent to the LKM only 45 characters are returned — this is due to the addition of the 12 characters "(33 letters)
" to the cord data that specifies the length of the string that was originally sent. This add-on is performed as a examination in social club to be certain that the code is sending and receiving unique data.
There are two significant problems with the current LKM. The first is that the LKM device can only be accessed with superuser permissions, and the second is that the current LKM is not multi-process safe.
User Access to the Device using Udev Rules
Throughout this commodity, the plan that interfaces to the LKM device is executed using sudo. It would be very useful to set up our LKM device and so that it can be accessed by a particular user or grouping, while still protecting the file system. To address this upshot, you lot can employ an advanced feature of Linux called udev rules that enables you lot to customize the beliefs of the udevd service. This service gives you some user-infinite control over devices on your Linux system.
For instance, to give user-level access to the ebbchar device, the commencement step is to place the sysfs entry for the device. You lot tin can accomplish this past using a simple find:
root@beaglebone:/sys# find . -name "ebbchar"
./devices/virtual/ebb/ebbchar
./grade/ebb/ebbchar
./module/ebbchar
Nosotros then demand to identify the KERNEL and SUBSYSTEM values about which to write the rule. Y'all can use the udevadm command to perform this task:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ udevadm info -a -p /sys/class/ebb/ebbchar
Udevadm info starts with the device specified past the devpath and then walks upward the concatenation of parent
devices. It prints for every device found, all possible attributes in the udev rules cardinal format. A
rule to friction match, can be composed by the attributes of the device and the attributes from one single
parent device.
looking at device '/devices/virtual/ebb/ebbchar':
KERNEL=="
SUBSYSTEM=="
DRIVER==""
The rules are independent in the /etc/udev/rules.d directory. A new rule tin can be added every bit a file using these values, where the file begins with a priority number. Using a name such every bit 99-ebbchar.rules creates a new rule with the lowest priority, so that it does non interfere with other device rules. The rule tin be written as in Listing 5 and placed in the /etc/udev/rules.d directory as follows:
molloyd@beaglebone:/etc/udev/rules.d$ ls
50-hidraw.rules l-spi.rules 60-omap-tty.rules 70-persistent-net.rules
molloyd@beaglebone:/etc/udev/rules.d$ more than 99-ebbchar.rules
#Rules file for the ebbchar device driver
KERNEL=="ebbchar", SUBSYSTEM=="ebb", MODE="0666"
#Rules file for the ebbchar device driver KERNEL=="ebbchar", SUBSYSTEM=="ebb", Way="0666" |
In one case the rules file is added to your arrangement, yous can test it using:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo insmod ebbchar.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ls -l /dev/ebbchar
crw-rw-rwT 1 root root 240, 0 Apr 9 00:44 /dev/ebbchar
You can come across that user and group at present have the permissions required to read from and write to this device. Interestingly, the sticky scrap is also set. The sticky bit usually means that write permission is not sufficient to delete files. Therefore, in the /tmp directory whatever user can create files, but no user can delete another user's files. The mucilaginous chip is represented by a capital T in the terminal grapheme place. This ordinarily appears as a lower-case t unless the executable (x) bit for others is prepare; even so, when the 10 scrap is non fix it appears as a capital T. However, it is non entirely clear why the gummy bit is beingness set by udev — information technology appears to be unusual to udev rules under Debian. At this indicate the test application can exist executed without requiring superuser permissions.
The strace Command
The strace command is a very useful debugging tool that can execute a plan in order to intercept and tape the system calls that it performs. The system call proper name, the arguments passed, and the resulting return value are all visible, which makes it a valuable tool for solving runtime issues. Importantly, you do non need the source code for the executable in order to view the output of strace. For instance, y'all can utilize strace on your user-infinite application in order to view the advice between the user-infinite program and the kernel module, which results in the following for the exam application:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo apt-go install strace
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ strace -v
usage: strace [-dffhiqrtttTvVxx] [-a cavalcade] [-east expr] … [-o file]
[-p pid] … [-s strsize] [-u username] [-E var=val] …
[command [arg …]]
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ sudo strace ./test
execve("./test", ["./test"], [/* 15 vars */]) = 0
…
write(1, "Starting device test code exampl"..., 37Starting de…) = 37
open("/dev/ebbchar", O_RDWR) = 3
write(1, "Writing bulletin to the device [T"..., 60Writing message …) = 60
write(three, "Testing the EBBChar device", 26) = 26
write(one, "Reading from the device...\northward", 27Reading from the device…) = 27
read(3, "", 100) = 0
write(one, "The received bulletin is: [Testin"..., 66The received …) = 66
write(one, "Terminate of the program\due north", 19End of the program) = 19
exit_group(0) = ?
The arrangement telephone call output gives us impressive insight into the advice that takes place between the user-space programme test and the /dev/ebbchar device driver.
LKM Synchronization Issues
In that location is a serious problem with the LKM that is described in Listing 2. In the first commodity in this series I pointed out that LKMs practice not execute sequentially and that they can be interrupted. Those facts accept important consequences for the code that is written in this article, which tin be demonstrated as follows:
Step 1: At the outset terminal window shell you can execute the test application, but do not allow information technology to run to completion (past not pressing ENTER when prompted), as follows:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./exam
Starting device exam code example...
Blazon in a short string to transport to the kernel module:
This is the message from the commencement concluding window
Writing bulletin to the device
Press ENTER to read back from the device...
Footstep 2: Then at a second terminal window beat you lot tin can execute the same test awarding simultaneously as a second procedure on the Linux device. For example:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$ ./test
Starting device test code case...
Type in a short cord to send to the kernel module:
This is the bulletin from the 2nd terminal window
Writing bulletin to the device
Printing ENTER to read back from the device...
Stride 3: You can then return to the start concluding window shell and press ENTER to run the program to completion, which results in the following output (shaded output is the repeated output from Pace 1):
Starting device test code example...
Type in a curt string to ship to the kernel module:
This is the message from the first final window
Writing bulletin to the device
Press ENTER to read back from the device...
Reading from the device...
The received message is:
Terminate of the program
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$
You can run across that the received message is actually the bulletin that was sent past the examination application from Stride two, which is running in the second terminal window shell (non the first as might be expected). This is considering the bulletin that was sent in Stride ii overwrote the cord bulletin that was being stored by the LKM equally a result of Pace 1.
Pace 4: You can return to the second terminal shell and run it to completion by pressing ENTER, which results in (the shaded output is the repeated output from Step ii):
Starting device exam lawmaking example...
Type in a short string to ship to the kernel module:
This is the bulletin from the second terminal window
Writing message to the device
Printing ENTER to read back from the device...
Reading from the device...
The received message is:
End of the program
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbchar$
No string is received. That is because the LKM is non storing any messages at that point in time. It has already delivered the stored message to the showtime concluding window test application and reset the buffer index to 0.
Adding Mutex Locks
The Linux kernel provides a full implementation of semaphores — a data type (struct semaphore
) that is used for controlling access past multiple processes to a shared resource. The easiest way to use this semaphore lawmaking inside the kernel is to apply mutexes, equally at that place is a full prepare of helper functions and macros available.
A elementary style to prevent the problems described above is to prevent 2 processes from using the /dev/ebbchar device at the same fourth dimension. A mutex is a lock that can set (put downwardly) before a process begins using a shared resource. The lock can and so be released (brought up) when the procedure is finished using the shared resources. When the lock has been set, no other procedure tin admission the locked lawmaking region. Once the mutex lock has been released by the procedure that locked it, the shared region of lawmaking is in one case once again available to exist accessed by the other process, which in turn locks the resource.
In that location are simply very minor changes required to the code in club to implement mutex locks. An outline of these changes is provided in Listing 6 beneath:
1 2 3 four 5 half-dozen 7 8 ix 10 11 12 xiii 14 fifteen 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 learn 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 some other process" ) ; return - EBUSY ; } … } static int dev_release ( struct inode * inodep , struct file * filep ) { mutex_unlock ( & ebbchar_mutex ) ; /// Releases the mutex (i.east., the lock goes up) … } static void __exit ebbchar_exit ( void ) { mutex_destroy ( & ebbchar_mutex ) ; /// destroy the dynamically-allocated mutex … } |
The full source code case is bachelor in the /extras/kernel/ebbcharmutex/ directory. The final LKM is chosen ebbcharmutex.c. If you now perform the same test on the code that contains the mutex locks, y'all will detect a different behavior. For example:
Step 1: Using the start terminal window shell yous can load the module and execute the test application, which results in the following output:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ sudo insmod ebbcharmutex.ko
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ lsmod
Module Size Used by
ebbcharmutex 2754 0
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ ls -50 /dev/ebb*
crw-rw-rwT i root root 240, 0 Apr 12 15:26 /dev/ebbchar
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ ./exam
Starting device test code example...
Blazon in a short string to send to the kernel module:
Testing the ebbchar LKM that has mutex lawmaking
Writing message to the device
Printing ENTER to read back from the device...
Pace 2: So using the second last window beat out you can attempt to execute the test application to create the 2nd process:
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$ ./test
Starting device test code example...
molloyd@beaglebone:~/exploringBB/extras/kernel/ebbcharmutex$
Equally expected and required, the 2d process fails to access the device.
Footstep three: Returning to the starting time final window, the plan can be allowed to run to completion by pressing ENTER:
Starting device test code case...
Blazon in a short cord to send to the kernel module:
Testing the ebbchar LKM that has mutex lawmaking
Writing message to the device
Press ENTER to read back from the device...
Reading from the device...
The received message is:
Cease of the plan
At this point the second terminal window beat tin can now execute the test program, whereupon it will acquire the mutex lock and run correctly.
Conclusion
Click for the HTML and PDF version of the auto-generated Doxygen code documentation
At that place are several different issues described in this article. The important outcomes of this commodity are that:
- You tin at present create your own device such as /dev/ebbchar, which you lot can write information to and read information from. This is important, as it provides a bridge between the Linux user space and the Linux kernel space. Information technology enables you to develop advanced drivers, such equally communication devices, which tin can be controlled by C lawmaking that is running in user space.
- You tin can appreciate why it is of import to exist aware of the synchronization issues that can arise with kernel module programming.
- You can use udev rules to alter the properties of a device equally it is loaded.
The adjacent article in this series examines how y'all can interface to GPIO devices, such every bit physical button and LED circuits, enabling y'all to write your ain custom drivers for embedded Linux applications that interface to custom hardware. You tin read that article here: Writing a Linux Kernel Module — Part 3: Interfacing to GPIOs (coming shortly!). Finally, List 7 and Listing 8 are provided for your reference — they provide a list of the standard error states that are used in this serial of articles, along with their associated error numbers and descriptions.
1 2 iii 4 5 6 vii 8 9 ten eleven 12 13 14 15 16 17 eighteen 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #define EPERM ane /* Performance not permitted */ #define ENOENT 2 /* No such file or directory */ #define ESRCH iii /* No such procedure */ #define EINTR 4 /* Interrupted system call */ #define EIO 5 /* I/O error */ #ascertain ENXIO 6 /* No such device or address */ #ascertain E2BIG 7 /* Argument listing besides long */ #define ENOEXEC 8 /* Exec format error */ #define EBADF 9 /* Bad file number */ #define ECHILD 10 /* No child processes */ #define EAGAIN 11 /* Attempt again */ #define ENOMEM 12 /* Out of memory */ #define EACCES thirteen /* Permission denied */ #define EFAULT xiv /* Bad address */ #define ENOTBLK 15 /* Cake device required */ #ascertain EBUSY 16 /* Device or resource busy */ #define EEXIST 17 /* File exists */ #ascertain EXDEV 18 /* Cross-device link */ #define ENODEV 19 /* No such device */ #define ENOTDIR 20 /* Not a directory */ #define EISDIR 21 /* Is a directory */ #ascertain EINVAL 22 /* Invalid argument */ #define ENFILE 23 /* File table overflow */ #define EMFILE 24 /* Likewise many open files */ #define ENOTTY 25 /* Not a typewriter */ #ascertain ETXTBSY 26 /* Text file busy */ #define EFBIG 27 /* File too big */ #define ENOSPC 28 /* No space left on device */ #define ESPIPE 29 /* Illegal seek */ #define EROFS thirty /* Read-but file system */ #define EMLINK 31 /* Also many links */ #ascertain EPIPE 32 /* Cleaved pipe */ #define EDOM 33 /* Math argument out of domain of func */ #define ERANGE 34 /* Math effect not representable */ |
1 2 3 4 five 6 7 8 ix 10 xi 12 13 14 15 16 17 18 nineteen 20 21 22 23 24 25 26 27 28 29 thirty 31 32 33 34 35 36 37 38 39 twoscore 41 42 43 44 45 46 47 48 49 fifty 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 lxxx 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 /* Resources 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 xl /* Also many symbolic links encountered */ #define EWOULDBLOCK EAGAIN /* Functioning 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 three halted */ #ascertain EL3RST 47 /* Level 3 reset */ #define ELNRNG 48 /* Link number out of range */ #define EUNATCH 49 /* Protocol driver not fastened */ #define ENOCSI l /* No CSI structure available */ #define EL2HLT 51 /* Level ii halted */ #define EBADE 52 /* Invalid substitution */ #define EBADR 53 /* Invalid asking descriptor */ #define EXFULL 54 /* Exchange total */ #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 */ #ascertain ETIME 62 /* Timer expired */ #ascertain ENOSR 63 /* Out of streams resources */ #define ENONET 64 /* Auto is not on the network */ #define ENOPKG 65 /* Parcel non installed */ #define EREMOTE 66 /* Object is remote */ #define ENOLINK 67 /* Link has been severed */ #define EADV 68 /* Advertise error */ #define ESRMNT 69 /* Srmount mistake */ #define ECOMM 70 /* Communication mistake on send */ #define EPROTO 71 /* Protocol error */ #define EMULTIHOP 72 /* Multihop attempted */ #define EDOTDOT 73 /* RFS specific error */ #define EBADMSG 74 /* Not a data bulletin */ #define EOVERFLOW 75 /* Value too large for defined data type */ #ascertain ENOTUNIQ 76 /* Name non unique on network */ #define EBADFD 77 /* File descriptor in bad state */ #define EREMCHG 78 /* Remote address changed */ #define ELIBACC 79 /* Can not admission a needed shared library */ #define ELIBBAD 80 /* Accessing a corrupted shared library */ #define ELIBSCN 81 /* .lib section in a.out corrupted */ #ascertain ELIBMAX 82 /* Attempting to link in besides many shared libraries */ #ascertain 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 piping error */ #ascertain EUSERS 87 /* Too many users */ #ascertain ENOTSOCK 88 /* Socket operation on not-socket */ #define EDESTADDRREQ 89 /* Destination address required */ #define EMSGSIZE xc /* Message likewise long */ #define EPROTOTYPE 91 /* Protocol wrong type for socket */ #ascertain ENOPROTOOPT 92 /* Protocol not bachelor */ #ascertain EPROTONOSUPPORT 93 /* Protocol not supported */ #ascertain ESOCKTNOSUPPORT 94 /* Socket type not supported */ #define EOPNOTSUPP 95 /* Functioning non supported on ship endpoint */ #define EPFNOSUPPORT 96 /* Protocol family non supported */ #ascertain EAFNOSUPPORT 97 /* Address family not supported by protocol */ #define EADDRINUSE 98 /* Accost already in use */ #define EADDRNOTAVAIL 99 /* Cannot assign requested address */ #ascertain ENETDOWN 100 /* Network is down */ #define ENETUNREACH 101 /* Network is unreachable */ #define ENETRESET 102 /* Network dropped connectedness because of reset */ #define ECONNABORTED 103 /* Software caused connection abort */ #define ECONNRESET 104 /* Connectedness reset by peer */ #define ENOBUFS 105 /* No buffer space available */ #define EISCONN 106 /* Transport endpoint is already connected */ #ascertain ENOTCONN 107 /* Ship endpoint is non connected */ #ascertain ESHUTDOWN 108 /* Cannot send after ship endpoint shutdown */ #define ETOOMANYREFS 109 /* Besides many references: cannot splice */ #define ETIMEDOUT 110 /* Connection timed out */ #define ECONNREFUSED 111 /* Connection refused */ #define EHOSTDOWN 112 /* Host is downwardly */ #define EHOSTUNREACH 113 /* No route to host */ #define EALREADY 114 /* Functioning 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 /* Non a XENIX named type file */ #ascertain 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 plant */ #define EMEDIUMTYPE 124 /* Incorrect medium type */ #define ECANCELED 125 /* Operation Canceled */ #define ENOKEY 126 /* Required key not bachelor */ #define EKEYEXPIRED 127 /* Key has expired */ #define EKEYREVOKED 128 /* Primal has been revoked */ #define EKEYREJECTED 129 /* Key was rejected by service */ /* for robust mutexes */ #ascertain EOWNERDEAD 130 /* Possessor died */ #define ENOTRECOVERABLE 131 /* State non recoverable */ #define ERFKILL 132 /* Operation not possible due to RF-kill */ #define EHWPOISON 133 /* Retentiveness page has hardware mistake */
Subscribe to:
Post Comments (Atom)
|
0 Response to "Lkm Driver Read, Write, Open, Release, Llseek and Github"
Post a Comment