Hello readers! I was quite fascinated by what are device drivers and how do they work. Pursuing that I have learned a lot and I would like to share that with all of you. This article will focus on the need and use of device drivers.
What are Device Drivers?
Before I go into the details of what a device driver is, we need to understand where are they used.
Whenever we have a complex system which is controlling multiple processes at once (not unlike the device you are using now), we cannot just use while loops with delay to control tasks. Imagine if your computer did everything using delays! Unless and until you close one application, you wouldn’t be able to start another since the processor is stuck in a while loop. That would suck! Right? So how does it actually work? How are you using multiple processes at the same time? We are stuck. Are we?
At this point, the great ‘Operating System’ comes to our rescue. “What is an OS?” you ask. I don’t think I have enough space to answer it properly. “Operating systems” deserves a whole article for itself. But for our understanding, it would suffice to say that an OS is the program which allows other programs to run and it takes care that these programs do not mess up.
In essence, the OS is like a very strict librarian. The librarian happily allows you to reserve books, but if you dare not return it on time or if you try to damage the book, no one can save you from her wrath! Similarly, the OS allows other processes to reserve and use the resources of the system like memory, CPU, screen, network, etc. But if the running process tries to do something which it is not allowed to do, the process is ‘killed’ (generally with some kind of exception being generated).
Furthermore, the OS doesn’t allow just one process to hog up the CPU for entire time. After some predefined time, the running process is stopped and kept aside to let other processes use the CPU as well. This all happens thousands of times in a second! If I had a superpower which allowed me to slow down the time (I wish), then I would be actually able to notice that at a given point of time only one process is running. And after sometime, some other process is running and so on.
See the below diagrams and you would understand what am I trying to say. Because, well, a picture is worth a thousand words.
Once, I wanted to make the reg, green and blue LED turn on to make white colored light. But I could not turn on more than one LED at a time (make up any imaginary restriction you like). So, I defined tasks for each of the LEDs (since I like modular coding). Each task turns on the given LED. My first attempt was something like below.
In this attempt, I tried to put these tasks in a sequence and executed the code. I saw red light for the first 10 seconds, then green for the next 10 seconds and blue for the last 10 seconds. Well, this is not what I wanted. I wanted white light! How hard can it be? Thus, comes my second attempt where I chopped up all the tasks into very tiny pieces and arranged them together in a loop. Which looked something like this:
If you have noticed that this is not white color, then I must say that you are very perceptive! But if I had chopped up these tasks into 1000 pieces instead of just 30 pieces, I would get to see white light (I leave that up to your vivid imagination). So, dividing tasks into very small portions gives us the illusion of concurrent execution. This is what the OS does with multiple tasks. It is called context-switching.
Here is the evidence :
I digress. Again. If I start wandering off-topic, just ask me to focus. Oh, wait, this is an article. Perhaps I should just try to adhere to the topic.
We still don’t know why we need device drivers! Well, that’s fine. At least we learnt something about the OS. As I said earlier, OS is a very strict librarian. OS will not allow you to access the memory, hardware etc. directly. What if the process did something with the hardware which rendered it useless? Thus, if you want to work with any device on the system, you need some intermediate program which the OS deems trustworthy. As you might have guessed, these programs are device drivers. These programs allow your applications to connect with the hardware by reading commands from your programs and (safely) executing them on the hardware.
These programs are directly loadable into the kernel code (and hence have the name Loadable Kernel Modules: LKM). This allows for the OS code to remain small and allows the user (you) to use whichever driver you prefer. Even if you add a new hardware, maybe a new graphics card for gaming, you can just install the drivers and you are good to go! And whenever you happen to put down the gaming console, and the graphics card is not needed anymore, the device driver will be unloaded from the OS (although it doesn’t happen exactly that way).
Now that we have got this out of our way, I wonder whether any program can be used like a device driver? Or is there anything special about these? The first thing I noticed was there was no main() function! And somehow the compiler was fine with it, no errors, not even a warning!
I am sure that you must have thought – How is that even possible. Your compiler must be broken. As it turns out, my compiler was just fine. These programs (the device drivers) don’t need a main() function because it doesn’t require an entry point similar to main().
The functions defined within the drivers are called by the OS whenever something special happens to a specific file. Wait! Hold on! A File? what’s all this about a file?
No worries. Take a deep breadth. In and Out.
Each OS has different ways of representing the hardware the system has. It can be a special kind of object and who knows, maybe just a pointer to some address space. Linux uses files to represent the devices the system has. When you write() to the device file, that data is sent to the device. When you read() from that file, you get the data returned by the device. And when you ioctl() to the file, the command you want is sent to the device. In essence, there are somewhere around 30 different function to manipulate files. Yes 30! You can seek, read, write, release, lock and can do a lot more things with the files. These functions, when called by your program, call the OS which in turn calls the corresponding function in the device driver code.
These drivers are classified into 3 major types: Block drivers, Character drivers and Network drivers.
Block drivers always need to write/read a block of data (maybe 512 bytes or more).
Character driver can write/read one character (byte) at a time.
And Network driver is used for connecting with network interfaces. Although, Block drivers and Network drivers are beyond the scope of this article. We will just focus on the Character device driver.
To summarize, see the below figure. It shows how user space program (i.e. the programs you generally use), communicate with the OS space using system calls. These system calls then inform the corresponding device driver. Which, in turn, communicates to the hardware. This is a very simplified view of an OS and generally there is a lot more stuff going on and a lot more interfaces are at play. But, for our understanding, this should suffice.
Whew! We are finally done with this. What’s next? Ah, yes, the driver itself. I will cover SPI driver in my next article , Stay Tuned.
Lastly, thanks for taking out the time to go through this article. This was my first ever article and I hope that you guys like, or better yet, love it!
Aniruddha is a graduate student at NC State University – studying computer engineering. He came in contact with embedded systems during his undergraduate education while he was in the RoboCon team. Till this point, he has done numerous projects and has presented 2 papers in international conferences which are to be published. Apart from this, his interests lie mainly in the domain of RTOS, device drivers and Optimization.