(Originally authored by Jesse Off, April 23, 2012)
At Technologic Systems, we have been gravitating towards creating mechanisms for custom hardware as applications in userspace rather than as a kernel drivers for several years now. Often times, the reasons for this can be difficult to communicate since the majority come from mindset that the first layer of software around the hardware is called the device driver and device drivers always have been a job for the OS kernel.
We don't really believe this to be necessarily so. There are enough advantages to userspace programming that we now only create code in the kernel as a last resort. If we absolutely must make a modification, we prefer the smallest modification necessary to export the rest of the work to userspace. Rather than relent to putting a ADC/DAC API in the kernel because it uses IRQs, we prefer a mechanism to export generic IRQ services to userspace. Rather than put a custom SD driver in the kernel because it needs to show up in the system as a block device to be mountable, we prefer to use a mechanism to create block devices from userspace.
We have implemented, with varying success, the following types of hardware 100% in userspace:
- Block device drivers for SD, NAND, SPI flash, Phase change memory, and RAID-like translation layers using the Linux NBD (Network Block Device) driver.
- Ethernet drivers using the Linux TAP driver.
- Serial port and 900Mhz radio drivers using the pseudo-tty driver.
- CAN network drivers via a RPC-like TCP socket protocol.
- Battery-backed RTC's by simply using settimeofday() userspace syscalls
- LCD framebuffers using userspace SysV SHM segments.
- Hardware watchdogs via named pipes
- Temp sensors, GPIO, LEDs, hardware random number generators, buttons/keypads, etc.. by directly manipulating them via /dev/mem via simple command line utilities and shell scripts.
When we design hardware, we always have in the back of our head the limitations and ramifications of using 100% userspace APIs. We have certain use cases in mind when making hardware and pick what compromises to make in hardware/software such that we can meet our performance goals comfortably without resorting to risky or complicated engineering trickery in Linux. Sometimes this means creating a bigger hardware FIFO to compensate for the coarse 10ms usleep() granularity from Linux userspace. Sometimes this means putting extra state machines in hardware so that software has less to do in times where performance really counts.