A Simple Approach to Character Drivers in User Space

The BaseBoard4 from Demand Peripherals can contain different combinations of 25 different character devices, all multiplexed on to a single USB-serial link. Its drivers, described here, show how writing drivers in user space can get a complex device up and running quickly.

Installing the Fanout and Proxy Drivers

The fanout and proxy modules are fairly straightforward
to build and install. Be sure the kernel header files for
your kernel are available. Both drivers are in the tarball at
www.linuxtoys.org/usd/usd.tar.gz. Download the driver tarball,
then untar, build and install the drivers:

tar -xzf proxy.tar.gz
cd proxy
make
sudo make install

How and where you install your modules and create device nodes is a
matter of personal preference. You can, for example, add the following
to your rc.local startup script or put the equivalent commands in a
udev rules file:

The robot's bootup scripts are slightly different because we wanted the
device node names to reflect the device it serves. For example, the dual
quadrature decoder might create fanout device nodes with the following:

The source tarball contains some simple demonstration programs in the
demo directory. The program pxtest2.c shows how to use the proxy device
to configure a user-visible string, and pxtest2 works by accepting a short
string and echoing it back on request. As mentioned above, drivers often
have to limit or otherwise modify a configuration value set by the user.
The pxtest2 program demonstrates this kind of processing by adding one
to each (non-newline) character in the input. You can run pxtest2 with
the following commands:

Our ad hoc approach to building device drivers in user space
has some nice features. It does not add a lot of kernel code and does
not require any user-space libraries. It supports select() everywhere
you might want it, and it has good flow control for streamed data.

Fanout and proxy have some shortcomings too. The data stream from
a fanout device is byte-aligned, which makes it inappropriate for an
application that needs to send blocks of binary data. Fanout could
not, for example, be used to simulate a new /dev/input device. Demand
Peripherals gets around this problem by sending lines of ASCII text that
are terminated by a newline. If you need multibyte transfers, you could
add an ioctl() to fanout that sets the byte count for atomic reads from
the data source.

If you like the simplicity of /dev/proxy but really need ioctl()
support, you can add it to the proxy driver by allocating two minor
numbers for each proxy device. Use the even-numbered minor numbers for
the data interface and the odd-numbered minor numbers for the ioctl()
interface. Your configuration might look like this:

Your additions to the proxy driver would have to serialize the data
passed to and from the ioctl() request, and your user-space driver dæmon
would have to open both devices to handle the ioctl() requests separately
from the data stream requests.

We used fanout and proxy to add device drivers for an FPGA-based
robotic controller, but they are actually fairly generic. What Linux
problems can you solve using fanout and proxy?

Bob Smith is a consultant specializing in embedded Linux.
You can reach him at bsmith@linuxtoys.org.