Reader small image

You're reading from  Linux Device Driver Development Cookbook

Product typeBook
Published inMay 2019
Reading LevelIntermediate
PublisherPackt
ISBN-139781838558802
Edition1st Edition
Languages
Tools
Right arrow
Author (1)
Rodolfo Giometti
Rodolfo Giometti
author image
Rodolfo Giometti

Rodolfo Giometti is an engineer, IT specialist, GNU/Linux expert and software libre evangelist. He is the author of the books BeagleBone Essentials, BeagleBone Home Automation Blueprints and GNU/Linux Rapid Embedded Programming by Packt Publishing and maintainer of the LinuxPPS projects. He still actively contributes to the Linux source code with several patches and new device drivers for industrial applications devices. During his 20+ years of experience, he has worked on the x86, ARM, MIPS, and PowerPC-based platforms. Now, he is the co-chief at HCE Engineering S.r.l., where he designs new hardware and software systems for the quick prototyping in industry environment, control automation, and remote monitoring.
Read more about Rodolfo Giometti

Right arrow

Additional Information: Advanced Char Driver Operations

Technical requirements

When we have to manage a peripheral, it's quite common to need to modify its internal configuration settings, or it may be useful to map it from the user space as if it was a memory buffer in which we can modify internal data just by referencing a pointer.

For example, frame buffers or frame grabbers are good candidates to be mapped as a big chunk of memory from the user space point of view.

In this case, having the support of the lseek(), ioctl(), and mmap() system calls is fundamental. If, from the user space, the usage of these system calls is not tricky, within the kernel they require some attention by the driver developer, especially the mmap() system call, which involves the kernel Memory Management Unit (MMU).

Not only that one of the principal tasks a driver developer must pay attention to is the data exchanging mechanism with the user space...

Going up and down within a file with lseek()

Here we should remember that the prototypes of the read() and write() system calls were the following:

ssize_t (*read) (struct file *filp,
char __user *buf, size_t len, loff_t *ppos);
ssize_t (*write) (struct file *filp,
const char __user *buff, size_t len, loff_t *ppos);

When we tested our char driver using the program in the chapter_03/chrdev_test.c file, we noticed that we weren't able to reread written data unless we patched our file as follows:

--- a/chapter_03/chrdev_test.c
+++ b/chapter_03/chrdev_test.c
@@ -55,6 +55,16 @@ int main(int argc, char *argv[])
dump("data written are: ", buf, n);
}

+ close(fd);
+
+ ret = open(argv[1], O_RDWR);
+ if (ret < 0) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+ printf("file %s reopened\n", argv[1]);
+ fd = ret...

Using ioctl() for custom commands

In Chapter 3, Working with Char Drivers, we discussed the file abstraction and mentioned that a char driver is very similar to a usual file, from the user space point of view. However, it's not a file at all; it is used as a file but it belongs to a peripheral, and, usually, peripherals need to be configured to work correctly, due to the fact they may support different methods of operation.

Let's consider, for instance, a serial port; it looks like a file where we can (forever) read or write using both the read() and write() system calls, but to do so, in most cases, we must also set some communication parameters such as the baud rate, parity bit, and so on. Of course, these parameters can't be set with read() or write(), nor by using the open() system call (even if it can set some accessing modes as read or write only), so the...

Accessing I/O memory with mmap()

In the Getting access to I/O memory recipe in Chapter 6, Miscellaneous Kernel Internals, we saw how the MMU works and how we can get access to a memory-mapped peripheral. Within the kernel space, we must instruct the MMU in order to correctly translate a virtual address into a proper one, which must point to a well-defined physical address to which our peripheral belongs, otherwise, we can't control it!

On the other hand, in that section, we also used a userspace tool named devmem2, which can be used to get access to a physical address from the user space, using the mmap() system call. This system call is really interesting, because it allows us to do a lot of useful things, so let's start by taking a look at its man page (man 2 mmap):

NAME
mmap, munmap - map or unmap files or devices into memory

SYNOPSIS
#include <sys/mman.h>...

Locking with the process context

It's good to understand how to avoid race conditions in case more than one process tries to get access to our driver, or how to put to sleep a reading process (we talk about reading here, but the same thing also holds true for writing) in case our driver has no data to supply. The former case will be presented here, while the latter will be presented in the next section.

If we take a look at how read() and write() system calls have been implemented in our chrdev driver, we can easily notice that, if more than one process tries to do a read() call or even if one process attempts a read() call and another tries a write() call, a race condition will occur. This is because the ESPRESSObin's CPU is a multiprocessor composed of two cores and so it can effectively execute two processes at the same time.

However, even if our system had just one...

Waiting for I/O operations with poll() and select()

In a complex system such as a modern computer, it's quite common to have several useful peripherals to acquire information about the external environment and/or the system's status. Sometimes, we may use different processes to manage them but we may need to manage more than one peripheral at a time, but with just a single process.

In this scenario, we can imagine doing several read() system calls on each peripheral to acquire its data, but what happens if one peripheral is quite slow and it takes a lot of time to return its data? If we do the following, we may slow down all data acquisition (or even lock it if one peripheral doesn't receive new data):

fd1 = open("/dev/device1", ...);
fd2 = open("/dev/device2", ...);
fd3 = open("/dev/device3", ...);

while (1) {
read(fd1, buf1, size1...

Managing asynchronous notifications with fasync()

In the previous section, we considered the special case in which we can have a process that must manage more than one peripheral. In this situation, we can ask the kernel, which is the ready file descriptor, where to get data from or where to write data to using the poll() or select() system call. However, this is not the only solution. Another possibility is to use the fasync() method.

By using this method, we can ask the kernel to send a signal (usually SIGIO) whenever a new event has occurred on a file descriptor; the event, of course, is a ready-to-read or read-to-write event and the file descriptor is the one connected with our peripheral.

The fasync() method does not have a userspace counterpart due to the already presented methods in this book; there is no fasync() system call at all. We can use it indirectly by utilizing...

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Linux Device Driver Development Cookbook
Published in: May 2019Publisher: PacktISBN-13: 9781838558802
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Rodolfo Giometti

Rodolfo Giometti is an engineer, IT specialist, GNU/Linux expert and software libre evangelist. He is the author of the books BeagleBone Essentials, BeagleBone Home Automation Blueprints and GNU/Linux Rapid Embedded Programming by Packt Publishing and maintainer of the LinuxPPS projects. He still actively contributes to the Linux source code with several patches and new device drivers for industrial applications devices. During his 20+ years of experience, he has worked on the x86, ARM, MIPS, and PowerPC-based platforms. Now, he is the co-chief at HCE Engineering S.r.l., where he designs new hardware and software systems for the quick prototyping in industry environment, control automation, and remote monitoring.
Read more about Rodolfo Giometti