Hello! First of all this is my first Lemmy post, so if I did anything wrong pls tell me!
Now, I’m 19yo in 4th semester of Computer Engineering, and while I’m doing good in college I realized that they give us good background in electronics (from the basics to microcontrollers. ICs. logical design, etc) but the programming aspect is high level and web-oriented (python. java, php)! I appreciate learning those, but I’m not interested on that but rather on a kernel/firmware development! So… I’ve been learning C for some weeks and while I do love it (mainly been learning from K&R and Zed A. Shaw - Learn C the Hard Way) I don’t really know how to practice the skills required to do the proper bridge between hardware and software.
Basically, how does one begin their first real project to learn how to write drivers/baremetal and testing them? Thanks for reading and sorry if my question is dumb, I just feel a bit lost.


This answer is going to go in multiple directions.
If you’re looking for practice on using C to implement ways to talk to devices and peripherals, the other commenter’s suggested to start with an SBC (eg Raspberry Pi, Orange Pi) or with a microcontroller dev kit (eg Arduino, MSP430, STM32) is spot-on. That gives you a bunch of attached peripherals, the datasheet that documents the register behavior, and so you can then write your own C functions that fill in and read those registers. In actual projects, you would probably use the provided libraries that already do this, but there is educational value in trying it yourself.
However, just because you write a C function named “put_char_uart0()”, that isn’t enough to prepare for writing full-fledged drivers, such as those in the Linux and FreeBSD kernel. This next step is more about software design, where you structure your C code so that rather than being very hardware-specific (eg for the exact UART peripheral in your microcontroller) you have code which works for a more generic UART (which abstracts general details) but is common-code to all the UARTs made by the same manufacturer. This is about creating reusable code, about creating abstraction layers, and about writing extensible code. Not all code can be reusable, not every abstraction layer is desirable, and you don’t necessarily want to make your code super extensive if it starts to impact your core requirements. Good driver design means you don’t ever paint yourself into a corner, and the best way to learn how to avoid this is through sheer experience.
For when you do want to write a full-and-proper driver for any particular peripheral – maybe one day you’ll create one such device, such as by using an FPGA attached via PCIe to a desktop computer – then you’ll need to work within an existing driver framework. Linux and FreeBSD drivers use a framework so that all drivers have access to what they need (system memory, I/O, helper functions, threads, etc), and then it’s up to the driver author to implement the specific behavior (known in software engineering as “business logic”). It is a learned skill – also through experience – to work within the Linux or FreeBSD kernels. So much so that both kernels have gone through great lengths to enable userspace drivers, meaning the business logic runs as a normal program on the computer, saving the developer from having to learn the strange ways of kernel development.
And it’s not like user space drivers are “cheating” in any way: they’re simply another framework to write a device driver, and it’s incumbent on the software engineer to learn when a kernel or user space driver is more appropriate for a given situation. I have seen kernel drivers used for sheer computational performance, but have also seen userspace drivers that were developed because nobody on that team was comfortable with kernel debugging. Those are entirely valid reasons, and software engineering is very much about selecting the right tool from a large toolbox.
Thanks for the detailed reply, yeah I’m going with the microcontroller kit definitely, I kinda had it in mind before posting but wanted to make sure as I felt a bit lost given how extense (and interesting) the field is. I had no idea of the user space drivers, it is actually a really interesting concept and I’m definitely checking it out as it sounds like a good place to start contributing to the Linux kernel (which is something I wish to do + I daily drive Linux so most likely I will end up writing a solution for a problem I can personally have). Which software do you suggest for testing? The only one I’ve used for virtual ISO is VirtualBox.
I personally started learning microcontrollers using an Arduino dev kit, and then progressed towards compiling the code myself using GCC and loading it directly to the Atmel 328p (the microcontroller from the original Arduino dev kits).
But nowadays, I would recommend the MSP430 dev kit (which has excellent documentation for its peripherals) or the STM32 dev kit (because it uses the ARM32 architecture, which is very popular in the embedded hardware industry, so would look good on your resume).
Regarding userspace drivers, because these are outside of the kernel, such drivers are not kept in the repositories for the kernel. You won’t find any userspace drivers in the Linux or FreeBSD repos. Instead, such drivers are kept in their own repos, maintained separately, and often does unusual things that the kernel folks don’t want to maintain, until there is enough interest. For example, if you’ve developed an unproven VPN tunnel similar to Wireguard, you might face resistance to getting that into the Linux kernel. But you could write a userspace driver that implements your VPN tunnel, and others can use that driver without changing their kernel. If it gets popular enough, other developers might put the effort into getting it reimplemented as a mainline kernel driver.
For userspace driver development, a VM running the specific OS is fine. For kernel driver development, I prefer to run the OS within QEMU, since that allows me to attach a debugger to the VM’s “hardware”, letting me do things like adding breakpoints wirhin my kernel driver.
Oh wow, ok I’m checking both of those kits, also well the userspace being on another repo makes sense, so contributing to it would still be nice for me, not only to contribute to the community but also to learn. QEMU sounds really fitting for my interests so I’m going to experiment with it, thanks! I appreciate all the help