Working with Char Drivers
A device driver is special code (running in kernel space) that interfaces a physical device to the system and exports it to the user space processes using a well-defined API, that is, by implementing some system calls on a special file. This is due to the fact that, in a Unix-like OS, everything is a file and physical devices are represented as special files (usually placed in the /dev directory), each one connected to a particular device (so, for instance, the keyboard can be a file named /dev/input0, a serial port can be a file named /dev/ttyS1, and a real-time clock can be /dev/rtc2).
Looking at the next diagram, we see that the kernel space is used to abstract hardware to user space, so that every process uses the same interface to get access to peripherals, and this interface is composed by a set of system calls:
The diagram also shows that it is possible to get access to peripherals not only by using device drivers but also by using another interface such as sysfs or by implementing a user space driver.
Since our peripherals are just (special) files, our drivers should implement the system calls we need to manipulate these files and especially the ones useful to exchange data. For example, we need open() and close() system calls to start and stop the communication with the peripheral and read() and write() system calls to exchange data with it.
The main difference between a normal C function and a system call is just the fact that the latter is mainly executed into the kernel, while a function executes into the user space only. For example, printf() is a function, while write() is a system call. The latter (except for the prologue and epilogue part of a C function) executes in the kernel space, while the former executes predominantly in the user space even if, at the and, it calls write() to actually write its data to the output stream (this is because all input/output data flows must pass through the kernel anyway).
For more information check this book out: https://prod.packtpub.com/hardware-and-creative/gnulinux-rapid-embedded-programming
Well, this chapter will show us how to implement at least the open(), close(), read(), and write() system calls in order to introduce device drivers programming and the first steps into char drivers development.
Now it's time to write our first device driver! In this chapter, we will start with a very simple character (or char) driver in order to cover the following recipes:
- Creating the simplest char driver
- Exchanging data with a char driver
- Using the ''Everything Is a File" abstraction