Is Linux Worth the Effort?

If your embedded system doesn't need networking and storage, porting Linux to your
hardware may not be worth the effort.

I was originally skeptical about how much Linux had to offer the embedded
community. I warmed to it after a positive experience on one project, but
further consideration has left me somewhat skeptical once again. In this column,
I share what I learned during that project. My hope is that when you're done
reading, you'll have learned something about how Linux can be put to best use in
embedded systems.

Any port in a
storm?Because so many RTOSes are written with portability in
mind"due to the wide variety of hardware platforms used in embedded
systems"performing a port to a new board sometimes takes only a few hours. And
porting an RTOS is often just a matter of porting to one particular
microcontroller. A microcontroller with a number of memory models may require a
port for each memory model, and the port may be tied to a particular compiler,
but that's the extent of the variation.

Porting Linux is a more complicated process. Linux expects mass storage and
an array of device drivers for peripherals, such as network cards, to be
considered a full implementation. Such a Linux implementation would be more
powerful than an easier-to-port RTOS, but the extra power is mostly in the area
of storage and networking. If your system doesn't require those capabilities,
you gain little from the effort you put into the port.

Single-board
LinuxSome embedded applications require a general purpose machine
with storage and networking. In such cases, the effort of porting Linux should
yield good results, especially if users are going to access the system over a
network. For these sorts of applications there is little temptation to vary much
from a standard PC architecture, except maybe to install a flash disk instead of
a hard disk to minimize moving parts. Thus, the port is also easier.

When you opt for a ready-made single-board computer (SBC) in your embedded
application, you want to take advantage of the software that has already been
written for that board. If the board has storage and a network connection, then
Linux is a great match because someone else has probably done the porting, and
the result is an environment in which a proficient Linux programmer can work
with limited embedded knowledge and limited access to the hardware.

