A Software Architecture For Embedded Telematics Devices On Linux
A Software Architecture For Embedded Telematics Devices On Linux
HUGO HOLGERSSON
HUGO
c HOLGERSSON, Göteborg, Sweden, December 2011
This master thesis proposes a Linux-based software architecture for modern telemat-
ics devices. Our device is installed in trucks and has the purpose of forwarding collected
GPS data to an online data center in a robust way. Telematics units are are highly
concurrent systems where a crucial part of the software architecture is the communica-
tion schema: Within the system, sensor data need to be independently gathered and
processed. Relevant information is then presented to the truck driver or forwarded to an
online data center.
We investigate D-Bus, an IPC system that is mostly used in desktop Linux systems, as
a potential replacement for traditonal, in-kernel message passing. When moving to more
elaborated embedded hardware platforms, such higher level programming paradigms and
protocols become more attractive than pure low level programming. This thesis highlights
the possibility of incorporating such techniques also in embedded Linux systems.
We demonstrate an event-driven system architecture, implemented using the Qt C++
framework. D-Bus is used for the system’s interprocess communication, in contrast to
TCP sockets that were used in the previous product. We make low level sensor data
available via D-Bus by two layers of abstraction: a Linux I2C chip driver and Linux
daemon using D-Bus.
Contents
1 Introduction 1
3 Software architecture 7
3.1 Architectural overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3.2 System services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.3 Exported XML interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.4 The Qt framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
5 Hardware abstractions 20
5.1 Board and peripheral layout . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.2 I2C bus principles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.3 Development setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
5.4 I2C abstractions in Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.4.1 Driver usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5.4.2 Linux kernel modifications . . . . . . . . . . . . . . . . . . . . . . . 23
5.5 Implementing an I2C slave firmware . . . . . . . . . . . . . . . . . . . . . 26
5.5.1 Hardware and bus initialization . . . . . . . . . . . . . . . . . . . . 26
5.5.2 Serving requests by interrupt handlers . . . . . . . . . . . . . . . . 26
i
A D-Bus in an embedded context 30
A.1 Comparison to sockets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
A.2 Introduction to D-Bus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
A.3 Criticism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
A.4 Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
A.4.1 Language bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
A.4.2 File size footprint . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
A.4.3 Memory usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
C Methods of development 36
C.1 Cross-compiling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
C.2 Debugging Linux applications . . . . . . . . . . . . . . . . . . . . . . . . . 37
C.3 Debugging "bare metal" code . . . . . . . . . . . . . . . . . . . . . . . . . 37
C.3.1 Debugging U-Boot . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
C.4 Build tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
C.4.1 IDE integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
ii
Acronyms
ABI Application Binary Interface
ADK Application Development Kit
API Application Programming Interface
CAN Controller Area Network
D-Bus FreeDesktop’s "Desktop Bus" for IPC
ELF Executable and Linkable Format
GCC GNU Compiler Collection
GDB GNU Project Debugger
GPS Global Positioning System
GUI Graphical User Interface
I2C 2-wire Inter-IC bus
IC Integrated Circuit
IDE Integrated Development Environment
IVI In-vehicle Infotainment
JTAG Joint Test Action Group (JTAG) standard test access port
PDK Product Development Kit
PDU Protocol Data Unit
POSIX Portable Operating System Interface
Qt A cross-platform application C++ framework
ROM Read-Only Memory
SDK Software Development Kit
SoC System on Chip
UML Unified Modeling Language
XIP Execute in place
XML Extensible Mark-up Language
iii
Chapter 1
Introduction
With Internet access everywhere new possibilities and markets has emerged in the field
of wireless surveillance. Any type of vehicles - whether it is a personal car, an ambulance,
a bus in public transport or a normal truck - can nowadays be connected to the Internet
thanks to the nation wide cellular phone networks.
A steady Internet connection enables fleet operators to monitor their vehicles’ po-
sitions in real time. By automatically reporting data, time is also saved for the truck
driver, who does no longer need to report his locations and work hours manually. A such
system, designed to report information on geographically sparse units, is commonly ref-
ereed to as a telematics system. Telematics systems designed for fleet management can
globally be seen as a combination of three subsystem that co-operates over the Internet:
Internet Internet
uplink connection to
(GPRS) fleet operator
1
Each one of the surveilled vehicles is equipped with an embedded computer system
(a). The embedded system runs on a customized hardware board that could either be
factory-fitted or integrated into the vehicle as an add-on. In any case, these configurations
does often include a GPS receiver and a mobile data modem that enables the system
to connect to the Internet. Through the modem connection, each of these embedded
systems are uplinked to the central server infrastructure (b) to which data concerning
the vehicle is uploaded. The back-office server side organizes the data and groups the
information according the requirements set out by the fleet that the vehicle belongs to.
These servers do also have an interface towards the fleet’s administration department
(c) by which fleet operators can benefit from the collected information presented by
desktop/phone software applications. Notably, each individual vehicle’s geographical
location and ongoing mission can be visualized on maps - an excellent tool to simplify
fleet administration and logistics.
This report focuses on the design aspects of the onboard (a) computer system, residing
inside the vehicles. Often, these systems do simultaneously support some kind of user
interaction, along side the functionality of telematics, forming an In-Vehicle-Infotainment
system.
Due to the implied safety concerns involved in designing systems used in and con-
nected to vehicle, developing IVIs is more difficult than developing personal devices, such
as mobile phones; without violating the drivers’ safety, these devices do need to use the
CAN bus in order to read the out values on for example velocity, charge and temperature.
When designing an embedded software system, it is nowadays common to rely on,
already freely available open-source components. Linux, a free general purpose operating
system, has become more and more common in the embedded segment the last ten years.
FreeElectrons, an embedded Linux consultant company, motivates this success by the
ability to re-use components that has been proved to work well, without losing control
over the software. Since Linux is free, it can be duplicated to any number of devices,
without additional licensing costs which make Linux a potential solution upon which
IVIs could be designed.
R. Streif, director of embedded solutions for The Linux Foundation argues that for
any Internet-connected consumer device, 95% of its functionality will be similar to other
devices of the same type. By relaying in open source technology for this part, more effort
and cost can be put on the fine-tuning the product, notably its user experience.
In order to enrich user experience, an IVI system is expected to be extendable with
new user applications. A bare Linux system does not have these abilities built-in which
was the reason why for example Google Android was designed. Android adds several
middleware services, on top of Linux that enables further user customization.
We have in this thesis investigated how to extend Linux in a similar way by adding two
supporting middleware services that helps developers to (1) benefit from the connected
hardware found in the vehicle, as well as (2) design applications that can rely on the
online servers. In order for application to benefit from these services, a way of inter
process communication is needed. This report demonstrates how the Linux/FreeDesktop
D-Bus can be used in an embedded system as a way to let middleware services export
2
functionality to the on-top applications.
This report is organized as follows: chapter 2 describes the requirements set out by
the company, chapter 3 gives an overview of the proposed solution, chapter 4 details
the middleware that gives access to the server infrastructure, chapter 5 describes the
foundations behind the middleware that gives hardware abstractions. The final section
draw conclusions. Three appendices are given with more information on D-Bus and the
development of embedded Linux systems.
3
Chapter 2
Apart from keeping the existing, basic telematics functionality (described in the first
chapter) the company gave a few explicit requirements concerning the next product.
This chapter describes the meanings of these requirements as well as the main principles
of the former product to which we need to remain compatible.
2.1 Requirements
When drawing the next generation product, the following requirements were taken into
consideration. The new product needs to:
(a) Comply to the given telematics specification.
(b) Be compatible with the current server infrastructure.
(c) Maintain existing connectivity to vehicle peripherals.
(d) Run the current GUI software presented to vechicule driver.
Requirement (b) implies that the external (proprietary) communication protocol
needs to be kept. By keeping the old binary protocol, the new embedded system will
be backward-compatible with the existing sever infrastructure. When designing the new
product, focus has thus rather been put on improving the embedded system’s internal
communication but in a way that is transparent to external communication.
Telematics specification The purpose of the embedded Linux system, also shown in
figure ??, is (a) is to gather data from the vehicle’s sensors and report this information
to a central server on the Internet.
Linux daemons (system services) upload data to the online data center (b) whose
database is storing information about all registered vehicles. At regular time intervals,
each service will upload data, concerning for example the vehicle’s current position and
internal temperatures.
From the customers point of view, gathered data concerning to their fleet is made
available either by a web interface or by a Windows-based software (c).
4
In-vehicle system A graphical user interface, similar to a common GPS, is presented
on a screen next to the truck driver. The current GUI program was formerly run on
an external device; a device with an independent operating system. In the upcoming
product a dumb frame buffer screen, directly connected to the main Linux system will
be used (this avoids the need of a second system with the sole purpose of providing the
GUI).
The GUI is designed around the Qt framework and is intended to be kept also in the
future system versions. In a software requirement perspective, this implies that the Qt
framework also needs to be presented on the future in-vehicle Linux system.
5
TCP/IP to
Internet
data ceter
Gateway
process
Sockets
within Linux
Figure 2.1: All communication, both external and internal, from and within the current embed-
ded Linux system, are done via TCP connections.
There is for example one service dedicated to monitor the vehicles geographical position,
another one is monitoring the temperatures.
The internal, interprocess communication among MultiEvent and the underlaying ser-
vices is managed by a set of internal TCP-type connections. Each service communicates
with MultiEvent on two, steadily open connection sockets called "data" and "messag-
ing". When a service starts, it will register itself both on MultiEvent’s data socket and
MultiEvent’s message socket. The created TCP connections are marked as purple/blue
lines between the service and MultiEvent in figure ??.
The data socket is used by the service when it is time to report sensor information.
The traffic on this channel is thus unidirectional and asynchronous; it is always sent by
the service and always received by MultiEvent for further transmission externally.
Each service listens to the messaging socket in order to discover incoming adminis-
tration requests. If the request demands some status information, the service will answer
on this socket. Consequently, this socket transfers data in both ways: it is used to send
administrative commands and possibly also to send urgent information, such as AirBag
information, that needs to be treated in real time, without being kept in a buffer.
6
Chapter 3
Software architecture
This chapter describes the proposed software architecture and its principal components.
The new software architecture is centralized around the FreeDesktop D-Bus. All services
will henceforth use the D-Bus to export functionality and perform IPC.
1
Even though not demonstrated in this implementation, we could for example imagine a situation
where we want to allow the CoProcessor service to reach the Internet data center via the Gateway
service.
7
App App App
D-Bus
Co-Processor Gateway
Linux kernel
Hardware
Figure 3.1: Alignment of architecture on top of the Linux kernel’s abstraction stack.
8
TCP/IP to
Internet
data center
Gateway
process
D-Bus in
user space
D-Bus
Figure 3.2: D-Bus is used for the IPC between services and applications. TCP is still used to
talk to with the online data center. Compare this to the old architecture where sockets also were
used internally for IPC.
Hardware abstractions In addition to the new middleware, another service was im-
plemented to forward the functionality provided by a hardware co-processor (described
in chapter 5).
Instead of letting each application access the Linux driver directly (by the character
device file /dev/stm32) the CoProcessor service was designed for this sole task; it is
meant to be the only program that directly uses the STM32 Linux driver.
We found the advantages of this design to be:
• Hardware access can be denied/allowed for single applications (if the D-Bus daemon
is configured correctly).
• Applications use one common IPC system and will not be "polluted" with low-level
driver calls. All applications rely on API’s that are accessible in the same manner.
• The driver implementation can be simpler. Being sure that only one program uses
the driver, the driver can be implemented without taking care of thread/synchronization
problems2 .
2
This saves initial implementation time but is not recommended as a long-term solution.
9
3.3 Exported XML interface
One of the main principles behind the software architecture is to let all services export
their all functionality via the FreeDesktop D-Bus. The recommended way to define a D-
Bus interface is by XML files. [?] These XML files can later on by used to automatically
create skeleton classes to use in the implementation.
Services export their API via D-Bus through a mechanism called proxy objects. The
XML file describes the interface of these objects. Both the exporting service and the
(client) applications need to agree on the interface in order to communicate over D-Bus:
clients will only be able to use the exported functions, no more, no less. Appropriately,
the service is obliged to export all methods described by the XML.
Internet gateway All applications that need to access the Internet server have to pass
through Gateway. The following XML markup describes the D-Bus interface of Gateway.
The interface defines the exact functionality that is offered: clients can use the functions
getMsg(), uploadData(), uploadMsg() and listen for the msgArrived-signal.
If a message arrives to the channel corresponding to the one of a particular client, its
message can be retrieved using the getMsg() function. The upload-functions are called
when an application has data to transit online, for example, sensor data or messages
about the vehicle’s condition. All the messages being passed are bundled in Qt’s own
type of byte arrays, QByteArray.
10
Code 1 XML defines the interface to the Gateway service.
<node>
<interface name="com.oktalogic.gateway">
<signal name="msgArrived">
<arg name="channel" type="y" direction="out"/>
</signal>
<method name="uploadData">
<arg name="msgData" type="ay" direction="in" />
</method>
<method name="uploadMsg">
<arg name="msgData" type="ay" direction="in" />
</method>
</interface>
</node>
11
Hardware abstractions Applications in need of a specific hardware feature from the
co-processor can use the interface it exports via D-Bus. The service makes an abstraction
of the raw commands that are sent to Linux kernel. Having an extra abstraction let us
keep the driver simple; the Linux kernel driver will only need to forward the commands
(strings of bytes) that are generated by the CoProcessor service.
Consequently, when new commands are supported by the co-processor’s firmware,
CoProcessor and its XML interface needs to be updated but not the actual driver 3 . The
following XML markup describes the D-Bus interface of the CoProcessor service.
<method name="feedWatchdog">
</method>
</interface>
</node>
Our prototype version demonstrates two, very simple, example functions of the co-
processor. It was out of the scope of this project to define the exact functionality and
relation between the two microcontrollers.
3
The Linux driver framework provides us with an interface to the physical I2C system but will not
care about what kind of data is being transmitted. See chapter 5.
12
3.4 The Qt framework
The Qt framework was an explicit requirement of the graphical user interface, presented
to the vehicle’s driver. Having the framework available on target also makes other user
space applications able to benefit from the library. Previously, all programs were purely
based on operating system or POSIX primitives. The two proposed services, Gateway
and CoProcessor do both rely on Qt’s framework. Qt is designed with two essential
paradigms in mind:
• Event-driven programming
Qt includes D-Bus language bindings 4 that make it possible to use the D-Bus objects
as native C++/Qt-objects. The XML files, describing the D-Bus objects, are read by
the qdbusxml2cpp program of Qt in order to generate two skeleton classes:
Adaptor Class used by a middleware service to receive incoming D-Bus calls according
to the defined interface.
Proxy Class used by any application that wants to use the object exported by the
service. The proxy class translates native C++ method invocations into D-Bus
calls, directed to the concerned service.
Chapter 4, "Internet gateway design", describes in more detail how these paradigms
are implemented practically in a real Qt-application using D-Bus.
4
Qt’s bindings depend on libdbus. It is possible to write programs directly relying on libdbus, but it
is highly discouraged by both the Qt and the D-Bus developer communities. One developer stated that
"about 2000 lines of code" would be needed if not using a library binding (such as Qt or Glib).
13
Chapter 4
We propose a new object oriented design of the middleware between the server infrastruc-
ture and the in-vehicle Linux system. The new middleware fulfills the same functional
requirements as the former implementation but has been completely rewritten in C++.
The middleware, simply called Gateway, relies on the event-driven paradigm to avoid
unnecessary use of threads. Its interface to connected Linux applications has been sim-
plified by using Qt’s D-Bus bindings.
14
Incomming D-Bus calls
*
OktaPdu OktaLogin
SteadyBank DBusAdaptor
1 1
2
1
1 1 1 1 1
«uses»
Figure 4.1: UML diagram depicting all classes of the Gateway application. The schema is
simplified, only the classes and their relations are shown. All methods and properties are left
out.
4.1.1 D-Bus
Theses classes are handling IPC connectivity using Qt’s D-Bus bindings. Qt’s D-Bus
bindings are used to dispatch incoming messages and outbound data/messages from
connected Linux applications that need to talk to the Internet server. The D-Bus module
is thus used to handle the communication to and from the Linux applications connected
to Gateway via D-Bus. Both transfer directions are treated asynchronously. In order to
do this, the classes depend on Storage where data are located between calls.
Examples
1. When services want to upload data or messages, functions to receive the outbound
data are exported. The data are put into Storage for further transmission and
treatment by the External connection.
2. When a message arrives from the Internet server to an application behind the
gateway: in this case, a broadcast is sent over the D-Bus, similar to how it is
15
done in a "hubbed" Ethernet network. The concerned application, whose identifier
matches the receiver field of the incoming package, will then be able to retrieve
the message that has been buffered in Storage since the moment it was parsed out
from the TCP socket.
DBusAdaptor The adaptor defines what methods and properties that will be exported
to D-Bus clients. Other programs can transparently call these functions via so called
"proxy objects", as if they were native functions.
The qdbusxml2cpp tool was used to generate the skeleton of the adaptor-class. The
purpose of the adaptor is simply to export native C++ functions and make them available
via the D-Bus. The actual implementation of the functions are stored separately, in
DBusConnection.
DBusConnection This class implements all functions exported by the D-Bus adap-
tor. The reason why the class is called DBusConnection because it is also responsibil-
ity for setting up the connection to the D-Bus system deamon, running in the back-
ground of user space in the Linux system. When connecting to the deamon the name
"com.oktalogic.gateway" is given our application, according to D-Bus naming standards.
4.1.2 Storage
This module includes classes that handle Gateway’s internal bufferization of inbound and
outbound messages. The messages are stored while the services on D-Bus or the Internet
data center is not ready to treat the information.
Bank Bank handles a queue of byte streams. These byte streams are temporarily
stored within the application. Thanks to the queue, data can be received, buffered and
treated continuously.
SteadyBank SteadyBank extends Bank with the feature to backup the structure’s
data to a file stored in disk/flash memory.
If nobody is able to treat the saved data for the moment, we suppose that somebody
will do so in the future. Meanwhile data is saved.
In some rare cases, this future point in time arrives after a system reboot which is
why a backup, a serialized "mirror" of the Banks data structure is kept on flash disk.
A backup on disk is also needed in case of Internet connection break down or any
another service interruption that causes a restart of the Gateway middleware.
If the system needs to be restarted in an abrupt way, while still having stored data
(to upload), the data will once again be available after reboot by reading the backup file.
The file is easily located since its file name is kept constant.
Until data is extracted (and uploaded), we let the bank, stored both in memory and
on disk, grow in size.
16
BankManager BankManager is a central class that controls all bufferization: it holds
the banks of both inbound and outbound byte streams towards the online server infras-
tructure.
Two SteadyBanks are used for outbound streams (one to store data steams, another
one to store messages streams). The outgoing byte streams are already marked with
the channel number of the sending Linux application. This number is found among the
stream’s first bytes of data, the "stream header" 1 .
Incoming messages are not saved on the disk because the system relies on the data
center that is supposed to re-send messages in case of loss. For these messages, one
temporary Bank is given to each connected application. The number of connected appli-
cations is variable (hence the star-cardinality shown in the UML diagram).
The manager is able to form TCP packages, ready to upload, made out of the buffered
outbound data. When the timer hits a new periodic upload of data, the BankManager
instance will be called to extract outbound data from the two SteadyBanks and make
them available for upload.
17
While parsing and receiving bytes of an incoming package, this class will keep a
notation of the state. The implementation uses the enum+switch/case approach for
simplicity. Our state mainly serves the purpose of a marker of location in the current
PDU’s stream of bytes.
The objective programming pattern "the state pattern" could have been used by
putting "each state’s behavior in an own class". [?] This would have led to more flexibility
some would argue, but to the cost of much more classes. For the moment, we want the
proof-of-concept implementation to be kept simple. By having all states and transitions
handled in the same class, the code becomes easier to overview. Besides, the application’s
behavior does not change very much depending on current state (which would otherwise
have the reason of further abstraction).
Events within Gateway Events in Qt are sent through "signals" and received and
handled in methods called "slots". Behind the scenes, a Qt application implements this
mechanism using one central UNIX select() system call. [?] However, for the program-
mer’s point of view, each class appears to do its own event management locally, making
the code very straight-forward.
The gateway’s three software modules do all depend on one or several events. The
following table lists the events that can occur within the each of Gateway’s three modules:
18
• External connection
• Storage
• D-Bus
Motivation In event-driven programming, the only thread running is the thread of the
event loop, or the server thread. [?] All events are put in the main event queue and are
treated, one by one, by calling its designated event handler. The event handlers, Qt’s
"slots", are called from this server thread.
This forms a huge contrast in comparison to the legacy design where 2x+1 threads
were used 2 . The old product created a new dedicated thread with the sole purpose
of monitoring one single TCP connection. As we avoid these threads we will improve
the overall performance of the system. Managing a large number of concurrently active
threads imposed larger latency, context switching, cache misses and release jitter. [?]
Studies also show that there is typically a maximum number of threads that a given
system can support, and beyond which performance degradation occurs. [?]. When
keeping a fixed number of threads, we will not risk to reach this limit when the number
of clients to the gateway increases. Additionally, as the server thread’s stack is unwind
after the execution of an event handler we do not risk to have several growing stacks that
occupy memory.
Finally, in single-threaded applications the effort of implementation is simplified: we
do not need to rely on mechanisms for mutual exclusions and, notably, bother about
their possible synchronization issues. [?]
2
On for the data socket and one for the message socket for each connected application as well as the
normal main.
19
Chapter 5
Hardware abstractions
20
Integrated
ARM Core peripheral:
I2C controller
IMX25
main chip
SoC
Board
peripherals
GPRS modem
STM32 etc.
co-processoor
chip
SoC
Board
Figure 5.1: Schematic the hardware board and the routed chips.
A bus is characterized by having many devices connected, branched out from a com-
mon wire. When many devices are connected to the same physical wires they will all
share the same electronic voltage, thus a structured way of sharing the bus is needed.
The I2C protocol defines how this is done by a few simple principles:
Each bus has one appointed master node. The master is the only node that can
initiate a data transfer. In the proposed architecture the main processor, running Linux,
is appointed master. The master can request to either send or to retrieve data from one
of the remaining slave devices on the bus. For example, in order to retrieve one byte of
data from a slave device, the master will need to send a data request to the slave. [?]
I2C provides a protocol for sending and receiving bytes, but the interface presumes
nothing about the contents of the bytes being transferred. For example, in order to
inform the slave about what kind of data we we are looking for, a write-request (data
transmission to slave), containing a hint to the slave about what to deliver next, can be
sent before prior the read-request.
The I2C bus does only need two wires, given that the devices share a common ground
- which is the case normally the case in an embedded system. One wire, SDA, for the
serial data (and slave address information) and SCL as a periodic clock. The clock signal
will be used by the slave in order to understand when to sample a bit value from the
21
SCL
I2C I2C
SDA
master slave
Figure 5.2: Schematic view of the two wires of the I2C bus.
Hardware configuration The development took place by connecting the I2C pins of
an IMX25 PDK board to the corresponding pins of a STM32 Discovery board. Since the
IMX25 PDK includes several components that are already using the bus, this board did
already include the needed pull-up resistors (they have a voltage of 3.3V at idle). Using
a voltmeter we concluded that PIN connectors marked I2C SDA and SCL were indeed
shared among many board components.
22
reference manual and comments in the library code we got an abstract view of how the
I2C controller functions. [?]
1. Register itself with the kernel (to later be able to create a file found under the
/dev).
2. Set up pointers to C-functions callbacks, called when a Linux program uses /dev/stm32.
The driver decides what data that are actually communicated using I2C. The com-
municated data can be seen as a layer on top, and can thus can be anything that we
decide it to be, as long as the both master and slave agree on the protocol. In our im-
plementation the driver is simply forwarding the same byte strings that it was fed with
from user space.
23
User
Space
Kernel
I2C Chip Drivers
I2C Core
I2C Bus Drivers
Hardware: I2C controller
Figure 5.3: Bus drivers and chip drivers are developed separately and then glued together by
Linux kernel’s I2C core.
Binding driver to device The USB, PCI and I2C subsystems all share the same
binding model for drivers:
• The bus controller in hardware is controlled and monitored by one bus driver. Slave
devices, attached to the bus, are represented by chip drivers.
• All chip drivers need to register with the I2C core.
• The core calls the chip driver’s probe()-function to ask the driver if it is able to
control a newly found slave device. [?]
The bus driver is responsible for controlling the low level signaling (addresses, data
and clock information) performed by the I2C controller of the microprocessor. Further-
more, several chip drivers are developed, one for each slave device connected to the bus.
This is similar to how each device connected to the PCI or USB bus have one dedicated
driver, while there is always only one physical bus and bus controller. [?]
In the IMX-series of processes FreeScale has already provided a bus driver for their
customized Linux kernel. Using this kernel, we could thus focus on the development
implement an I2C chip driver as an abstraction of our STM32 co-processor. When
talking about a Linux I2C driver in general, one is actually often referring to the I2C
chip driver.
24
Upon compilation of the Linux kernel for an embedded system it is possible to define
a mapping between I2C addresses and name identifiers. In order to for an I2C chip driver
to be accepted (linked in at runtime) by the Linux kernel, the drivers name identifier
needs to match one of the names defined during kernel compilation.
One might argue that this approach imposes a certain rigidity to the software: once
the kernel has been compiled, no names/address-mappings can be modified without a
complete recompilation. This can indeed be a problem, but in cases of embedded systems
the hardware is usually fixed and we will know what devices (and their addresses) that
are to be connected on the product. Nevertheless, this was the approach taken in this
design.
To make the kernel aware of the connected STM32 chip, it was given the name
stm32-i2c and the address 0x0B, in the kernel source code that describes the hardware
configuration, before compiling the kernel. For the IMX class of processors, information
about the system’s hardware configuration is added to certain files of the Linux kernel
source directory /arch/arm/mach-imx/. [?]
25
5.5 Implementing an I2C slave firmware
Both the IMX25 and STM32 are equipped with similar I2C controllers in hardware
that can be used to get information about ongoing bus activity and state. However,
the STM32 is a microcontroller that is much simpler than the IMX25 and will not run
standard operating system: the STM32 firmware is supposed to run on "bare metal".
Consequently, when not having an operating system all peripherals need to be config-
ured manually: the STM32 firmware needs to both manage the I2C controller (as Linux’s
bus driver does) and interpret received data (as Linux’s chip driver does).
26
serve requests from the USB host (the PC-side). [?]
In the STM32 microprocessor, two interrupt channels from the I2C sub system are
connected to the Cortex processor core. The channels are masked by default so we needed
to unmask them by setting the bits ITEVFEN, for bus events and ITERREN, for error events.
[?]
Bus events One essential bus event is when the master has called for the slave’s
address. The hardware controller handles the low level acknowledgement on the wire but
the software interrupt is needed in order to prepare the slave’s upcoming transmission
or retrievement of bytes.
If the status register reports that the master has requested a transmission of bytes
the slave will immediately need fill the data register with the first byte. Subsequent bytes
are put into the data register once the master has ACK’ed the first transmission.
Otherwise, if the request was to receive bytes from the master, the software will wait
a second bus interrupt that occurs once the I2C controller has received a byte. Upon
this event, our firmware copies the byte of the data register and goes on working until
the event happens again.
Error events An error interrupt is signaled upon "ACK failure". The is event does
not necessarily mean a failure; it is also set when the master does not want to receive
more data. When the master has received the last byte it omits the acknowledgement
which was interpreted as a failure to the slave. This bit needs to be cleared in the error
handler before new requests could be treated.
27
Chapter 6
We have added two middleware services on top of the Linux kernel to provide a new
software architecture for telematics devices. One middleware was developed to commu-
nicate with the online data center, the second service exports features from a co-operating
microprocessor. Both middlewares have been built from scratch using the Qt framework.
Since middleware services need to export functionality to applications, one of the key
components in a software architecture is the IPC system. We chose to use D-Bus because
of its good bindings in Qt that helped initial development.
In the former product, TCP sockets were used to export features - each IPC con-
nections required a pair of sockets as communication end-points. By using the language
bindings found in the Qt framework, D-Bus calls can be treated like any other Qt event,
eliminating the overhead of threads that monitor sockets. In an embedded context with a
microprocessor with one single core, no performance advantages can be drawn from using
parallel threads. When we no longer need to monitor several sockets the implementation
can be made much simpler.
D-Bus has built-in support for exporting functionality from one service to many
applications. The proof-of-concept implementation shows how D-Bus can be used as a
way to do inter process communication, between these handcrafted middleware services
and the users’ applications. Using an IPC layer such as D-Bus is probably more secure
than using bare TCP sockets since the intermediate daemon only dispatches messages
that are addressed to methods actually being exported. A program’s external interface
can thus not be flooded with nonsense data which otherwise is a risk with TCP streams.
Clearly, the functions being called still need to be written to deal with ambiguous data
input.
One obvious design disadvantage of the system would be the risk of a failed D-Bus
daemon. Relying on the D-Bus daemon indeed adds another possible point of failure to
the telematics system, in addition to the Linux kernel itself and the various (middleware)
services. Meanwhile, the D-Bus daemon needs to be configured manually to only accept
traffic from permitted programs.
Extending Linux by adding middleware services is a concept that is also found in
Google Android. An in-vehicle infotainment system with similar functionality could
28
indeed have been built by adding a telematics middlware to Android, as studied by the
authors of [?]. Our conclusion is, however, that building a system from scratch, around
a standard "vanilla" Linux, could be easier simply because fewer components need to
be ported. Building a system from scratch, on top of a vanilla Linux kernel, gives more
freedom to the developer, as each and every software component can be chosen freely.
More freedom gives bigger open source communities to turn to for help. This free-
dom also makes it easier to find software that has already been tested with a similar
hardware configuration. In contrary, when building a Linux system from scratch, due to
time limitations, often only the most crucial components are actually implemented. If
extendability in terms of third party applications is required, it could possibly be easier
to port a more complete solution such as MeGoo or Android.
29
Appendix A
Before settling down for a new Linux based software architecture it was useful to study
ways of communication between the internal processes. This chapter presents a study
on D-Bus, the de facto standard of interprocess communication (IPC) on desktop Linux
setups and compares it traditional sockets.
Both sockets and D-Bus allows communication between software processes, not nec-
essarily written in the same programming language. While not being a manifested re-
quirement this pays the way for more flexible implementation.
While D-Bus still is the IPC standard method in embedded systems built on MeeGo,
other distributions has taken other approaches. One such example is Google Android’s
"Binder". Binder will not be studied further in this papers because it is highly coupled
to Android systems and is only supposed to work within that context. D-Bus operates
entirely in user space, in contrast to for example Andorid’s Binder, which is incorporated
into the Linux kernel.
30
libraries nor services to use them. By relying on kernel space mechanisms for passing the
messages, the number of context switches are naturally few.
A.3 Criticism
Using D-Bus involves adding more dependencies to the system. Furthermore, dispatching
D-Bus messages requires significantly more overhead than in IPC methods built into the
kernel. As messages are dispatched by an user space application, more memory copies
and context switches are involved. The counterargument would be that this additional
cost is compensated by more reliable and controlled transfers.
There have been discussions about to move the D-Bus into kernel space to improve
responsiveness. This is generally a discouraged idea since it is conflicting against the idea
of keeping the kernel as small as possible.
A.4 Requirements
In order to get information on the resource requirements of D-Bus and its Qt bind-
ings, tests were conducted in QEMU simulating a target hardware. The tested Linux
environment was built by BuildRoot and compiled for ARM using CodeSourcery’s cross-
compiler. The idea was to use an image with a configuration equal to the one that later
will be deployed on the real hardware.
In order to send a message to D-Bus, the client application would need to use the
libdbus library. It is however recommended to use a higher-level binding implemented
31
such as the one of the Qt framework. A language binding implies an extra dependency
but is less error-prone to use. This chapter explains how to study the additional cost in
disk/memory/cpu usage and footprint of including a D-Bus into an embedded system.
32
When several processes share dynamic libraries, when a new service is added, thanks
to virtual memory, only its new code and data (ie. the writable parts of the program
and its libraries), will eventually be allocated memory. The paging is done progressively
because Linux uses copy-on-write.
This makes VSZ and RSS less representative as they count the pages/frames of the
shared libraries several times. We are interested in the memory footprint of a complete D-
Bus setup, including the daemon as well as the libraries shared by the applications using
the bus. To get accurate information about the number of frames actually occupied
in memory we would need a way to summarize the frames (marked as RSS) for both
processes but only counting shared code and data from library ones.
33
Appendix B
B.0.5 Kernel
The kernel is responsible for maintaining all the abstractions of the operating system,
including virtual memory, processes and other hardware abstractions. The kernel needs
to be configured to support the filesystem used by the embedded device. [?]
Within the kernel layer, Linux is composed of five major subsystems: the process
scheduler (sched), the memory manager (mm), the virtual file system (vfs), the network
interface (net), and the inter-process communication subsytem (ipc). Conceptually each
subsystem is an independent component of the Linux kernel even though there is some
interdependence among the five subsystems in the actual implementation. [?]
1
Actually, the very first code to be executed is found in a read-only memory called the boot strap.
The boot strap locates the boot loader program in an external memory according to a configuration set
by hardware pins on the board.
34
B.0.6 Libraries and development frameworks
While the Linux kernel indeed offers a wide API, the abstraction level is much lower
than what is expected by the application developers. For example, to ease development
of visually appealing user applications confirming to upcoming standards of infotainment
systems further layers of abstraction are added. A supporting software layer does often
help building graphical interfaces but can also incorporate other functional domains to
form a complete a software development framework. Such frameworks make more as-
sumptions and are thus easier to use. [?]
35
Appendix C
Methods of development
This chapter highlights the embedded Linux traditional development setup. We present
some freely available tools to build and debug open source code that is designed to run
on embedded microcontrollers.
C.1 Cross-compiling
Software development of embedded Linux systems is performed at a host work computer.
Since the processor architecture of the host and the target differs, a cross-compiler is used:
the produced machine code will not be of the same kind as the one of the host.
Developing embedded Linux typically involves cross compiling, notably when devel-
oping applications to run on ARM or another non x86-based hardware platform. The
cross-compiled system is then debugged either on a physical target, similar to the final
hardware, or in a hardware simulator run on the PC.
Cross-compilers and the tools needed in a cross development environments are freely
available on the Internet. This saves the developer from starting the project by compiling
the toolchain from source - a tedious task that involves much configuration. [?] One
example of a free toolchain is CodeSourcery’s ARM Lite++ that also contains a matching
GNU C library. This toolchain was for all ARM-based development in this project.
A cross-compiler is compiled against a certain C library, which is why the target’s
C library must be chosen and at same time as building or selecting, the tool chain 1 .
[?] Changing one of the two at a later point is not recommended. A swap of toolchain
and thus the chosen C library could imply a recompilation of all the systems’ software
components 2 . Furthermore, all binaries, both kernel and user space, in the system must
1
Actually, GCC only needs the C library when it compiles the C++ language. Since the C library
needs to be cross compiled itself we need a preliminary "bootstrap" cross compiler to do this. Hence, a
first compiler us built with support for C only, and a full compiler is built once the C library is available.
[?]
2
In a desktop Linux setup, ldconfig is used to keep symbolic links updated. For example, /lib/ld-
linux.so.2 points to /lib/i386-linux.gnu/ld-2.13.so in Ubuntu. But it is still considered dangerous to
upgrade glibc.
36
be compiled with the same ABI. The ABI is a convention that covers, for example how
arguments are passed to functions on assembly level. Nowadays, everybody uses EABI.
[?] All of these compatibility problems are taken care of when using a prebuilt tool chain.
37
In order for GDB to be able to follow execution of code it needs information about
the where written source code is put in RAM of target. When a "bare metal" software
is compiled, two files usually are generated for this purpose:
The debug information includes the symbol table and information about where the
compiled C functions’ (machine) instructions are put into RAM of target. This mapping
is needed by GDB to give an interactive view of which lines in of the source that is
currently executed by the target. The ELF-file’s debug information is used by GDB to
match the program counter’s value, given "live" by the JTAG, with an exact location in
the C source code.
With OpenOCD runing as gdbserver, the brigde between GDB and the JTAG hard-
ware, we could successfully perform traditional breakpoint-debugging on the most low-
level code of the microcontroller. Notably, OpenOCD proved to be very useful while
tuning U-Boot, the Linux boot loader used in this project. In principle, the same meth-
ods can also be used to debug the Linux kernel, or any piece of code that runs in an
environment where the "on target" gdbserver is not availble. OpenOCD was also used
successfully to flash a sample firmware for the STM32 microcontroller.
Handling re-location of code The first code the microcontroller executes is called
the bootstrap and is found in a fixed ROM memory. The bootstrap is never touched and
is made by the microprocesor vendor. It is responsible of locating the first piece software
(in our case U-Boot) and start running it. According to physical PIN-configurations,
the bootstrap code will either go look for a program in NOR flash (to do "execution in
place", XIP) or from another source, a NAND flash for example. If the program is stored
in a memory that does not support XIP, it is first copied to RAM.
Once the bootstrap has located U-Boot it passes control the U-Boot software. The
main purpose of U-Boot is to locate the Linux kernel in flash and pass it control. Working
with the flash memory is easier if U-Boot runs from RAM. This is the reason why U-
Boot relocates itself to RAM. All code, functions as well as variables will be given new
38
addresses, a change that the GDB debugger needs to be informed of. GBD needs to be
configured differently if code to be debugged is run from flash or has been relocated to
RAM as seen in Code ?? and Code ??.
Code 5 .gdbinit example. Before relocation, execution takes place directly from flash so
the addresses found in the ELF file can be used by GDB.
# Connect to OpenOCD
target remote localhost:3333
When debugging code from ROM, only hardware breakpoints can be used (software
interrupts requires write access to insert BKPT instructions). The ARM9-based micropro-
cessors do only have two hardware breakpoints [?]. This limitation forces us to manually
disable a breakpoint once being hit. GDB’s thbreak command is used to set temporary
hardware breakpoints that are automatically disabled and thus being freed to break at
another address.
Code 6 After relocation, GDB needs to be informed about the code’s new placement
in RAM. The relocation address can be read out from U-Boot’s internal data structure
using GDB commands.
...
(gdb) thbreak board.c:418
(gdb) print/x addr
$3 = 0x83fbb000
(gdb) symbol-file
(y or n) y
(gdb) add-symbol-file u-boot 0x83fbb000
(y or n) y
Reading symbols from u-boot...done.
(gdb) thbreak board_init_r
After relocation the .text and .data-segments have been copied from flash to RAM.
GDB can be informed of the code’s new location by first resetting the symbol table with
39
symbol-table and then re-read it using the new base address found by print/x addr.
# JTAG speed
adapter_khz 1000
We reset and halt the processor once GDB connects to let GDB set breakpoints on
the very beginning of the execution.
40
The following paragraphs describe the most well-known of the freely available build
tools environments that were considered when settling down for a new build system.
BuildRoot Buildroot was used in this project and is a free build tool that is built
upon Make and the Makefile format. It can build its own tool chain or use an existing
one, such as the one from CodeSourcery, to create ready-to-use target file system im-
age. The "ncurses"-based configuration menus are also linked the standard Linux kernel
configuration.
Yocto The Yocto Project, announced in late 2010 by Intel is an attempt by hardware
vendors to unit with embedded Linux developers in creating "custom" Linux distribu-
tions. In similarity to BuildRoot, Yocto can generate a complete RootFS image but we
believe that, in order to do so, it requires more configuration and is by far not as stright
forward as in Buildroot.
41
Bibliography
[2] Elisabeth Freeman, Eric Freeman, Bert Bates, Kathy Sierra, and Elisabeth Robson.
Head First Design Patterns. O’Reilly Media, 2004.
[3] Thiemo Voigt Adam Dunkels, Oliver Schimidt and Muneeb Ali. Protothreads: Sim-
plifying Event Driven Programming of Memory-Constrained Embedded System.
2006.
[5] Andy Wellings and MinSeong Kim. Asynchronous Event Handling and Safety Crit-
ical Java. JTRES 10, pages 53–62, August 2010.
[6] Jean-Marc Irazabal and Steve Blozis. AN10216-01, The I2C Manual, March 2003.
[8] Alessandro Rubini Jonathan Corbet and Greg Kroah-Hartman. Linux Device
Drivers, 3rd Edition. O’Reilly Media, 2005.
[9] Kernel developers. Linux kernel source code documentation on I2C, 2011.
[10] Jan Axelson. USB Mass Storage: Designing and Programming Devices and Embed-
ded Hosts. Lakeview Research LLC, 2006.
[12] Peter B. Galvin Abraham Silberschatz and Greg Gagne. Operating System Concepts,
Seventh Edition. John Wiley & Sons, 2004.
[13] Andreas Aardal Hanssen. Qt’s Network Module. DevDays2007, PowerPoint presen-
tation, 2007.
42
[14] FreeDesktop.org. D-bus specification. http://dbus.freedesktop.org/doc/
dbus-specification.html, December 2011.
[18] Gilad Ben-Yossef Karim Yaghmour, Jon Masters and Philippe Gerum. Building
Embedded Linux Systems. O’Reilly Media, 2008.
43