I already have the other book by the same author, Joseph Kong, "Designing BSD Rootkits: An Introduction to Kernel Hacking" and liked it very much, so when I got the chance to get an advance copy of his new book for review, "FreeBSD Device Drivers: A Guide for the Intrepid", I couldn't say no.
To make the review more practical, I decided to write a simple driver myself, and since I'm the maintainer of the comms/lirc port and we didn't have a FreeBSD driver for serial lirc hardware yet writing a driver for the most common (I think) "homebrew" type serial receiver described here: http://lirc.org/receivers.html seemed like a good idea.
About the book: http://nostarch.com/bsddrivers
The book introduces you to almost everything you need to know to write many types of drivers, it does this mainly by doing code walkthroughs for several example- and real-world drivers. It obviously cannot cover _everything_ (sound drivers for example are not covered, nor is miibus(4)), but what it covers I'd say should give you enough information to be able to look at manpages and existing drivers for missing details. 100% recommended!
About the driver: http://people.freebsd.org/~nox/tmp/uartlirc-preliminary-003.shar
Instead of writing the serial lirc driver from scratch I decided to take the existing uart(4) driver from 9.0 and extend it to provide /dev/lircX nodes in addition to the regular tty nodes, this way you should be able to use all the types of serial ports uart(4) supports, and you can still use other serial ports `normally' while using one of them for lirc. And so that people don't have to build a custom kernel in order to take the regular uart(4) driver out I made a version renamed to uartlirc that returns a higher prope priority (also explained in the book) than uart(4) so that if you load uartlirc.ko from loader at boot it simply overrides the regular uart(4) driver.
The diffs against 9.0 uart(4) I put here: http://people.freebsd.org/~nox/tmp/uart-lirc-preliminary-003.patch - including comments why I still call it "preliminary". (I want to commit this or a future version of uartlirc to ports later. [Edit: done, see comms/uartlirc.])
How the driver works:
The "homebrew" serial lirc receiver essentially just connects the output of an IR receiver chip to the DCD line of a serial port, so IR pulses by a remote cause DCD to go off and on in sync, and if the correspondig /dev/lircX device node was opened from userland the driver measures the time since the last DCD change in the status irq routine and stores that and the info whether it's a pulse or a space in a fifo to be returned by read() calls from the /dev/lircX node to userland. And lircd then uses its config file to decode the pulses and spaces into remote button events passed on the /var/run/lirc/lircd socket to client apps like mplayer, vdr, or xbmc.
And finally a note about the serial IR module available at: http://www.dvbshop.net/product_info.php/info/p13_IR-Module-2-Meter-COM-Port-serial--WinLIRC-LIRC-Girder-Igor-etc-.html
This module is what I tested the driver with (first) using
[cmd=#]mode2 -d /dev/lirc0[/cmd]
and it turned out on the first serial port I tested it on (a laptop docking station) it didn't work because this IR module uses a voltage divider (two resistors) for the IR receiver chip power instead of a regulator or Z diode, and since the docking station serial port only outputs +-5 V instead of the +-12 V in the RS232 spec the IR receiver chip only got about 3 V and didn't work. Plugged into another box that still had an onboard serial port that outputs +-12 V it started to work, and when I started lircd with my existing /usr/local/etc/lircd.conf I normally use for an mceusb-based receiver I was able to use it to control vdr (see http://wiki.freebsd.org/VDR) as well. (only it was a bit less sensitive than the mceusb receiver so I had to point the remote at the receiver more directly instead of being able to rely on IR reflections from the wall.)
[Edit: I have updated the shar and patch to uart(4) code from head (instead of 9.0), fixed read() not respecting O_NONBLOCK, and improved behaviour at kldunload. [...]]
[Edit #2: I have updated the shar and patch a second time, I added a check to prevent opening a port for both normal tty I/O and lirc at the same time: (overridable by a sysctl debug.uartlirc_allowopenboth) http://people.freebsd.org/~nox/tmp/uartlirc-preliminary-003.shar and http://people.freebsd.org/~nox/tmp/uart-lirc-preliminary-003.patch. If someone finds other bugs/problems please let me know!
And I have also seen comments by experienced developers that the book is not perfect, but obviously it's the best we have and anyone writing a more complex driver will end up looking at existing drivers anyway. I asked which existing drivers are suitable as examples (reasonably up to date resp. apis etc), and Scott Long said:
- Code: Select all
<scottl_> for network drivers, igb and ixgb are probably decent examples that don't use MIIBUS. bce is probably a good example that does
<scottl_> for storage, MFI is good for block drivers
<scottl_> isci is good for CAM drivers
And George Neville-Neil pointed me at a class about igb(4) he gave at bsdcan 2009: http://www.bsdcan.org/2009/schedule/events/146.en.html.]