Recently, I used a board from SSV (http://www.ssv-embedded.de/) called the
TRM/816. It's a single-board PC with a few extra peripherals: some I/O lines, a
CAN controller, and so on. The TRM/816 can run DOS or Linux, and its user
interface consists of a monochrome display and a numeric keypad. The user
interface is designed to be controlled by means of a custom application rather
than by means of a Linux shell or a Linux application. The Linux root console
can be connected to the serial port. In this case, I was building a device that
would connect to the CAN network running in a vehicle and display some status
information for service engineers. The device also needed modem connectivity to
allow remote monitoring and remote download of software upgrades.

Controlling a modem is straightforward from Linux because the mgetty
software provides a process that listens for incoming calls, responds to rings,
and hangs up when appropriate. Some configuration was required to allow for the
variations in the command set between modem manufacturers. The point-to-point
protocol (PPP) could then turn the modem connection into a TCP/IP connection.
(If so, additional configuration will be necessary to set the IP address.)

Once these components are in place, any ordinary PC can dial in to the
Linux-based device using a standard PPP connection. Once connected, users can
manage the system remotely via telnet or FTP. For our diagnostic device, we only
needed FTP and telnet during development; such access served no purpose in the
finished system. The more important communication happened using a socket
connection between a custom application on the PC and a process running on the
Linux box that was dedicated to communicating with the PC. I called this program
the remote-control process.

Linux's inet daemon allows you to choose which applications run in
response to certain kinds of traffic. We set up our device so that the
remote-control process would only launch when a remote PC tried to open a
connection to the device. We were also able to open one copy of the remote
control process for each incoming connection if required.

The root of it
allThe TRM/816 runs Linux entirely from a RAM disk. When the
system starts up, the disk image is copied from flash to RAM and the kernel
boots using the RAM disk as the root partition. Because you start from the same
state every time you power up the system, issues such as log files growing too
large or accidental deletion of a vital file are avoided. The disadvantage is
that if you download an updated program via FTP, that version will reside in the
RAM disk and disappear on the next reboot.

To permanently change the filesystem, you have to change an image of the
filesystem that you maintain on a more conventional Linux box. You can mount the
TRM/816 root file system to make it look like a normal partition and then alter
the files as required. When you're happy with the setup, you can compress the
filesystem and copy it to the device's flash memory via a serial connection.

To a conventional Linux developer this would seem unwieldy, but to the
embedded systems developer, who is used to flashing his entire application onto
a device after every software change, it's not an unreasonable way to do
business. What might seem more surprising to the embedded developer is that this
RAM disk occupies about 2MB of RAM. On my project, the code was only a few
hundred lines long, so the proportion of space occupied by this code is only a
tiny fraction of the overall memory requirement. This would not be typical if I
were using no operating system or a small kernel.

If you're faced with the necessity of running from RAM in this fashion,
you'll have to decide whether you want to be able to telnet into the device and
make updates that will persist through a reset. Being able to modify the system
throughout its life is normal in the desktop world, but it may or may not be
desirable on your embedded application.

Who drives the
hardware?I've shown you that running Linux gives us nice ways to
communicate with the outside world. But what happens when we try to communicate
with custom hardware"the bread and butter of most embedded systems? This is
where Linux development diverges from conventional embedded systems programming.

The elegant solution is to provide a device driver so that all hardware
access from user processes passes through that driver. For example, the device
driver might make the CAN controller visible as a file, /dev/CAN, and
this file can then be opened to read and write data onto the CAN bus. The
device's ioctl() function configures the controller for items such as bus
speed and error thresholds. (The ioctl(), short for I/O control, system
call is a standard Unix function for configuring a device and provides the only
access to the device that's not a stream of data into or out of the device. The
exact type of data passed to an ioctl() call varies for each device
driver.)

The advantage of a device driver is that even if you were to switch to
another CAN controller, the interface to the device via /dev/CAN would
hold. There's a strict division between control of the hardware, which handles
building a packet, and the application-level control, which decides what the
data means.

The device driver solution provides access management by only allowing one
process at a time to have special file /dev/CAN open. We can also set the
ownership and permissions for this special file to manage which users have
access to the CAN bus.

But do we really want that flexibility? This is a closed system, so the
concept of a user requesting "cat /dev/CAN" is nonsensical. The extra
control and protection we get from a Linux device driver buy us capabilities
that are great in a multiuser system, but don't have much upside on an embedded
system. In a closed system, designers can restrict the kind of activity that is
allowed.

An alternative to a device driver is to access the hardware directly from any
user process. This will concern many Linux purists. However, as a developer,
this is a far simpler approach. This is a closed system, so I can be certain
that no other process is trying to use the same hardware at the same time as I
am. You don't have to learn the intricacies of writing and installing Linux
device drivers.

One major loss is that a user process has little control over the real-time
behavior of the code. On my CAN project, I knew that there was sufficient
buffering in the CAN controller to minimize the chance of missing a message
while waiting for a context switch. The other disadvantage of accessing the
hardware directly from user space is that there's no easy way to intercept
interrupts there. In this project, since I had already given up the possibility
of hard real-time control, the loss of interrupts wasn't a big issue, though I
can see it would be in many other cases. Fortunately, RT Linux has a mechanism
for mapping real interrupts that are caught by the kernel to "soft interrupts,"
which can then be handled in user space.

A question of
scaleThe Linux philosophy is to write general solutions that
scale well and to separate user activities from kernel or device-driver
activities. But adding scalability takes effort. If many developers are going to
use the resulting drivers, then the effort is well spent, since these developers
won't have to access the hardware directly. If the target user for the device
driver is one developer"as is often the case"then the return on investment is
not good.

Optimization often depends on crossing the boundaries, or layers, that
separate different parts of our code. If I separate my application from my
device driver, I lose opportunities to optimize. In particular, I've seen
projects where the device drivers had to be rewritten to be less general, and
faster, than standard Linux drivers.

The separation of application and device driver is an alien concept to many
embedded programmers, which makes it a significant obstacle to using embedded
Linux for custom hardware.

In Linux, each device is seen as a file. The serial port is
/dev/serial, a terminal connection is /dev/term1. Like files,
devices are opened, closed, read, and written. This makes sense for what Linux
does best, which is manage storage and network connections. It doesn't map as
well to the control of the A/D converters, general-purpose I/O ports, and custom
hardware found in many embedded systems. Directly accessing memory locations
that map to the registers of the chip being controlled provides the kind of
control an embedded developer wants. Doing it this way also makes it easier to
see a correlation between the values written and the description in the data
sheet.

Some would argue that having a device driver allows porting to another CAN
controller or a different system. But porting the device driver is no easier
than porting an application that accesses the hardware directly. The Linux
approach works well when there are lots of developers using the same hardware,
but not on one custom board.

I see great advantages to Linux when starting with an existing SBC. Ideally,
all of the peripherals on the SBC would have Linux drivers already, since there
should be a large family of developers using the SBC. It is much harder to
justify that approach for a custom board where there is only one team of
developers writing software.

Unfortunately the TRM/816 board does not provide drivers for all of its
peripherals. Drivers are provided for standard peripherals, such as the serial
port, but extras like the CAN controller, serial EEPROM, and Compact Flash
interface, come only with sample code, not a complete driver.

Still, for this particular project, embedded Linux was a good match. Only
service technicians within the company were going to use the device, so volumes
were low. We could afford to spend extra money on the hardware in order to
reduce development time. Using embedded Linux on an SBC provided that balance.
If the same product had to be mass produced, we would likely have arrived at a
very different hardware and software solution.

Ask yourself . . .
The most important measure of Linux's success is whether projects
that use Linux manage to ship the resulting product.

As a developer, you need to search for a good fit. In the case of Linux, it's
most appropriate to consider first your networking and storage needs.

Niall Murphy has been writing software for user interfaces and medical
systems for 10 years. He is the author of Front Panel: Designing Software for
Embedded User Interfaces. Murphy's training and consulting business is based
in Galway, Ireland. He welcomes feedback and can be reached at nmurphy@panelsoft.com.