A while back I started tipping my toes into Linux Kernel module development. Mainly, to understand a driver for a data capture card I got to work with (or for, I believe).
Well, there is a go-to reference: the book Linux Device Drivers, 3rd Edition by Corbet, Rubini and Kroah-Hartman (from now on LDD3).
It’s great, it explains a lot and contains lots of hands-on example code, too. But, unfortunately it refers to the 2.6 Linux kernel. We’re at 6.8 at the time of writing this. So it’s a bit outdated.
No worries though, FOSS is a beautiful beast, and people have taken the example modules and updated them. Around version 5.15 that is. And things have changed again – at least for tty
it seems.
There is a pull request to make it 6.x compatible, but … it’s almost a year old by now, and it seems incomplete. Yet, it was a really great thing to come across at the start of this journey, because it restored my sanity.
So, here’s my go at the tiny tty example driver and I hope I can finish it up into something that works with a 6.x Linux kernel.
Things have changed
Using static major/minor numbers is discouraged, or at least, made easier to avoid in more recent kernel versions (feels like since 4.x or so). So, some functions used in LDD3’s examples simply don’t exist anymore.
alloc_tty_driver
is now superseeded by tty_alloc_driver
(okay, that re-naming is kind of evil). And while the former only bothered about the number of supported ports, the latter wants flags, too. So, it looks like the returned struct of type tty_driver
already contains a lot of entries when tty_alloc_driver
is done with it.
I’ve refrained from using the TTY_DRIVER_NO_DEVFS
flag, because I think dynamic stuff is always nice, so TTY_DRIVER_DYNAMIC_DEV
it is.
tty_driver->owner
is not supposed to be set anymore, according to this old’ish LKLM post. Same goes for ->major
(see tty_alloc_driver
).
The module is not put down anymore by put_tty_driver
but by tty_driver_kref_put
which seemingly also handles references in proc
(I’ve run into issues that the proc
entry was not removed after rmmod
ing the module and hence, on the next try insmod
was complaining).
I mention this, because LDD3’s static void __exit tiny_exit(void)
spends two thirds of its code to close ports and kfree
associated memory. This code is still present in the pull request with the updated example from 2023.
Still, I have to investigate if tty_driver_kref_put
also removes timers.
Things have gotten easier
Compared to the example for a 2.6 kernel in LDD3, the current version (at least for module __init
and __exit
) is way easier and frankly cleaner, i.e. easier to read.
Still, or maybe exactly because of that, I think it’s time for a fourth edition of Linux Device Drivers.
I try to go through with the rest of the module and understand and ideally fix it. Then I’ll upload it too, for later generations at kernel 8.x to despair of it. Link soon.