92
TRAINEESHIP – PROJECT Installation and Implementation of Xenomai on an Industrial Component and Porting a Driver for an Industrial IO-Card to the Xenomai Infrastructure by Ondrej Cevan Submitted in partial satisfaction of the requirements for the degree of BACHELOR of COMPUTER ENGINEERING at the Vienna University of Technology Advisors: Professor Dr. Uwe Schmidtmann and Dipl.-Inf. Bodo Wenker (Institute I2AR, Emden University of Applied Sciences) Univ.Ass. Dipl.-Ing. Dr.techn. Wilfried Elmenreich (Institute of Computer Engineering, Vienna University of Technology) July-August 2006

57842309 Xenomai Implementation

Embed Size (px)

DESCRIPTION

mtech project

Citation preview

Page 1: 57842309 Xenomai Implementation

TRAINEESHIP – PROJECT

Installation and Implementation ofXenomai on an Industrial Componentand Porting a Driver for an IndustrialIO-Card to the Xenomai Infrastructure

by

Ondrej Cevan

Submitted in partial satisfaction of the requirements for thedegree of

BACHELOR of COMPUTER ENGINEERING

at the

Vienna University of Technology

Advisors:

Professor Dr. Uwe Schmidtmann and Dipl.-Inf. Bodo Wenker

(Institute I2AR, Emden University of Applied Sciences)

Univ.Ass. Dipl.-Ing. Dr.techn. Wilfried Elmenreich

(Institute of Computer Engineering, Vienna University of Technology)

July-August 2006

Page 2: 57842309 Xenomai Implementation

Installation and Implementation ofXenomai on an Industrial Componentand Porting a Driver for an IndustrialIO-Card to the Xenomai Infrastructure

Xenomai is a new real-time operating system emulation framework basedon GNU/Linux. It was designed with the goal to help application designersusing traditional and proprietary real-time operating systems to move assmoothly as possible to a GNU/Linux based execution environment.

The aim of this work is to establish a Xenomai environment on an indus-trial computer from Siemens and write a simple device driver for the digitalI/O card CPCI-EA221 using the Real-Time Driver Model (RTDM) whichis integrated in Xenomai. To achieve this goal, first a simple real-time user-space task under Xenomai was created. As a second step a Linux devicedriver was written and later adapted to the Xenomai-RTDM features.

The Fachhochschule (The University of Applied Sciences) in Emden,Germany, initiated this work because of the increasing interest on Xenomaireal-time environment in the industry. This work is supposed to be usedfor further research and working on Xenomai at the university mainly inthe automation sphere.

i

Page 3: 57842309 Xenomai Implementation

Contents1 Introduction 1

1.1 Xenomai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.2 Workplace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.3 Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.4 Structure of the Project Report . . . . . . . . . . . . . . . . . . 5

2 Xenomai – an Installation Guide 62.1 System Requirements . . . . . . . . . . . . . . . . . . . . . . . . 6

2.1.1 Preinstallation Steps . . . . . . . . . . . . . . . . . . . . 62.2 Downloading and Unpacking the Sources . . . . . . . . . . . . . 82.3 Compiling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92.4 Setting up Bootloader and Testing . . . . . . . . . . . . . . . . 12

2.4.1 Configuring LILO . . . . . . . . . . . . . . . . . . . . . . 122.4.2 Configuring GRUB . . . . . . . . . . . . . . . . . . . . . 132.4.3 Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142.4.4 Error Handling . . . . . . . . . . . . . . . . . . . . . . . 15

3 Xenomai- First Application 19

4 Linux Driver 214.1 Programming the Driver . . . . . . . . . . . . . . . . . . . . . . 214.2 API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

4.2.1 IOCTL Code and Argument Structure: . . . . . . . . . . 244.2.2 Examples: . . . . . . . . . . . . . . . . . . . . . . . . . . 24

4.3 Test Application . . . . . . . . . . . . . . . . . . . . . . . . . . 25

5 Xenomai – RTDM Driver 265.1 Programming the RTDM Driver . . . . . . . . . . . . . . . . . . 265.2 API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

5.2.1 Examples: . . . . . . . . . . . . . . . . . . . . . . . . . . 285.3 Test Application . . . . . . . . . . . . . . . . . . . . . . . . . . 28

6 Conclusion 306.1 Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306.2 Outlook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

ii

Page 4: 57842309 Xenomai Implementation

7 Listings 327.1 Xenomai – First Application . . . . . . . . . . . . . . . . . . . . 33

7.1.1 timer 1.c . . . . . . . . . . . . . . . . . . . . . . . . . . . 337.1.2 timer 2.c . . . . . . . . . . . . . . . . . . . . . . . . . . . 367.1.3 Makefile . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

7.2 Linux Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407.2.1 cpci ea221 driver.c . . . . . . . . . . . . . . . . . . . . . 407.2.2 cpci ea221 driver.h . . . . . . . . . . . . . . . . . . . . . 517.2.3 test led.c . . . . . . . . . . . . . . . . . . . . . . . . . . . 547.2.4 test led.h . . . . . . . . . . . . . . . . . . . . . . . . . . 577.2.5 Makefile . . . . . . . . . . . . . . . . . . . . . . . . . . . 587.2.6 cpci ea221 load.sh . . . . . . . . . . . . . . . . . . . . . . 607.2.7 cpci ea221 unload.sh . . . . . . . . . . . . . . . . . . . . 62

7.3 Xenomai- RTDM Driver . . . . . . . . . . . . . . . . . . . . . . 637.3.1 xen cpciea221 driver.c . . . . . . . . . . . . . . . . . . . 637.3.2 xen cpciea221 driver.h . . . . . . . . . . . . . . . . . . . 757.3.3 xen led timer.c . . . . . . . . . . . . . . . . . . . . . . . 777.3.4 xen led timer.h . . . . . . . . . . . . . . . . . . . . . . . 837.3.5 Makefile . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

References 87

iii

Page 5: 57842309 Xenomai Implementation

1 Introduction

This project report is intended to be used as a manual for establishing Xeno-mai environment which runs together with Linux distribution and for writingfirst Linux device driver and Xenomai device driver for CPCI1 cards. We chosethe way of writing this report as a manual because this project should explaininstalling the Xenomai environment and make the step-in phase for program-ming device drivers mainly in Xenomai smoother. The following sections willbriefly introduce the major facts on our working environment and then the nextchapters will show you the way to the Xenomai driver for a CPCI card.

1a standard based on PCI, see section 1.2

Figure 1.1: the workplace

1

Page 6: 57842309 Xenomai Implementation

1 Introduction 1.1 Xenomai

1.1 Xenomai

There are different real-time operating systems (RTOS) these days using dif-ferent APIs, but one can find common features and behaviors among manyof them. Major embedded RTOS, such as VRTX, VxWorks, pSOS+ and afew others, have implemented a real-time kernel behavior which has become ade facto standard. Xenomai uses these similarities to build a nucleus offeringset of generic services. These services build an architecture-neutral abstrac-tion layer, that provides the foundation for developing emulation modules ofreal-time application programming interfaces, which mimic the correspondingreal-time kernel APIs. One can set the Xenomai’s emulators or APIs for real-time operating systems at configuring the kernel in the kernel menu [Real-timesub-system->Interfaces ]. We will come to it in the next chapter. Of coursethere is also the possibility to use the Xenomai’s native API, which was usedat programming the applications for this project. For a detailed report on im-plementing a RTOS emulation framework on GNU/Linux see the paper [Ger04].The aim which Xenomai wants to achieve with this approach, using the simi-larities among traditional RTOS, is to offer the application designers a smoothand comfortable way for migrating the real-time applications from traditionalRTOS to GNU/Linux.

To make Xenomai’s tasks hard real-time in GNU/Linux a Real-Time Appli-cation Interface (RTAI) [rta] co-kernel is used. It allows to run real-time tasksseamlessly aside of the hosting GNU/Linux system while the tasks of the ’reg-ular’ Linux kernel can be seen as running in a low-priority mode. The RTAIco-kernel shares hardware interrupts and system-originated events like trapsand faults with the Linux kernel using the Adaptive Domain Environment forOperating Systems (Adeos)2 [ade, Ger05] virtualization layer, which in turnensures RTAI low interrupt latencies.

You can find a lot of useful information on Xenomai on the webpages [xenb,xena, Unib] and in the Xenomai source directory3.

1.2 Workplace

This traineeship project was done at The University of Applied Sciences inEmden, Germany, in July- August 2006. At our disposal we had a Siemensindustrial computer Sicomp SMP16, figure 1.2, and a Siemens digital I/O cardCPCI-EA221, figure 1.3.

2Adeos can be loaded as a Linux module to allow another OS to run along with it.3it depends where you installed the sources, we had it in /usr/src/xenomai-2.2.0/doc

2

Page 7: 57842309 Xenomai Implementation

1 Introduction 1.2 Workplace

Figure 1.2: SICOMP-SMP16 c©Siemens

Figure 1.3: CPCI-EA221 c©Siemens

The industrial microcomputer SICOMP [Sieb] is a high performance modu-lar, open board architecture system that allows hardware systems to be tailoredindividually to a task and combined with customer developments. Our Sicomphad a SMP16 CPCI central processing unit (CPU) with the 1266MHz powerand a CPCI bus interface. The hardware is PC compatible and has been ex-panded with additional safety functions.

Compact PCI (CPCI) is a high-performace standard based upon peripheralcomponent interconnect (PCI) technology and is a norm of the PICMG (PCI

3

Page 8: 57842309 Xenomai Implementation

1 Introduction 1.3 Source Code

Figure 1.4: input/output control box

Industrial Computer Manufactures Group). Because it is based on PCI, devicedrivers for CPCI can use the same design approach as normally used for PCIdevice drivers.

The CPCI-EA221 card is a plug-in computer card. It has 16 input and 16output pins and 4 control LEDs on the front panel. The control LEDs signalthe state of the input/output pins, depending on the configuration setting. Thepossible configurations and other technical information on the device can befound in [Siea]. To test the inputs and outputs of the card we used a deviceconnected to the port of the card, picture 1.4. It was also connected to a 24Vsource as the input voltage of the card is that high. On the leds of this testdevice we could observe the states of all of the 16 output pins and set the stateof every of the input pin with the devices’ buttons.

On our Sicomp computer a Linux distribution, Debian GNU/Linux, release3.1 (sarge), with Linux kernel 2.6.17.6 was installed and run together withXenomai. The way how to establish the Linux environment together with theXenomai is described in the next chapter 2.

1.3 Source Code

Care has been taken to comment the source code to make reading the codemore comfortable, however, for better and deeper understanding of the used

4

Page 9: 57842309 Xenomai Implementation

1 Introduction 1.4 Structure of the Project Report

functions one should read the relevant APIs. It would make no sense to writedetailed description of the used operations into the source code nor into thisproject report.

One can see some free places in the code that should be filled before thedriver can be said to be complete. When we found such places we wrote thereTODO and noted what we think there should be done. It is important toremark that the code listed at the end of this project report does not have theambition to be complete and ready for distribution. The basic aim was to getthe understanding of how to write simple device drivers for Xenomai and todocument that way, so that this document can be used as a starting point forfurther developing of a driver for Xenomai. We wrote the Linux device driverto obtain basic knowledge needed for writing drivers also under Xenomai. Wedidn’t get into its precise code writing. Nevertheless, the code is functional andwe think and hope that it, together with the documentation, will be helpfullsource for future developers who want to start programming device driversunder Xenomai.

1.4 Structure of the Project Report

The project report is structured as follows: The Xenomai environment will beestablished in the chapter 2 and afterwards the chapter 3 shows two simpleapplications using the environment. On the base of these two codes the basicprogramming approach used in Xenomai is explained. The following chapter 4concerns with the programming of Linux device driver for the CPCI-EA221card to get the basic knowledge on programming PCI/CPCI driver modules.With that knowledge we can start programming Xenomai driver module in thechapter 5. The following chapter 6 summarizes the key results of the presentedwork and gives an outlook on what can be expected from future developmentand research in this area. Finally, the project report ends with the chapter 7in which one can find the source code written and used in the scope of thisproject. All the documents, manuals, papers etc applied in this project arelisted at the end of this project report.

5

Page 10: 57842309 Xenomai Implementation

2 Xenomai – an Installation Guide

This section describes the step-by-step approach we needed to go throughto install Xenomai. To make the installation guide more schematic someLinux-Debian commands are present since Debian Linux was used as an en-vironment to prepare the with Xenomai extended Linux kernel. We usedLinux kernel 2.6.17.6 and Xenomai version 2.2.0. To see other installationguides visit [Unib, Xenomai 2.1, 2.2 Installation Guide], [Len] and look at thereadme.txt file in the Xenomai installation package.

2.1 System Requirements

This installation guide assumes that the user is running a functionalGNU/Linux environment, best with a Debian or a Debian-like (e.g. Ubuntu)distribution. The user should be also familiar with the basic Linux commandsand working in shell, because the whole installation process explained is real-ized in a terminal using CLI1. Since we will need the existing kernel config fileof the Linux OS we are working in, it is necessary to ensure that the recentkernel version is 2.6.xx.yy. The best when the version is as close as possible tothe kernel we are going to compile and to merge with the Xenomai extension.

2.1.1 Preinstallation Steps

Check the Linux kernel version you are currently using:

$ uname -r

2.6.12-10-386

To search for the latest available kernel image for your distribution using apt:

$ su #change to root, super-user mode

Password:

# apt-cache search ^kernel-image-2.6

1Command Line Interface

6

Page 11: 57842309 Xenomai Implementation

2 Xenomai – an Installation Guide 2.1 System Requirements

It is worth to checkup if /etc/apt/sources.list has a good source wherethese images can be found, e.g. add a line deb ftp://ftp.de.debian.org/debian/stable main contrib to get the source to the stable packages of a Debian distri-bution. To get more help on configuring apt and its sources.list see man-pagesor help on internet.

If the list you will get contains a kernel image with a newer version numberthan your current kernel, we recommend to install it. Take care to check thatthe kernel image is suitable for your CPU architecture. If you are not sureabout the CPU of your computer, you can get the info with the command cat/proc/cpuinfo.

To install new kernel image:

# apt-get install kernel-image-(version-number)

OR

# apt-get upgrade ## your old recent kernel will be updated with the

## new available kernel, also if you have some older

## programs in your distribution they will be updated,

## see man pages of apt-get for more info.

The bootloader GRUB or LILO should be configured automatically by theprogram apt-get and offer you the possibility at boot-up to return back to theprevious kernel version in case the new one will not work. We recommend thatyou check up that your bootloader is configured properly. See GRUB or LILOdocumentation for more info.

It is also important before starting with the compilation to check out if youhave the correct version of the compiler. Make sure you have gcc-3.2 ornewer. Older versions can have problems compiling newer versions of Linuxkernel. First find the path to it and then check the symbolic link gcc if it isshowing on the right compiler. If not, download the new one, and make a linkto it.

# whereis gcc

gcc: /usr/bin/gcc /usr/lib/gcc

# cd /usr/bin/

# ls -l | grep gcc

gcc gcc-4.0 gcc-3.0 ...

# ls -l gcc

lrwxrwxrwx 1 root root 7 Jan 23 17:23 gcc -> gcc-4.0

In this case is the compiler right.If your compiler is old:

7

Page 12: 57842309 Xenomai Implementation

2 Xenomai – an Installation Guide2.2 Downloading and Unpacking the Sources

# apt-cache search ^gcc

# apt-get install gcc-4.0

# cd /usr/bin/

# ls -l | grep gcc

lrwxrwxrwx 1 root root 7 Jan 23 17:23 gcc -> gcc-3.0

-rwxr-xr-x 1 root root 89208 Oct 1 2005 gcc-4.0

-rwxr-xr-x 1 root root 89208 Oct 1 2005 gcc-3.0

...

# ln -s gcc-4.0 gcc

# ls -l gcc

lrwxrwxrwx 1 root root 7 Jan 23 17:25 gcc -> gcc-4.0

One more sentence to the compiler: Do not compile Linux kernel and Xeno-mai with different compilers or different compiler’s versions, it could makeLinux kernel and Xenomai incompatible.

2.2 Downloading and Unpacking the Sources

For establishing an Xenomai environment we need three files to download:

• the Linux kernel source code

• the Xenomai source code

• and the Adeos ipipe patch, since Xenomai relies on the Adeos microkernelwhich is implemented as a patch.

Download the latest stable version of Linux kernel source, linux-2.6.xx.yy.tar.gz, from http://kernel.org/ and save it to /usr/src. The mainpage [kerb] informs on the latest stable version.Next we will need the latest stable version of Xenomai. Download it fromhttp://download.gna.org/xenomai/ and save it to /usr/src.Finally, the Adeos ipipe patch must be downloaded from http://download.

gna.org/adeos/ and also saved to /usr/src/. Normally in DebianGNU/Linux was the Adeos patch provided in the package kernel-patch-adeos,but this package has not been updated for kernel versions more recent than2.6.11.

# lftp

lftp :~> lftp http://www.kernel.org/pub/linux/kernel/v2.6/

lftp :~> get linux-2.6.17.6.tar.bz2 -o /usr/src/

8

Page 13: 57842309 Xenomai Implementation

2 Xenomai – an Installation Guide 2.3 Compiling

lftp :~> lftp http://download.gna.org/xenomai/stable

lftp :~> get xenomai-2.2.0.tar.bz2 -o /usr/src/

lftp :~> lftp http://download.gna.org/adeos/patches/v2.6/i386

lftp :~> get adeos-ipipe-2.6.17-i386-1.3-08.patch -o /usr/src/

lftp :~> exit

#

Note that the version numbers can differ, but take care that the first threeversion numbers of the patch do match with the first three version numbers ofthe kernel. We recommend to use the latest stable versions.

To unpack the sources:

# cd /usr/src/

# tar -xjf linux-2.6.17.6.tar.bz2

# tar -xjf xenomai-2.2.0.tar.bz2

2.3 Compiling

Before you start compiling you will need to run a Xenomai script which informsthe Linux kernel about Xenomai and the Adeos ipipe patch. The script willinstall Adeos and link Xenomai with Linux kernel.

# cd /usr/src/xenomai-2.2.0/scripts

# ./prepare-kernel.sh

The script will ask you for a path to the Linux kernel source and Adeospatch. Make sure you specify the right ones. In our case:

Linux path: /usr/src/linux-2.6.17.6

Adeos path: /usr/src/adeos-ipipe-2.6.17-i386-1.3-08.patch

Now we will need the existing config file of the recently running kernel. Youshould find it in /boot/ and copy it to /usr/src/linux-2.6.17.6/

There are more possibilities how to configure the kernel before compiling,one of them is with the command make menuconfig or you can also editdirectly the file, e.g. vi .config which can be practical if you are searchingfor some specific attributes. With make menuconfig you will jump into atext-based menu-style configurator, where you can descend into the menus

9

Page 14: 57842309 Xenomai Implementation

2 Xenomai – an Installation Guide 2.3 Compiling

and change the configuration options which interest you.

IMPORTANT: if you are a newcomer in the kernel configuration and youdon’t want to spend lot of time with recompiling the kernel it is wise notto change anything in the configuration. The result will be that the kernelwill have many modules you will maybe never need and therefore it will bebigger and need more time to compile, also higher latencies in the real-timeenvironment of Xenomai could occur. The good thing is that it should runjust after one compilation without freezing and kernel panic at boot-up.

As a newcomer do just this in the configuration:

• In the menuconfig main-menu you should see a sub-menu Real-time sub-system, enter it and make sure Xenomai is marked for installing as abuild-in, meaning that it will be build directly into the kernel.

• In Real-time sub-system –> Interfaces make sure Native API is chosenfor installing.

• Make sure you don’t have SMP enabled if you are on a UP (uni-processor)machine [Unia].

If you decide to optimize your kernel the following hints will help you, butbe sure, that you know that it will not make your kernel unable to run onyour hardware. It is recommended to exclude all modules and build-ins, thatyou will not need later while working with the system, e.g. you could excludeJoystick interface, Touchscreens or ISDN support. Also set the file systemsyou will want to use and the system will need as build-ins. The web page [Len]recommends “to activate I-pipe and to deactivate some kernel features (essen-tially some power management features) that would ruin the latencies in thereal-time kernel”:

• In Processor type and features :

– Check that Interrupt pipeline is enabled (this is to enable the Adeosimplementation - I-pipe).2

– Check that Local APIC support on uniprocessors and IO-APIC sup-port on uniprocessors are enabled.

• In Power management options (ACPI, APM):

2In newer versions of Xenomai is this option not configurable.

10

Page 15: 57842309 Xenomai Implementation

2 Xenomai – an Installation Guide 2.3 Compiling

– In ACPI (Advanced Configuration and Power Interface) Support:

∗ Disable Processor.

– In APM (Advanced Power Management) BIOS Support:

∗ Disable APM (Advanced Power Management) BIOS support.

– In CPU Frequency scaling:

∗ Disable CPU Frequency scaling.

• In Real-time sub-system

– Optionally, if needed by your applications, enable Interrupt shieldsupport.

– Enable Watchdog support (this is sometimes useful).

– In Scalability:

∗ Optionally, if your applications create many RT threads, enableO(1) scheduler.

– In Machine (x86):

∗ Only if your system has FPU3, enable FPU support to improvelatency.

∗ In SMI workaround:

· Only if your system uses SMI4, AND you encouter highmaximum latencies, enable Enable SMI workaround andGlobally disable SMI 5.

– In APIs, enable all options.

– In Drivers, enable all options.

Finally save changes and type make to let the kernel compile. Note: if youare compiling the kernel for the first time, it may be appropriate to inform you,that it takes, dependend from the computing power of your computer, one tothree hours.

Shell commands to configure and compile the kernel:

3Look for FPU in /proc/cpuinfo file4SMI- System Maintenance Interrupt5If some devices do not work anymore when loading Xenomai modules with the SMI

workaround enabled, it probably means that those devices rely on SMI. You should tryto re-enable some SMI interrupts in Xenomai’s SMI workaround configuration, one byone. Try to enable an SMI interrupt, compile and then disable it again if it has no effect.See also [Unib, High latencies with SMI not disabled].

11

Page 16: 57842309 Xenomai Implementation

2 Xenomai – an Installation Guide 2.4 Setting up Bootloader and Testing

# cp /boot/config-2.6.12-10-386 /usr/src/linux-2.6.17.6/.config

# cd /usr/src/linux-2.6.17.6

# make menuconfig

# make

# make modules_install install

We spent a lot of time recompiling the kernel searching for the right configu-ration. It is wise to get help from somebody who has already some experiencesin it if you want to optimize the kernel. For more info or getting help withconfiguring, compiling and hacking the kernel, visit also webpages that are con-cerned with Linux kernel compilation, editing and hacking. As a starting pointwe recommend [lxc, kera].Next go to the Xenomai directory and compile Xenomai:

# cd /usr/src/xenomai-2.2.0

# ./configure

# make

# make install

2.4 Setting up Bootloader and Testing

After the sources were successfully compiled you will need to add the new kernelto LILO or GRUB, dependend on what is your system using.

2.4.1 Configuring LILO

Keep in mind that you are adding new kernel, not changing the settings of theold one. When there will be problem booting-up the new kernel you can stillreturn to the old one.Say your lilo.conf 6 has something like this:

...

image=/boot/vmlinuz-2.6.8-2-686

label=linux-2.6.8

read-only

...

After adding the new kernel it should look like this:

6You should find lilo.conf in directory /etc/ and open it in you favorite editor.

12

Page 17: 57842309 Xenomai Implementation

2 Xenomai – an Installation Guide 2.4 Setting up Bootloader and Testing

...

image=/boot/vmlinuz-2.6.8-2-686

label=linux-2.6.8

read-only

image=/boot/vmlinuz-2.6.17.6-xenomai

label=linux-xenomai

read-only

...

Save changes and inform LILO about the changed lilo.conf file with runningthe command lilo. Afterwards you can reboot into your new kernel. If youobserve any problems at boot-up please read the sub-section 2.4.4.

2.4.2 Configuring GRUB

Keep in mind that you are adding new kernel, not changing the settings of theold one. When there will be problem booting-up the new kernel you can stillreturn to the old one.Say your menu.lst7 has something like this at the end of the file:

...

title Debian, kernel 2.6.15-1-486

root (hd0,0)

kernel /boot/vmlinuz-2.6.15-1-486 root=/dev/hda1 ro

initrd /boot/initrd.img-2.6.15-1-486

savedefault

boot

...

After adding the new kernel it should look like this:

...

title Debian, kernel 2.6.15-1-486

root (hd0,0)

kernel /boot/vmlinuz-2.6.15-1-486 root=/dev/hda1 ro

initrd /boot/initrd.img-2.6.15-1-486

savedefault

boot

7You should find menu.lst in directory /boot/grub/ and open it in your favorite editor.

13

Page 18: 57842309 Xenomai Implementation

2 Xenomai – an Installation Guide 2.4 Setting up Bootloader and Testing

title Xenomai, kernel 2.6.17.6

root (hd0,0)

kernel /boot/vmlinuz-2.6.17.6-xenomai root=/dev/hda1 ro

savedefault

boot

...

Save changes and reboot. If you observe any problems at boot-up please readthe sub-section 2.4.4.

2.4.3 Testing

After successfully logged into the system with the new kernel first check ifAdeos is working with the command “dmesg | grep I-pipe”. You should getthis output:

I-pipe 1.3-06: pipeline enabled

I-pipe: Domain Xenomai registered

also check if the directory /proc/ipipe/ exists. Next, the command “dmesg |grep Xenomai” should return:

Xenomai: hal/x86 started

Xenomai: real-time nucleus v2.2.0 (Seven String) loaded

Xenomai: starting POSIX services.

Xenomai: starting RTDM services.

if Xenomai was loaded properly. Also execute /usr/xenomai/bin/xeno-test.If no error occurs, any Xenomai application should execute fine also. The testmeasures real-time interrupt latencies of your Linux/Adeos and Xenomai setup.See the man pages for options:

# man /usr/xenomai/man/man1/xeno-test.1

To cancel the test you should press “Ctrl-C”. That test uses program dd toexecute high load on the system, therefore it is wise to checkout if the programwas cancelled together with the test. Type “ps” to see if dd is still running, ifyes type “killall dd” to kill that process.

Also it is recommended to look on the other programs delivered withXenomai in the /usr/xenomai/bin/ directory. Man pages can be found in/usr/xenomai/man/. Further you can run as a root the testsuite tests in/usr/xenomai/testsuite.

14

Page 19: 57842309 Xenomai Implementation

2 Xenomai – an Installation Guide 2.4 Setting up Bootloader and Testing

# cd /usr/xenomai/testsuite/latency

# ./run

...

If you will get an error message please read the sub-section 2.4.4.

2.4.4 Error Handling

In what follows this installation guide will give solutions to the problems weobserved while installing the Xenomai.

Kernel Panic at Boot-up

It is possible that you will get this error message at boot-up: VFS:Cannot openroot device or “Kernel panic- not syncinc: VFS: Unable to mount root fs onunknows-blocks(3,3)”. In that case, you may need to make the initial RAM-disk. To do so, reboot into your old kernel and go to the directory /boot/.

# cd /boot/

# mkinitrd -o /boot/initrd.img-2.6.17.6-xenomai 2.6.17.6-xenomai

The -o option specifies the file to be generated and the second argument2.6.17.6-xenomai specifies directory to grab modules from (/lib/modules/x.x.x-x) Then, if you are using LILO, add initrd.img-2.6.17.6-xenomai intothe /etc/lilo.conf file.

...

image=/boot/vmlinuz-2.6.17.6-xenomai

label=linux-xenomai

initrd=/boot/initrd.img-2.6.17.6-xenomai ## new line

read-only

...

Run lilo again and reboot.

If you are using GRUB, add initrd.img-2.6.17.6-xenomai into the /boot/

grub/menu.lst file.

...

title Xenomai, kernel 2.6.17.6

root (hd0,0)

15

Page 20: 57842309 Xenomai Implementation

2 Xenomai – an Installation Guide 2.4 Setting up Bootloader and Testing

kernel /boot/vmlinuz-2.6.17.6-xenomai root=/dev/hda1 ro

initrd /boot/initrd.img-2.6.17.6-xenomai ## new line

savedefault

boot

...

And reboot.

Testprograms not Working

If you get an error message like “Xenomai: native skin or CON-FIG XENO OPT PERVASIVE disabled.” check the kernel log with the com-mand “dmesg | grep Xenomai” and if you get similar output then here:

...

Xenomai: Local APIC absent or disabled!

Disable APIC support or pass "lapic=1" as bootparam.

Xenomai: system init failed, code -19.

Xenomai: starting POSIX services.

Xenomai: POSIX skin init failed, code -19.

Xenomai: RTDM skin init failed, code -19.

...

You will have to pass lapic=1 as a bootparameter. Having LILO you can doit in lilo.conf file as follows:

...

image=/boot/vmlinuz-2.6.17.6-xenomai

label=linux-xenomai

append="lapic=1"

initrd=/boot/initrd.img-2.6.17.6-xenomai

read-only

...

Run lilo and reboot8. If you are using GRUB modify the kernel line in /boot/

grub/menu.lst:

...

title Xenomai, kernel 2.6.17.6

8you can also reboot from the command line writing simply reboot and pressing enter

16

Page 21: 57842309 Xenomai Implementation

2 Xenomai – an Installation Guide 2.4 Setting up Bootloader and Testing

root (hd0,0)

kernel /boot/vmlinuz-2.6.17.6-xenomai root=/dev/hda1 ro lapic

initrd /boot/initrd.img-2.6.17.6-xenomai

savedefault

boot

...

and reboot.

NOTE: The reason why we didn’t tell you to write all these options to thebootloaders in subsections 2.4.1, 2.4.2 is that with some architectures and kernelconfigurations it may be working also without them. The initrd option- initialRAM-disk contains modules that need to be loaded by the kernel at boot-up.If no modules are needed to be loaded in the boot-up phase, the system shouldbe able to boot-up also without it. See man pages of mkinitrd and lilo.conf formore help.The cause of the need to pass lapic as a bootparameter seems to be a bug inXenomai. As we found in the mailing-list9 it should have been fixed, but asof 07.07.2006, we still had to add that parameter in spite of the fact that wehave enabled it while configuring the linux kernel. On the web page [Len], thatis of an older date, is the explanation: “Xenomai requires to have local APICenabled for the CPUs by the kernel, which is always the case for SMP systems,but is not always the case by default for single-CPU systems.”You can find more information concerning LILO on the man pages of lilo orviewing the webpage [lxc, lesson4] or [Sko]. For GRUB manual see [Fou].

Failed to Start Menuconfig

During our installation process we also observed a problem with starting themenuconfig because the ncurses lib’s were not installed. So if you will get errormessages saying that some ncurses files are missing, just type:

# apt-get install ncurses-dev

to install them.

If you still will have problems starting make menuconfig, you can editdirectly /usr/src/linux-2.6.17.6/.config file or as a last possibility trymake config, which is pure line by line text mode and will ask you lot ofquestions.

9https://mail.gna.org/public/xenomai-help/2006-04/msg00178.html

17

Page 22: 57842309 Xenomai Implementation

2 Xenomai – an Installation Guide 2.4 Setting up Bootloader and Testing

Error Loading Shared Libraries

If you will get an error message saying: “error while loading shared libraries:libnative.so.0: cannot open shared object file: No such file or directory”, add“/usr/xenomai/lib” to /etc/ld.so.conf and run /sbin/ldconfig .

If you have some other problems installing Xenomai you should visit theofficial Xenomai webpages [xenb, xena] and use an internet search engine.

18

Page 23: 57842309 Xenomai Implementation

3 Xenomai- First Application

After the Xenomai environment was successfully established we can start towrite our first application. The best way is to look at an existing noncomplexcode and analyze its Xenomai operations. One can find some examples of simpleXenomai codes on the webpage [Unib] and Xenomai actual API on [xenc] orin the Xenomai source directory /usr/src/xenomai-2.2.0/doc/generated/

html/api/main.html.

In this work we used the code from the webpage [Unib] which can be foundin the section 7.1 of this paper. As one can see, the main() function preparesthe environment for the real-time task execution, starts the task and waitsfor a signal that cancels that task. In Xenomai versions before 2.2.0 also thetimer had to be started explicitly in the main function, now it is done by theoperating system itself when a real-time task begins its execution. The real-time task is stand-alone and self-contained. It defines its variables, configuresitself and executes the real-time functions. It is important to note that thesetwo examples of codes use function printf which is not real-time and thoughcan disturb the hard real-time feature. Therefore the use of printf is notrecommended in the real hard real-time tasks.

In the real-time task of “timer 1.c” we used in the while-loop thert task set mode function. At this point we want to note that the tasks cre-ated in Xenomai can run either in the primary or in the secondary executionmode. The former means that the threads can run in the context of the highestpriority domain in the pipeline, like kernel-based Xenomai threads. The lattermeans that the tasks can run in the regular Linux space but still are consideredas real-time by Xenomai. More on these modes can be found in [Ger05, section2.1.].

The basic difference between the two example codes is that they use differenttiming modes. The first one “timer 1.c” uses the aperiodic (one-shot) and thesecond one “timer 2.c” the periodic timing mode. The first one is enabled bydefault when configuring the kernel before kernel compilation and the secondone must be set explicitly in the kernel configuration menu following these stepsin the kernel configuration menu:

Real-time sub-system -->

19

Page 24: 57842309 Xenomai Implementation

3 Xenomai- First Application

Timing -->

[ ] Use periodic timer hardware

In periodic mode, clock ticks are interpreted as periodic jiffies. In oneshotmode, clock ticks are interpreted as nanoseconds. The, by default, aperiodicmode has much better accuracy for different time scales that cannot be easilyexpressed as multiples of a single base tick. The periodic mode makes the hard-ware timer tick at constant frequency and rounds the timeouts to a constanttime slice. Therefore it is recommended to use aperiodic (oneshot) mode forprecise timing.

Before compiling one of the codes with Makefile you have to add <xeno-install>/bin to your PATH variable. That’s easily done this way:

$ PATH=$PATH:/usr/xenomai/bin #use your <xeno-install>/bin path!

$ echo $PATH

/sbin:/usr/sbin:/bin:/usr/bin:/usr/bin/X11:/usr/xenomai/bin

You can set the name of the code you want to compile in the Makefile whenyou add the name to the TASK variable. Compile it and run in the super-usermode. Both programs will print out periodically (every second) a message onthe stdout.

20

Page 25: 57842309 Xenomai Implementation

4 Linux Driver

Before we begin to write a hard real-time driver for our CPCI-EA221 card underXenomai we first focus on writing a simple driver under Linux system withoutXenomai extension. The reason not to go directly to program real-time driveris that we could not find any good reference, any tutorial for programming ourCPCI driver under Xenomai. Luckily this was not the case with Linux andwe soon have found an excellent source of the needed information. The bookLinux Device Drivers, Third Edition [CRKH05], which is also freely availablefor download. In the next sections will be described what you need to know tocreate the CPCI driver and at the end of the chapter also a short description ofthe functions our driver offers to the user space (API) will be defined. It is alsoimportant to note that our driver was build to serve only to one CPCI-EA221device, to extend its functionality for more devices is one of the more pointsthat should be done in the future development.

The complete code is in the section 7.2.

4.1 Programming the Driver

You can extend the functionality of your Linux kernel by adding modules to iteven at run time, so you don’t have to restart the system to get your moduleworking with the kernel. That’s a very nice feature since the developmentprocess can progress faster. Our driver is also a module and therefore thefirst thing we need to know is how to define and initialize kernel modules. Toobtain that knowledge we recommend to read [CRKH05, chapter 2, Buildingand Running Modules]. To get the background knowledge on Linux kernel,Linux drivers, Linux licensing, etc it is also wise to read [CRKH05, chapter 1,An Introduction to Device Drivers].

Afterwards we need to find the device the driver is supposed to work on.The recommended way is to inform the kernel in what hardware device weare interested. We export the structure defining our device to the moduleloading system so that the system knows which driver to call when it findsthe specified device. As it was already mentioned in the chapter 1, CPCIis a high-performance standard based upon peripheral component interconnect

21

Page 26: 57842309 Xenomai Implementation

4 Linux Driver 4.1 Programming the Driver

(PCI) technology and therefore we can initialize any CPCI device like describedin [CRKH05, chapter 12, PCI Drivers]. Also, there is a helpful pci skeleton codepci skel.c provided in the programs package of the book.

The first three needed steps to do in the device initialization function .probe()are to wake up the device with pci enable device(), allocate a memory regionwith request mem region() and to map the allocated memory to the kernel spacevirtual memory with ioremap(). Afterwards you can already write to the deviceregisters using functions iowrite8(), iowrite16() or iowrite32(). To study how toallocate and map the memory read [CRKH05, chapter 9, Communicating withHardware, section: Using I/O Memory] and [CRKH05, chapter 8, AllocatingMemory, section: vmalloc and Friends]. In [CRKH05, chapter 12, PCI Drivers]you will find how to find the physical location (memory address) of the device.Do not forget to deallocate, to free always the memory you allocated in case ofan error and in the exit() function of the driver.

So far we have initialized the driver module to be able to read from andto write to the registers of the device. What we need now is to define andmake available some functions the driver will offer to the kernel and that wayalso to the (user-space) applications. Our device falls into the category of chardevices and such devices are accessed through names in the filesystem. To learnhow to create a name in the filesystem for a device read [CRKH05, chapter 3,Char Drivers, section: Major and Minor Numbers]. Basically, by creating ofsuch an entry point, the device will get its major and minor number and alsoits name. Afterwards you can check the successful allocation viewing the file/proc/devices. You should see the major number and the device name there.After creating the numbers, the driver needs on one side a node in the filesystemthat serves as an entry point for the applications that want to use the deviceand on the other side the device numbers must be connected to the driver’sinternal functions that implement the operations on the device.

Creation of the device node with the allocated major number is bestdone by the loading script. In our CPCI-EA221 driver we use the scriptcpci ea221 load.sh to insert the module to the kernel and to create the de-vice node. The script cpci ea221 unload.sh is used to remove the device nodesand the driver module from the kernel. Note that the minor number is hard-coded, because the driver is build to work just with one CPCI-EA221 device.In our code, if you prefere to give your own major number to the device youcan do it at compile time when you change the appropriate macro in the headerfile of the driver or you can also specify the number at module loading timewith the command ./cpci ea221 load.sh cpci ea221 major=xx

To know how to connect the device numbers to the internal functions ofthe device which implements the device’s operations read [CRKH05, chapter3, Char Drivers, sections: Some Important Data Structures and Char Device

22

Page 27: 57842309 Xenomai Implementation

4 Linux Driver 4.2 API

Registration]. It is needed to define the file operations structure with thefunctions the driver will implement. A pointer to such structure is called fops.In our module we just use open(), close() and ioctl(). The important methodis ioctl() in which we define specific hardware control operations [CRKH05,chapter 6, Advanced Char Driver Operations, section: ioctl]. In this casewe just wanted to enable to read from and to write to the registers of thedevice. Because with these two functions the device can be configured alsofrom the user-space we don’t need to think about all possible configurations ofthe device and write those configurations as separate ioctl() operations. Thefunctions are described in 4.2 and to be able to configure the device you willneed the CPCI-EA221 technical manual [Siea]. In our ioctl() operations wecould not use the recommended fast functions put user(), get user() becauseour parameter structure didn’t fit in one of the specific sizes needed when usingthese functions, that’s why we had to use copy to user() and copy from user()instead.

The last step is to registry our char device [CRKH05, chapter 3, Char Drivers,section: Char Device Registration]. In the .probe() we call setup cdev()as a lastoperation, in which we set up the important cdev structure and add that struc-ture to the other cdev structures that are available to the kernel. Immediatelyafterwards the kernel can handle operations on the device, that’s why it is im-portant to call the function cdev add() first when the driver is completely readyto process the operations on the device.

4.2 API

The applications can use the ioctl function:

int ioctl(int fd,

unsigned long cmd,

struct ioc_param* arg);

Parameters:[in] fd : File descriptor as returned by open()1.[in] cmd : IOCTL code (see 4.2.1).[in|out] arg : Pointer on an argument structure (see 4.2.1).

Return value:In case of an error -1 and sets errno to indicate the error.

1A good source of many detailed function descriptions can be found on the webpage [Gro04].

23

Page 28: 57842309 Xenomai Implementation

4 Linux Driver 4.2 API

4.2.1 IOCTL Code and Argument Structure:

You should include into your code the file cpci_ea221_driver.h or add thefollowing code into your header file:

#include <linux/ioctl.h> //needed for creating the ioctl cmnd numbers

struct ioc_param{

uint32_t bv;

uint8_t offset;

};

typedef struct ioc_param ioc_param_struct;

#define EA221_IOCRESET _IO(0xEE, 0)

#define EA221_IOCWRITE _IOW(0xEE, 1, ioc_param_struct)

#define EA221_IOCREAD _IOWR(0xEE, 2, ioc_param_struct)

The bv member of the ioc param struct is a place where to store the 32-bitslong vector you want to write into a register in the CPCI-EA221 card. Alsothe driver will store into the bv variable the value from a register you wantedto read. The offset is for storing the value that must be added to the baseaddress (BA) of the device to obtain the full address of the register. You willfind these offset values in the address column of the register tables in [Siea,chapter 4, Overview of the Registers].

The meaning of the IOCTL commands is following:

• EA221 IOCRESET stands for software reset. If you want to know howexactly the board reacts to the reset see [Siea, chapter 5, Reset Reaction].

• EA221 IOCWRITE stands for writing into the device registers.

• EA221 IOCREAD stands for reading from device registers.

4.2.2 Examples:

To reset the board: ioctl(fd, EA221 IOCRESET). Next, for instance, wewant to write the vector 0x0000FFFF in the register with the addressBA+0x28. We simply declare and initialize one variable in the driver.c fileas an ioc param struct with the given values and use an ioctl function, i.e.:

ioc_param_struct data = {0x0000FFFF, 0x28);

ioctl(fd, EA221_IOCWRITE, &data);

24

Page 29: 57842309 Xenomai Implementation

4 Linux Driver 4.3 Test Application

When we want to read from an register on the address BA+0x28 :

ioc_param_struct data = {0x00, 0x28);

ioctl(fd, EA221_IOCWRITE, &data);

Afterwards will be the value read from the register stored in data.bv variable.

value_from_register = data.bv;

4.3 Test Application

For test reasons we wrote a simple test application that should prove if ourioctl() functions are working properly also from the user-space. Everythingimportant is in the main() task. First we need to open the character devicewith the open() function. After opening the device file, or, in other words, afteropening the entry point to the device, we have the file descriptor on which wecan apply the ioctl() functions. In the while loop the process will change theoutput of the device after every pressing enter button on the keyboard. So onetime there will be HIGH on the output pins and another time will be LOW,the state of the output pins will be signalized with the LED #1, if outputs willbe HIGH the LED #1 will be also HIGH (switched on), if the outputs will beLOW, the LED #1 will be off.

25

Page 30: 57842309 Xenomai Implementation

5 Xenomai – RTDM Driver

Now we know some basic functions of Xenomai and know also how to write asimple driver module under Linux kernel. This chapter will explain how to usethe Xenomai features in the driver module, so that in the end we will have adriver module which runs in hard real-time. It will be shown that some thingsthat are important in Linux drivers are not used in Xenomai drivers. At thispoint we would like to note, that drivers under Xenomai are using the Real-Time Driver Model (RTDM) skin that can be loaded together with the maininterface, so make sure you enabled the installation of RTDM in the kernel con-figuration menu. In this chapter we will use instead of the name Xenomai driverthe name “RTDM driver” as it represents the used API better. As a startingpoint we recommend to read an article on RTDM by Jan Kiszka [Kis05] fromwhich we cite the goal of the RTDM “The Real-Time Driver Model (RTDM) isan approach to unify the interfaces for developing device drivers and associatedapplications under real-time Linux.”.

Because there is no such book for RTDM as for Linux drivers [CRKH05],we helped us with reading the source code of the real-time serial port mod-ule xeno 16550A.ko1, further with reading the code on the Captain’s web-page [Unib, mostly the section: Hard Real Time Driver Example Tutorial withMMAP using the RTDM (Real Time Driver Model)] and studying the Xenomaiand RTDM API on [xenc]. With these three sources of information and thebackground we gained by programming the Linux driver in the previous chap-ter we could achieve our next goal, to write our own CPCI-EA driver modulewith Xenomai(RTDM) features. As for the Linux driver, in the chapter 4, aswell for this driver holds that it was build to serve only to one CPCI-EA221device, to extend its functionality for more devices is one of the more pointsthat should be done in the future development.

For the complete source code of the driver see the section 7.3.

5.1 Programming the RTDM Driver

The definition and initialization of the driver module is made the same way asin the Linux driver. Alike is it with defining the device, we are interested in, to

1the source code is in the file /usr/src/xenomai-2.2.0/ksrc/drivers/16550A/16550A.c

26

Page 31: 57842309 Xenomai Implementation

5 Xenomai – RTDM Driver 5.1 Programming the RTDM Driver

the module loading system. You can even use the same PCI skeleton code asintroduced in the chapter 4 and you should also read the first two paragraphsof the section “Programming the driver” from the same chapter.

Next we need to define the RTDM device description that will be passedlately to rtdm dev register() function. The description is specified within thestructure rtdm device. An overview of central parameters that have to be spec-ified can be found in [Kis05, section 2.2, Device Registration and Invocation]otherwise see the API [xenc]. In our device module we left out or left blankthe device name and proc name because it will be defined in the .probe() func-tion. This approach will be usefull later when the module will be adapted tosupport more devices. Another important structure used in our code is thexen cpciea221 context structure. It is used as an internal source of informationon our device, we simply store there what we think will be needed in furtherwork with the device.

When the RTDM device structure is defined we can continue with working onthe .probe() function. After enabling the pci device with pci enable device() andobtaining the physical location and size of the physical memory of the CPCI-EA221 card, the memory for the RTDM structure will be allocated and sub-sequently the structure will be copied into that new memory space. Then thename of the RTDM device is specified, given to the RTDM structure and usedfor allocating the memory region for CPCI card with request mem region().The memory region is afterwards mapped to the virtual kernel space withioremap(). The name of the RTDM device given to the device structure isimportant because it is the only one access point to our RTDM device, thusit should be different for every device using this driver. The approach used inthe Linux driver, that each driver has a node in the file system that serves asan access point to the device, is not followed in RTDM drivers. For this reasonwe don’t need to allocate major and minor numbers of the device nor to writean extra loading script for the driver’s module. From that follows also that wewill not find the RTDM device name in the /dev/ directory, but you can findit listed in the /proc/iomem file. In the section 5.3 will be shown how to accessthe RTDM device. After the memory was successfully mapped to the kernelspace, the last thing needed to be done is to register the RTDM structure withrtdm dev register(). Immediately afterwards the kernel can call the functionsdefined in the RTDM structure, so make sure that the function is called firstwhen everything is prepared for the proper device operation.

The functions rt open() and rt close() are not doing anything necessary atthis state of the development of the driver. Their bodies have now rather ademonstrative character. To every open device instance is a device contextstructure associated. In the rt open() is its use depicted. One can see how toaccess data from the rtdm device struct or from the context structure of the

27

Page 32: 57842309 Xenomai Implementation

5 Xenomai – RTDM Driver 5.2 API

device we defined ourselves as a place where to store the data that could beneeded by other functions handling on the device. In further development thisfunctions should be used to allocate or deallocate additional memory for thedriver, or to start the interrupt handlers and so on. The bodies of the ioctl()function are quite the same as in the Linux driver module, just most of theused operations were replaced by the RTDM operations. Otherwise all whatwas said on ioctl() in the chapter 4 holds also in this module. See 5.2 for APIof the RTDM ioctl().

5.2 API

The applications can use the rt dev ioctl function:

int rt_dev_ioctl(int fd,

unsigned long cmd,

struct ioc_param* arg);

Parameters:[in] fd : File descriptor as returned by rt dev open().[in] cmd : IOCTL code (see 4.2.1).[in|out] arg : Pointer on an argument structure (see 4.2.1).

Return value:Positive value on success, otherwise negative error code.

5.2.1 Examples:

The same as in the subsection 4.2.2 with an small exception, ioctl() is replacedby rt dev ioctl() function.

5.3 Test Application

To test our driver module with the Xenomai-RTDM features we build up asimple application. As you can see from the source code, the structure ofthe application is similar to the applications in the chapter 3. There is themain() task that initializes everything needed, like opening the character de-vice, starting the real-time task and waiting for cancel of the real-time task

28

Page 33: 57842309 Xenomai Implementation

5 Xenomai – RTDM Driver 5.3 Test Application

and also there is the real-time task which embodies the most important partof the application. The real-time task supports different modes, which can beset up in the header file with uncommenting or commenting the two macros#define PRINTF and #define SIMPLE. With PRINTF enabled a messagewill be printed on stdout, but because printf function brings high latenciesand corrupts the precise timing it is not recommended to use it. If SIMPLE isenabled, just a simple real-time task will be executed in which no care on inputregister is taken, all output pins will change their common state periodically.Otherwise, if SIMPLE is not enabled, the little bit more complex task willbe executed that takes care on the input register. That means that the stateof the output register will change periodically considering the the state of theinput. This application proves that it is possible to write to and to read fromthe registers of the device under timing constraints and from the user-space.

One more note to opening the device in the Xenomai environment. Thedevice is opened with the rt dev open(dev name, flags) function in which thedev name stands for the name we defined and used in the .probe() function ofthe RTDM driver when calling request mem region(). As already mentionedbefore, this is the access point to the driver and subsequently to the device.This seems to be the biggest difference to the approach used in common Linuxdrivers.

29

Page 34: 57842309 Xenomai Implementation

6 Conclusion

This project showed that solutions for real-time execution of tasks under Linuxthat can run together with ’regular’ Linux kernel already exists. We showedhow to establish such real-time environment, how to write the first tasks usingit and concerned on the development of a device driver. The first steps neededfor designing a driver under ’regular’ Linux and under Xenomai were explained.When comparing these device drivers one can see that they have some maindifferences, like that the Xenomai device is not accessed per a node in thefilesystem, and they have also many similarities, and in some cases what oneneeds to do is just to rename the function, like it was the case with access ok()in ’regular’ Linux and rtdm read user ok() in Xenomai.

In the folllowing sections we will discuss briefly what problems we foundat programming of our driver and what could be the next steps or possibleextensions of our project.

6.1 Problems

We observed only two problems for that we didn’t have time to solve. Firstis that while loading the device driver module into the kernel space, the im-portant function .probe() is executed two times, except of being just one time.We observed the same also with unloading the module from the kernel. Theproblem is with allocating and mapping the memory in this function that isdone in that case also two times. The first allocation and mapping is the rightone and we get wrong values in the second loop. We solved this problem withassuring that the mapping and allocation is executed only one time by givinga if condition at the beginning of the .probe() function. The same for unload-ing function of the module, because otherwise we would deallocate the samememory two times, what is not desired. We are sure this is not the right wayto go, but it seems to be the easiest one at the time. This problem should besolved another way.

The second problem we have is with the test application xen_led_timer.c.The catch signal() function should reset the board when the exit signal occurs.The board should put all of its outputs low and take no care on inputs, whichis not the case. We didn’t come on the solution till deadline.

30

Page 35: 57842309 Xenomai Implementation

6 Conclusion 6.2 Outlook

6.2 Outlook

There are quite many things still to do. What we did is only that one can write asimple user-space application that can configure the device, per device registers,how it wants and then use the card’s services. The most important work whatwe did not come to is setting up and using the interrupts. We think that thenext step in extending the functionality of the device driver under Xenomaishould be the interrupt support. Therefore, it is necessary to establish hardreal-time interrupt handlers and write some applications using them. Otherextensions could be for example the support for multiple CPCI devices, somemore sophisticated ioctl functions, establishing communication with externaldevices using the CPCI-EA221 card, etc. It would be also important to maketests on hard real-time, to measure the tasks execution and latencies to get toknow and approve hard-real time features of the Xenomai system.

31

Page 36: 57842309 Xenomai Implementation

7 Listings

32

Page 37: 57842309 Xenomai Implementation

7 Listings 7.1 Xenomai – First Application

7.1 Xenomai – First Application

7.1.1 timer 1.c

/*======================================================================*/

/* Traineeship-Project: Implementation of Xenomai */

/* Title: timer task #1, based on code on website: www.captain.at */

/* Purpose: - to get first experience with the Xenomai environment */

/* */

/* File: timer_1.c */

/* Version: 0.1 */

/* Date: 08/2006 */

/* */

/* Description: - hard real-time user-space task that prints out */

/* periodically a simple text on stdout */

/* Software: Linux Debian kernel 2.6.17.6 with Xenomai-2.2.0 extension*/

/* Hardware: Sicomp SMP16 (Siemens industrial computer) */

/* */

/* Authors: www.captain.at, Ondrej Cevan (TU Vienna) */

/* Licence: GPL */

/* Note: - set aperiodic timing mode (in kernel config menu) to get*/

/* precise timing */

/* - No warranty is attached, we cannot take responsibility */

/* for errors or fitness for use. */

/*======================================================================*/

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

#include <signal.h>

#include <sys/time.h>

#include <sys/io.h>

#include <sys/mman.h>

#include <native/task.h>

#include <native/queue.h>

#include <native/intr.h>

#define STACK_SIZE 0 // a pre-defined size will be substituted

#define STD_PRIO 99 // the highest one

33

Page 38: 57842309 Xenomai Implementation

7 Listings 7.1 Xenomai – First Application

RT_TASK test_task_ptr;

const char* cmnd = "<not yet set>";

int int_count = 0;

int end = 0;

// --s-ms-us-ns

RTIME task_period_ns = 1000000000llu;

/* cookie --> A user-defined opaque cookie the real-time kernel will pass

to the emerging task as the sole argument of its entry point.*/

void testtask(void *cookie){

int count = 0;

int ret;

unsigned long overrun;

/* Make a task periodic by programing its first release point and

its period in the processor time line.

*/

ret = rt_task_set_periodic(NULL, TM_NOW,

rt_timer_ns2ticks(task_period_ns));

if (ret) {

printf("%s: error while set periodic, code %d\n",cmnd, ret);

return;

}

while(!end){

//switch to primary mode

ret = rt_task_set_mode(0, T_PRIMARY, NULL);

if (ret) {

printf("%s: error while rt_task_set_mode, code %d\n",cmnd, ret);

return;

}

// Wait for the next periodic release point

// and write the number of overruns to &overrun.

ret = rt_task_wait_period(&overrun);

if (ret) {

printf("%s: error while rt_task_wait_period, code %d\n",cmnd, ret);

return;

}

count++;

printf("%s: message from testtask: count=%d\n", cmnd, count);

fflush(NULL);

34

Page 39: 57842309 Xenomai Implementation

7 Listings 7.1 Xenomai – First Application

}

}

// signal-handler, to ensure clean exit on Ctrl-C

void clean_exit(int dummy) {

printf("%s: cleanup\n",cmnd);

end = 1;

rt_task_delete(&test_task_ptr);

printf("%s: end\n", cmnd);

}

int main(int argc, char *argv[]) {

int err;

cmnd = argv[0];

printf("%s started\n", cmnd);

printf("press ctrl-c to exit\n");

// install signal handler

signal(SIGTERM, clean_exit);

signal(SIGINT, clean_exit);

//disable paging for this program’s memory

mlockall(MCL_CURRENT | MCL_FUTURE);

//create and start a task

err = rt_task_spawn(&test_task_ptr, "Timer",

STACK_SIZE, STD_PRIO, 0, &testtask, NULL);

if (err) {

printf("%s: error rt_task_spawn\n", cmnd);

return 0;

}

// wait for a signal & return of signal handler

pause();

fflush(NULL);

return 0;

}

35

Page 40: 57842309 Xenomai Implementation

7 Listings 7.1 Xenomai – First Application

7.1.2 timer 2.c

/*======================================================================*/

/* Traineeship-Project: Implementation of Xenomai */

/* Title: timer task #2, based on code on website: www.captain.at */

/* Purpose: - to get first experience with the Xenomai environment */

/* */

/* File: timer_2.c */

/* Version: 0.1 */

/* Date: 08/2006 */

/* */

/* Description: - hard real-time user-space task that prints out */

/* periodically a simple text on stdout */

/* Software: Linux Debian kernel 2.6.17.6 with Xenomai-2.2.0 extension*/

/* Hardware: Sicomp SMP16 (Siemens industrial computer) */

/* */

/* Authors: www.captain.at, Ondrej Cevan (TU Vienna) */

/* Licence: GPL */

/* Note: - periodic timer support must be enabled */

/* (in kernel config menu) */

/* - No warranty is attached, we cannot take responsibility */

/* for errors or fitness for use. */

/*======================================================================*/

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

#include <signal.h>

#include <sys/time.h>

#include <sys/io.h>

#include <sys/mman.h>

#include <native/task.h>

#include <native/queue.h>

#include <native/intr.h>

#define STACK_SIZE 0 // a pre-defined size will be substituted

#define STD_PRIO 99 // the highest priority

RT_TASK test_task_ptr;

int int_count = 0;

int end = 0;

36

Page 41: 57842309 Xenomai Implementation

7 Listings 7.1 Xenomai – First Application

void testtask(void *cookie){

int count = 0;

while(!end){

//delay the execution of the calling task

//if we have periodic timer support

rt_task_sleep(1000000000);

//if we don’t have periodic timer support:

//rt_task_sleep(rt_timer_ns2ticks(1000000000));

count++;

printf("message from testtask: count=%d\n", count);

fflush(NULL);

}

}

// signal-handler, to ensure clean exit on Ctrl-C

void clean_exit(int dummy) {

printf("cleanup\n");

end = 1;

rt_task_delete(&test_task_ptr);

printf("end\n");

}

int main(int argc, char *argv[]) {

int err;

printf("start\n");

printf("press ctrl-c to exit\n");

// install signal handler

signal(SIGTERM, clean_exit);

signal(SIGINT, clean_exit);

//disable paging for this program’s memory

mlockall(MCL_CURRENT | MCL_FUTURE);

//create and start an RT task

err = rt_task_spawn(&test_task_ptr, "Timer", STACK_SIZE,

STD_PRIO, 0, &testtask, NULL);

if (err) {

printf("error rt_task_spawn\n");

return 0;

}

37

Page 42: 57842309 Xenomai Implementation

7 Listings 7.1 Xenomai – First Application

// wait for signal & return of signal handler

pause();

fflush(NULL);

return 0;

}

38

Page 43: 57842309 Xenomai Implementation

7 Listings 7.1 Xenomai – First Application

7.1.3 Makefile

##########################################################################

## Traineeship-Project: Implementation of Xenomai ##

## Title: Makefile for timer task #1 and #2 ##

## based on website: www.captain.at ##

## Purpose: - to build the timer applications ##

## ##

## File: Makefile ##

## Version: 0.1 ##

## Date: 08/2006 ##

## ##

## Software: Linux Debian kernel 2.6.17.6 with Xenomai-2.2.0 extension ##

## Hardware: Sicomp SMP16 (Siemens industrial computer) ##

## ##

## Authors: www.captain.at, Ondrej Cevan (TU Vienna) ##

## License: GPL ##

##########################################################################

## define name of user space application

TASK = timer_1

### Usually you don’t need to modify this file below this line ###

prefix := $(shell xeno-config --prefix)

ifeq ($(prefix),)

$(error Please add <xeno-install>/bin to your PATH variable)

endif

CC = $(shell xeno-config --cc)

LXRT_CFLAGS = $(shell xeno-config --xeno-cflags)

LXRT_LDFLAGS = $(shell xeno-config --xeno-ldflags)

all: $(TASK)

$(TASK): $(TASK).c

$(CC) $(LXRT_CFLAGS) $(LXRT_LDFLAGS) -lnative -o $@ $<

clean:

rm -f *.o $(TASK)

.PHONY: clean

39

Page 44: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

7.2 Linux Driver

7.2.1 cpci ea221 driver.c

/*======================================================================*/

/* Traineeship-Project: Implementation of Xenomai */

/* Title: driver for CPCI EA221 card from Siemens */

/* Purpose: - driver module that enables to write to the registers */

/* of the card also from user space application */

/* */

/* File: cpci_ea221_driver.c */

/* Version: 0.1 */

/* Date: 08/2006 */

/* */

/* Description: - wake-up the device, allocate and map its memory, */

/* define ioctl functions to be used from user space, */

/* inform the kernel about new features */

/* - ONLY ONE CPCI-EA221 CARD SUPPORTED!!! */

/* Software: Linux Debian kernel 2.6.17.6 */

/* Hardware: Sicomp SMP16, CPCI-EA221 card from Siemens */

/* */

/* Author: Ondrej Cevan */

/* TU Vienna */

/* */

/* Note: This code is based on the code from the book */

/* "Linux Device Drivers, Third Edition" by */

/* Alessandro Rubini, Jonathan Corbet and */

/* Greg Kroah-Hartman, published by O’Reilly & Associates. */

/* */

/* No warranty is attached, we cannot take responsibility */

/* for errors or fitness for use. */

/*======================================================================*/

/**

* includes

*/

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/module.h>

#include <linux/moduleparam.h>

#include <linux/pci.h>

#include <linux/vmalloc.h>

40

Page 45: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

#include <asm/io.h>

#include <linux/ioport.h>

#include <linux/fs.h>

#include <linux/kdev_t.h>

#include <linux/cdev.h>

#include <asm/uaccess.h>

#include <linux/capability.h>

#include <linux/sched.h>

#include "cpci_ea221_driver.h"

MODULE_DESCRIPTION("cpci_ea221_driver");

MODULE_AUTHOR("Ondrej Cevan ([email protected])");

MODULE_LICENSE("GPL");

/**

* global variables

*/

int first_loop = 0;

int ret_loop_val = 0;

int cpci_ea221_major = CPCI_EA221_MAJOR;

int cpci_ea221_minor = CPCI_EA221_MINOR;

int cpci_ea221_nr_devs = CPCI_EA221_DEVS;

/**

* module parameters that can be passed in

*/

module_param(cpci_ea221_major, int, S_IRUGO);

//module_param(cpci_ea221_minor, int, S_IRUGO); not yet supported

/**

* define devices our driver supports

*/

static struct pci_device_id cpci_ea221_ids[]={

{PCI_DEVICE(CPCI_VENDOR_ID_SIEMENS, CPCI_DEVICE_ID_EA221)},

{0,},

};

/**

* export pci_device_id structure to user space, allowing hotplug

* and informing the module loading system about what module works

* with what hardware device

41

Page 46: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

*/

MODULE_DEVICE_TABLE (pci, cpci_ea221_ids);

/**

* create pci_driver structure,

* and register it in cpci_ea221_driver_init_module,

* unregister in cpci_ea221_driver_exit_module

*/

static struct pci_driver cpci_ea221_driver = {

.name = "cpci_ea221_driver",

.id_table = cpci_ea221_ids,

.probe = cpci_ea221_probe,

.remove = __devexit_p(cpci_ea221_remove),

};

/**

* internal structure

* info on our cpci_ea221 card

*/

struct cpci_ea221_struct{

/* address in kernel space (virtual memory)*/

void __iomem *base_address;

unsigned long location; /* physical address */

unsigned long mem_size; /* size/length of the memory */

struct cdev cdev; /* char device structure */

};

struct cpci_ea221_struct cpci_card_struct;

struct cpci_ea221_struct *cpci_ea221_card = &cpci_card_struct;

/**

* file operations structure

* (file of operations that an application can invoke on the device)

*/

struct file_operations cpci_ea221_fops = {

.owner = THIS_MODULE,

.ioctl = cpci_ea221_ioctl,

.open = cpci_ea221_open,

.release = cpci_ea221_release,

};

/**

* open function

42

Page 47: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

*

* get the pointer to the cpci_ea221_struct and store it in the

* private_data space

*/

int cpci_ea221_open(struct inode *inode, struct file *filp)

{

struct cpci_ea221_struct *dev; // device information

dev = container_of(inode->i_cdev, struct cpci_ea221_struct, cdev);

filp->private_data = dev; // store for other methods

return 0;

}

/**

* release function

*

* close device, most of the cleaning stuff is made in the

* function cpci_ea221_remove

*/

int cpci_ea221_release(struct inode *inode, struct file *filp)

{

return 0;

}

/**

* ioctl() implementation

*/

int cpci_ea221_ioctl(struct inode *inode, struct file *filp,

unsigned int cmd, unsigned long arg)

{

int err = 0;

int retval = 0;

ioc_param_struct param = {0,0};

// get the intern structure of the card from the private data

// space, could be useful when we will have more devices to concern

struct cpci_ea221_struct *private_struct =

(struct cpci_ea221_struct *) filp->private_data;

/*

* extract the type and number bitfields, and don’t decode

* wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()

43

Page 48: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

*/

if (_IOC_TYPE(cmd) != EA221_IOC_MAGIC) return -ENOTTY;

if (_IOC_NR(cmd) > EA221_IOC_MAXNR) return -ENOTTY;

/*

* the direction is a bitmask, and VERIFY_WRITE catches R/W

* transfers. ‘Type’ is user-oriented, while

* access_ok is kernel-oriented, so the concept of "read" and

* "write" is reversed (i.e. when an user wants to read data,

* the kernel has to write them into the user space)

*

* always check the user space address before accessing it

*

* access_ok returns 0 for failure!

*/

if (_IOC_DIR(cmd) & _IOC_READ)

err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));

else if (_IOC_DIR(cmd) & _IOC_WRITE)

err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));

if (err) return -EFAULT;

switch(cmd) {

case EA221_IOCRESET:

//software reset of the board

iowrite32(0x80000000,private_struct->base_address+0x08);

iowrite32(0x00,private_struct->base_address+0x08);

break;

case EA221_IOCWRITE:

//write to register

/* if(! capable(CAP_SYS_RAWIO|CAP_SYS_ADMIN))

return -EPERM;

*/ printk(KERN_DEBUG "writing to registers\n");

retval = __copy_from_user(&param, (ioc_param_struct __user *)arg,

sizeof(ioc_param_struct));

if(retval == 0) {//success

//@TODO do some checkins, check if offset correct...

iowrite32(param.bv,private_struct->base_address+param.offset);

}else{

return -EFAULT;

}

break;

44

Page 49: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

case EA221_IOCREAD:

//read from register, write to user

/* if(! capable(CAP_SYS_RAWIO|CAP_SYS_ADMIN))

return -EPERM;

*/ retval = __copy_from_user(&param, (ioc_param_struct __user *)arg,

sizeof(ioc_param_struct));

if(retval == 0) {//success

//@TODO some checkins, check if offset correct...

param.bv=ioread32(private_struct->base_address+param.offset);

if(!__copy_to_user((ioc_param_struct __user *)arg, &param,

sizeof(ioc_param_struct)))

return -EFAULT;

}else{

return -EFAULT;

}

break;

default:

return -ENOTTY;

}

return retval;

}

/**

* Set up the char_dev structure for this device.

*/

static int cpci_ea221_setup_cdev(struct cpci_ea221_struct *dev)

{

int err = 0;

dev_t devno = MKDEV(cpci_ea221_major, cpci_ea221_minor);

cdev_init(&dev->cdev, &cpci_ea221_fops);

dev->cdev.owner = THIS_MODULE;

dev->cdev.ops = &cpci_ea221_fops;

/* note: do not call cdev_add until the driver is completly ready

to handle operations on the device */

err = cdev_add (&dev->cdev, devno, 1);

/* Fail gracefully if need be */

if (err)

printk(KERN_NOTICE "Error %d adding cpci_ea221", err);

45

Page 50: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

return err;

}

/**

* Get major and minor number to work with, asking for a dynamic

* major unless directed otherwise at load or compile time.

*/

int allocate_device_number(void)

{

int result = 0;

dev_t dev = 0;

if (cpci_ea221_major) { //if major number defined

dev = MKDEV(cpci_ea221_major, cpci_ea221_minor);

result = register_chrdev_region(dev,

cpci_ea221_nr_devs, "cpci_ea221_driver");

} else { //if major num not defined (equals 0)- allocate dynamically

result = alloc_chrdev_region(&dev, cpci_ea221_minor,

cpci_ea221_nr_devs, "cpci_ea221_driver");

cpci_ea221_major = MAJOR(dev);

}

if (result < 0) {

printk(KERN_WARNING "cpci_ea221_driver: can’t get major %d\n",

cpci_ea221_major);

return result;

}

printk(KERN_DEBUG "cpci_ea221_driver: major number is %d\n",

cpci_ea221_major);

return result;

}

/**

* This function is called by the PCI core when

* it has a struct pci_dev that it thinks this driver wants to control.

*

* Purpose: initialize the device properly

*/

static int __devinit cpci_ea221_probe(struct pci_dev *dev,

const struct pci_device_id *id)

{

/*

46

Page 51: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

* @TODO when initializing module, this function "probe" runs two times

* WHY? ...and as a side effect base address is initialized two times...

*

* the following if-condition avoids the two time execution

*/

int ret_val = 0;

printk("cpci_ea221 card found!\n");

//if-condition evaluates to true in the second loop

if(first_loop){

//first_loop =1;

return ret_loop_val;

}

first_loop=1;

//wake up the device

ret_val = pci_enable_device(dev);

if(ret_val!=0){

printk( KERN_WARNING "function pci_enable_device failed\n");

goto pci_enable_device_err;

}

//initialization of physical location and mem_size

cpci_ea221_card->location = pci_resource_start(dev,CPCI_EA221_BAR);

cpci_ea221_card->mem_size = pci_resource_len(dev,CPCI_EA221_BAR);

//allocate memory region

if(request_mem_region(cpci_ea221_card->location,

cpci_ea221_card->mem_size, "CPCI_EA221_mem")==NULL){

printk( KERN_WARNING "memory allocation failed!\n");

ret_val = -EBUSY;

goto request_mem_region_err;

}

//map IO mem to kernel space

cpci_ea221_card->base_address=ioremap(cpci_ea221_card->location,

cpci_ea221_card->mem_size);

if(!cpci_ea221_card->base_address){

printk( KERN_WARNING "cannot remap memory region\n");

ret_val = -ENODEV;

goto ioremap_err;

47

Page 52: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

}

printk(KERN_DEBUG "base address@length is %p @ %lx\n",

cpci_ea221_card->base_address, cpci_ea221_card->mem_size);

//software reset of the board

iowrite32(0x80000000, cpci_ea221_card->base_address+0x08);

iowrite32(0x00, cpci_ea221_card->base_address+0x08);

// setup cdev structure

// afterwards the kernel can access the operations on the device

ret_val = cpci_ea221_setup_cdev(cpci_ea221_card);

if(ret_val != 0){

printk(KERN_WARNING "cannot add device to the system!\n");

goto cdev_setup_err;

}

return ret_val;

//clean up code in case of errors

cdev_setup_err:

iounmap(cpci_ea221_card->base_address);

ioremap_err:

release_mem_region(cpci_ea221_card->location,

cpci_ea221_card->mem_size);

request_mem_region_err:

pci_enable_device_err:

//to get the same ret_val in the second unwanted loop...

ret_loop_val = ret_val;

// @TODO: now if in this function an error occurs, the driver module

// will stay loaded in the kernel and will wait for another cpci card.

// It is so because of the plugin feature of the driver- the module

// is waiting for new cards and tries to register them.

// the question is: should the module be unloaded in case of an error?

return ret_val;

}

/**

48

Page 53: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

* function to be executed when unloading the driver module

*/

static void __devexit cpci_ea221_remove(struct pci_dev *dev)

{

//@TODO:when removing module/driver this function runs two times

//WHY? ...the following if-condition avoids the two time execution,

// it evaluates to true in the second loop

if(!first_loop){

//first_loop=0;

return;

}

first_loop=0;

printk( KERN_DEBUG "removing...\n" );

//software reset of the board

iowrite32(0x80000000,cpci_ea221_card->base_address+0x08);

iowrite32(0x00,cpci_ea221_card->base_address+0x08);

//remove char device from the system

cdev_del(&cpci_ea221_card->cdev);

printk( KERN_DEBUG "removing base address@lenght %p @ %lx\n", \

cpci_ea221_card->base_address, cpci_ea221_card->mem_size);

//release virtual memory

iounmap(cpci_ea221_card->base_address);

//release memory region

release_mem_region(cpci_ea221_card->location, cpci_ea221_card->mem_size);

}

/**

* init module function

*/

static int __init cpci_ea221_driver_init_module(void)

{

int ret_val = 0;

printk( KERN_DEBUG "Module cpci_ea221_driver init\n" );

ret_val = allocate_device_number();

ret_val = pci_register_driver(&cpci_ea221_driver);

return ret_val;

}

49

Page 54: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

/**

* exit module function

*/

static void __exit cpci_ea221_driver_exit_module(void)

{

dev_t dev_no = MKDEV(cpci_ea221_major, cpci_ea221_minor);

printk( KERN_DEBUG "Module cpci_ea221_driver exit\n" );

/*after unregistering all PCI devices bound to this driver

will be removed*/

pci_unregister_driver(&cpci_ea221_driver);

//deallocate device numbers

unregister_chrdev_region(dev_no, cpci_ea221_nr_devs);

}

module_init(cpci_ea221_driver_init_module);

module_exit(cpci_ea221_driver_exit_module);

50

Page 55: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

7.2.2 cpci ea221 driver.h

/*======================================================================*/

/* Traineeship-Project: Implementation of Xenomai */

/* Title: driver for CPCI EA221 card from Siemens */

/* Purpose: - driver module that enables to write to the registers */

/* of the card from user space */

/* */

/* File: cpci_ea221_driver.h */

/* Version: 0.1 */

/* Date: 08/2006 */

/* */

/* Description: header file for .c file of the driver */

/* Software: Linux Debian kernel 2.6.17.6 */

/* Hardware: Sicomp SMP16, CPCI-EA221 card from Siemens */

/* */

/* Author: Ondrej Cevan */

/* TU Vienna */

/* */

/* Note: This code is based on the code from the book */

/* "Linux Device Drivers, Third Edition" */

/* by Alessandro Rubini, Jonathan Corbet and */

/* Greg Kroah-Hartman, published by O’Reilly & Associates. */

/* */

/* No warranty is attached, we cannot take responsibility */

/* for errors or fitness for use. */

/*======================================================================*/

#ifndef _CPCI_EA221_H_

#define _CPCI_EA221_H_

/**

* include files

*/

#include <linux/ioctl.h> //needed for creating ioctl-cmd numbers

/**

* macros

*/

//identification numbers of our I/O card

#define CPCI_VENDOR_ID_SIEMENS 0x10EE

#define CPCI_SUBVENDOR_ID_SIEMENS 0x110A

51

Page 56: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

#define CPCI_DEVICE_ID_EA221 0x0300

#define CPCI_SUBDEVICE_ID_EA221 0x4018

//base address register

//BIOS or OS stores the base address of the card there

#define CPCI_EA221_BAR 0x00

#define CPCI_EA221_MAJOR 0x00 /* dynamic major by default */

#define CPCI_EA221_MINOR 0x00 /* value other then null is not */

/* supported by the load script */

#define CPCI_EA221_DEVS 0x01 /* number of CPC_EA221 devices */

/**

* Prototypes for shared functions

*/

static void __devexit cpci_ea221_remove(struct pci_dev *dev);

static int __devinit cpci_ea221_probe(struct pci_dev *dev,

const struct pci_device_id *id);

int allocate_device_number(void);

int cpci_ea221_ioctl(struct inode *inode, struct file *filp,

unsigned int cmd, unsigned long arg);

int cpci_ea221_release(struct inode *inode, struct file *filp);

int cpci_ea221_open(struct inode *inode, struct file *filp);

/**

* IOCTL stuff

*/

struct ioc_param{

uint32_t bv; /* 32bit vector to write to register. */

/* when reading from register, */

/* the value will be stored there */

uint8_t offset; /* offset address of the register we */

/* want to read from or write to */

};

typedef struct ioc_param ioc_param_struct;

#define EA221_IOC_MAGIC 0xEE

#define EA221_IOCRESET _IO(EA221_IOC_MAGIC, 0)

#define EA221_IOCWRITE _IOW(EA221_IOC_MAGIC, 1, ioc_param_struct)

#define EA221_IOCREAD _IOWR(EA221_IOC_MAGIC, 2, ioc_param_struct)

52

Page 57: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

//the highest ordinal number of our IOCTL functions

#define EA221_IOC_MAXNR 2

#endif /* _CPCI_EA221_H_ */

53

Page 58: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

7.2.3 test led.c

/*======================================================================*/

/* Traineeship-Project: Implementation of Xenomai */

/* Title: user-space test code for Linux cpci ea221 driver */

/* Purpose: - to test if it is possible to write into the card’s */

/* registers from user-space */

/* */

/* File: test_led.c */

/* Version: 0.1 */

/* Date: 08/2006 */

/* */

/* Description: - turn on the LED #1 of the card when one of the output */

/* bits of the I/O register is set HIGH. */

/* - set some output bits high and toggle their state after*/

/* pressing <enter> on the keyboard */

/* - the effect is that after pressing <enter> the LED #1 */

/* will change its state */

/* Software: Linux Debian kernel 2.6.17.6 */

/* Hardware: Sicomp SMP16, CPCI-EA221 card from Siemens */

/* */

/* Author: Ondrej Cevan (TU Vienna) */

/* License: GPL */

/* */

/* No warranty is attached, we cannot take responsibility */

/* for errors or fitness for use. */

/*======================================================================*/

/**

* includes

*/

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/ioctl.h>

#include <sys/fcntl.h>

#include <signal.h>

#include "test_led.h"

54

Page 59: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

/**

* global variables

*/

static int fd;

/**

* clean-up after receiving exit signal

*/

void catch_signal(int sig) {

ioctl(fd,EA221_IOCRESET);

close(fd);

fd = -1;

printf("exit\n");

return;

}

/**

* main function

*/

int main (int argc, char* argv[]){

char buf[BUFFER_LENGTH];

//define signal handler

signal(SIGTERM, catch_signal);

signal(SIGINT, catch_signal);

// define parameter structure ("where to write what")

// define the offset from base address

ioc_param_struct data1 = {0x00000000, 0x00}; // I/O register

ioc_param_struct data2 = {0x0000FFFF, 0x28}; // LED register

//open character device

fd = open("/dev/cpci_ea221", O_RDWR);

if ( fd < 0 ) {

printf( "Unable to open file\n" );

exit ( EXIT_FAILURE );

}

// Software-reset of the card

ioctl(fd,EA221_IOCRESET);

55

Page 60: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

// send the param structure to the driver

ioctl(fd, EA221_IOCWRITE, &data1);

ioctl(fd, EA221_IOCWRITE, &data2);

while(fd!=-1){

printf ("PRESS CTRL-C to EXIT or ENTER to switch the LED #1 state\n");

//read stdin

if (fgets(buf, sizeof(buf), stdin) != NULL)

{

//read device output register

ioctl(fd, EA221_IOCREAD, &data1);

//toggle the output state

data1.bv ^= 0x0000FFFF;

//write the new bit vector to the device register

ioctl(fd, EA221_IOCWRITE, &data1);

}

}

return 0;

}

56

Page 61: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

7.2.4 test led.h

/*======================================================================*/

/* Traineeship-Project: Implementation of Xenomai */

/* Title: user-space test code for Linux cpci ea221 driver */

/* Purpose: - to test if it is possible to write into the card’s */

/* registers from user-space */

/* */

/* File: test_led.h */

/* Version: 0.1 */

/* Date: 08/2006 */

/* */

/* Description: header file for test_led.c task */

/* Software: Linux Debian kernel 2.6.17.6 */

/* Hardware: Sicomp SMP16, CPCI-EA221 card from Siemens */

/* */

/* Author: Ondrej Cevan (TU Vienna) */

/* License: GPL */

/* */

/* No warranty is attached, we cannot take responsibility */

/* for errors or fitness for use. */

/*======================================================================*/

#include <linux/ioctl.h>

#define BUFFER_LENGTH 40

struct ioc_param {

unsigned long bv;

unsigned short offset;

};

typedef struct ioc_param ioc_param_struct;

#define EA221_IOC_MAGIC 0xEE

#define EA221_IOCRESET _IO(EA221_IOC_MAGIC, 0)

#define EA221_IOCWRITE _IOW(EA221_IOC_MAGIC, 1, ioc_param_struct)

#define EA221_IOCREAD _IOWR(EA221_IOC_MAGIC, 2, ioc_param_struct)

57

Page 62: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

7.2.5 Makefile

##########################################################################

## Traineeship-Project: Implementation of Xenomai ##

## Title: Makefile ##

## Purpose: to build cpci_ea221_driver module and a test application##

## ##

## File: Makefile ##

## Version: 0.1 ##

## Date: 08/2006 ##

## ##

## Description: - to build module outside of the kernel tree, we ##

## configure the Makefile to run from the kernel source ##

## tree ##

## - the test application will be build the usual way from ##

## its parent directory ##

## Software: Linux Debian kernel 2.6.17.6 ##

## Hardware: Sicomp SMP16, CPCI-EA221 card from Siemens ##

## ##

## Author: Ondrej Cevan (TU Vienna) ##

## Licence: GPL ##

## Note: This code is based on the code from the book ##

## "Linux Device Drivers, Third Edition" ##

## by Alessandro Rubini, Jonathan Corbet and ##

## Greg Kroah-Hartman, published by O’Reilly & Associates. ##

##########################################################################

### Name of the kernel module and user space application ####

KERNELMODULE ?= cpci_ea221_driver

USERAPP ?= test_led

### cc for user-space application

CC = gcc

#### Usually you don’t need to modify this file below this line ####

all: modules $(USERAPP)

ifneq ($(KERNELMODULE),)

# This conditional selects whether we are being included from the

# kernel Makefile or not.

ifeq ($(KERNELRELEASE),)

58

Page 63: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

# Assume the source tree is where the running kernel was built

# You should set KERNELDIR in the environment if it’s elsewhere

KERNELDIR ?= /lib/modules/$(shell uname -r)/build

# The current directory is passed to sub-makes as argument

PWD := $(shell pwd)

modules:

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:

rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

else

# called from kernel build system: just declare what our modules are

obj-m := $(KERNELMODULE).o

endif

endif

### user application begin

ifneq ($(USERAPP),)

$(USERAPP): $(USERAPP).c $(USERAPP).h

$(CC) -o $@ $<

clean::

rm -f $(USERAPP)

endif

### user application end

.PHONY: modules modules_install clean

59

Page 64: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

7.2.6 cpci ea221 load.sh

#!/bin/sh

##########################################################################

## Traineeship-Project: Implementation of Xenomai ##

## Title: loading script for cpci_ea221_driver module ##

## Purpose: - to set-up an environment for the driver execution ##

## ##

## File: cpci_ea221_load.sh ##

## Version: 0.1 ##

## Date: 08/2006 ##

## ##

## Description: - load the module into the kernel, and create the device##

## entry point (device node) ##

## - only ONE device node will be created!!! ##

## Software: Linux Debian kernel 2.6.17.6 ##

## Hardware: Sicomp SMP16, CPCI-EA221 card from Siemens ##

## ##

## Author: Ondrej Cevan (TU Vienna) ##

## Licence: GPL ##

## Note: This code is based on the code from the book ##

## "Linux Device Drivers, Third Edition" ##

## by Alessandro Rubini, Jonathan Corbet and ##

## Greg Kroah-Hartman, published by O’Reilly & Associates. ##

##########################################################################

module="cpci_ea221_driver"

device="cpci_ea221"

mode="666"

# invoke insmod with all arguments we got

# and use a pathname, as newer modutils don’t look in . by default

/sbin/insmod ./$module.ko $* || exit 1

# remove stale nodes

rm -f /dev/${device}

# search in /proc/devices for the major number

major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices)

# echo "major is $major"

# create device node with minor number NULL

60

Page 65: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

mknod /dev/${device} c $major 0

# give appropriate group/permissions, and change the group.

# Not all distributions have staff, some have "wheel" instead.

group="staff"

grep -q ’^staff:’ /etc/group || group="wheel"

chgrp $group /dev/${device}

chmod $mode /dev/${device}

61

Page 66: 57842309 Xenomai Implementation

7 Listings 7.2 Linux Driver

7.2.7 cpci ea221 unload.sh

#!/bin/sh

##########################################################################

## Traineeship-Project: Implementation of Xenomai ##

## Title: unloading script for cpci_ea221_driver module ##

## Purpose: - to clear an environment from cpci_ea221_driver module ##

## ##

## File: cpci_ea221_unload.sh ##

## Version: 0.1 ##

## Date: 08/2006 ##

## ##

## Description: - unload the module from the kernel, and delete ##

## the device entry point (device node) ##

## - only ONE device node will be deleted!!! ##

## Software: Linux Debian kernel 2.6.17.6 ##

## Hardware: Sicomp SMP16, CPCI-EA221 card from Siemens ##

## ##

## Author: Ondrej Cevan (TU Vienna) ##

## Licence: GPL ##

## Note: This code is based on the code from the book ##

## "Linux Device Drivers, Third Edition" ##

## by Alessandro Rubini, Jonathan Corbet and ##

## Greg Kroah-Hartman, published by O’Reilly & Associates. ##

##########################################################################

module="cpci_ea221_driver"

device="cpci_ea221"

# invoke rmmod with all arguments we got

/sbin/rmmod $module $* || exit 1

# Remove stale nodes

rm -f /dev/${device}

62

Page 67: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

7.3 Xenomai- RTDM Driver

7.3.1 xen cpciea221 driver.c

/*=====================================================================*/

/* Traineeship-Project: Implementation of Xenomai */

/* Title: real-time driver for CPCI EA221 */

/* Purpose: - hard real-time driver module that enables to write */

/* to the card’s registers from the user space */

/* */

/* File: xen_cpciea221_driver.c */

/* Version: 0.1 */

/* Date: 08/2006 */

/* */

/* Description: - wake-up device, allocate and map its memory, */

/* define ioctl functions to be used from the user space */

/* - ONLY ONE CPCI-EA221 CARD SUPPORTED!!! */

/* Software: Linux Debian kernel 2.6.17.6 with Xenomai-2.2.0 ext. */

/* Hardware: Sicomp SMP16, CPCI EA221 card from Siemens */

/* */

/* Author: Ondrej Cevan (TU Vienna) */

/* Note: - based on the code from the webpage www.captain.at, */

/* and from the book "Linux Device Drivers, Third Edition" by */

/* Alessandro Rubini, Jonathan Corbet and Greg Kroah-Hartman, */

/* published by O’Reilly & Associates . */

/* */

/* No warranty is attached, we cannot take responsibility */

/* for errors or fitness for use. */

/*=====================================================================*/

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/module.h>

#include <linux/fs.h>

#include <linux/pci.h>

//@TODO check if all includes needed

#include <linux/vmalloc.h>

#include <asm/io.h>

#include <linux/ioport.h>

#include <linux/kdev_t.h>

#include <linux/cdev.h>

63

Page 68: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

#include <asm/uaccess.h>

#include <linux/capability.h>

#include <linux/sched.h>

#include <rtdm/rtdm_driver.h>

#include "xen_cpciea221_driver.h"

/**

* module info

*/

MODULE_DESCRIPTION("RTDM driver for CPCI EA221");

MODULE_AUTHOR("ondrej cevan ([email protected])");

MODULE_LICENSE("GPL");

/**

* global variables

*/

int first_loop=0;

int ret_loop_val=0;

/**

* define devices our driver supports

*/

static struct pci_device_id cpci_ea221_ids[]={

{PCI_DEVICE(CPCI_VENDOR_ID_SIEMENS, CPCI_DEVICE_ID_EA221)},

{0,},

};

/**

* export pci_device_id structure to user space, allowing hotplug

*/

MODULE_DEVICE_TABLE (pci, cpci_ea221_ids);

/**

* create pci_driver structure,

* and register it in cpci_ea221_driver_init_module,

* unregister in cpci_ea221_driver_exit_module

*/

static struct pci_driver cpci_ea221_driver = {

.name = DRV_NAME,

.id_table = cpci_ea221_ids,

64

Page 69: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

.probe = cpci_ea221_probe,

.remove = __devexit_p(cpci_ea221_remove),

};

/**

* info on our cpci_ea221 card

*/

struct xen_cpciea221_context{

/* address in kernel space (virtual memory) */

void __iomem *base_address;

/* physical address */

unsigned long location;

/* size/length of the memory */

unsigned long mem_size;

struct rtdm_device *xen_device;

int dev_id;

};

struct xen_cpciea221_context xen_cpciea221_struct;

struct xen_cpciea221_context *xen_cpciea221_card = &xen_cpciea221_struct;

/**

* open named rt device

*

* as of version 0.1 is this function doing nothing necessary we would need

* for execution of other driver’s methods, but it will be useful

* in further development work, e.g. to start interrupt handler

*/

int xen_cpciea221_rt_open(struct rtdm_dev_context *context,

rtdm_user_info_t *user_info,

int oflags)

{

struct xen_cpciea221_context *my_context;

// get the id of the card from the rtdm_device struct

// of the owning device

int dev_id = context->device->device_id;

// get the begin of driver defined context data structure of our driver

my_context = (struct xen_cpciea221_context *)context->dev_private;

// store the id of the card in the xen_cpciea221_context struct.

// be aware!!! this structure is without any data we gave to it

// in the .probe()

65

Page 70: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

/* @TODO: get the xen_cpciea221_context struct with initialized data

like base_address, location, mem_size....

in Linux driver we used container_of() operation to do it

*/

my_context->dev_id = dev_id;

printk( KERN_DEBUG "opening dev with id:%d\n", my_context->dev_id);

return 0;

}

/**

* close named rt device

*

* as of version 0.1 nothing necessary is provided

* by this function

*/

int xen_cpciea221_rt_close(struct rtdm_dev_context *context,

rtdm_user_info_t *user_info)

{

struct xen_cpciea221_context *my_context;

// get the context struct

my_context = (struct xen_cpciea221_context *)context->dev_private;

printk( KERN_DEBUG "closing... id of dev is %d\n", my_context->dev_id);

return 0;

}

/**

* ioctl() implementation

*/

int xen_cpciea221_rt_ioctl(struct rtdm_dev_context *context,

rtdm_user_info_t *user_info,

int cmd,

void *arg)

{

int err = 0;

int retval = 0;

ioc_param_struct param = {0,0};

/*

* extract the type and number bitfields, and don’t decode

66

Page 71: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

* wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()

*/

if (_IOC_TYPE(cmd) != EA221_IOC_MAGIC) return -ENOTTY;

if (_IOC_NR(cmd) > EA221_IOC_MAXNR) return -ENOTTY;

/*

* the direction is a bitmask, and VERIFY_WRITE catches R/W

* transfers. ‘Type’ is user-oriented, while

* access_ok is kernel-oriented, so the concept of "read" and

* "write" is reversed (i.e. when an user wants to read data,

* the kernel has to write them into the user space)

*

* always check the user space address before accessing it

*

* access_ok returns 0 for failure!

*/

if (_IOC_DIR(cmd) & _IOC_READ)

err = !rtdm_read_user_ok(user_info, arg, sizeof(ioc_param_struct));

else if (_IOC_DIR(cmd) & _IOC_WRITE)

err = !rtdm_rw_user_ok(user_info, arg, sizeof(ioc_param_struct));

if (err) return -EFAULT;

switch(cmd) {

case EA221_IOCRESET:

//software reset of the board

iowrite32(0x80000000,xen_cpciea221_card->base_address+0x08);

iowrite32(0x00,xen_cpciea221_card->base_address+0x08);

break;

case EA221_IOCWRITE:

//write to register

/* if(! capable(CAP_SYS_RAWIO|CAP_SYS_ADMIN))

return -EPERM;

*/ //printk(KERN_DEBUG "writing to registers\n");

retval = rtdm_copy_from_user(user_info, &param,

arg, sizeof(ioc_param_struct));

if(retval == 0) {//success

//@TODO do some checkins, if offset correct...

iowrite32(param.bv,

xen_cpciea221_card->base_address+param.offset);

67

Page 72: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

}else{

return -EFAULT;

}

break;

case EA221_IOCREAD:

//read from register, write to user

/* if(! capable(CAP_SYS_RAWIO|CAP_SYS_ADMIN))

return -EPERM;

*/

retval = rtdm_copy_from_user(user_info, &param,

arg, sizeof(ioc_param_struct));

if(retval == 0) {//success

//@TODO some checkins, if offset incorrect...

param.bv=ioread32(xen_cpciea221_card->base_address+param.offset);

if(!rtdm_copy_to_user(user_info,

arg, &param, sizeof(ioc_param_struct)))

return -EFAULT;

}else{

return -EFAULT;

}

break;

default:

return -ENOTTY;

}

return retval;

return 0;

}

/**********************************************************/

/* DRIVER OPERATIONS */

/**********************************************************/

static const struct rtdm_device xen_cpciea221_driver = {

struct_version: RTDM_DEVICE_STRUCT_VER,

device_flags: RTDM_NAMED_DEVICE | RTDM_EXCLUSIVE,

context_size: sizeof(struct xen_cpciea221_context),

device_name: "", //DEV_FILE,

//don’t try to find DEV_FILE in /dev/ - it’s not there -

// this is an RTDM internal identifier

68

Page 73: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

//you can find it in /proc/iomem

/* If you do not use kmalloc and kfree, and you made

sure that there is no syscall in the open/close handler, you

can declare the open_rt and close_rt handler.

*/

open_rt: xen_cpciea221_rt_open,

open_nrt: xen_cpciea221_rt_open,

ops: {

close_rt: xen_cpciea221_rt_close,

close_nrt: xen_cpciea221_rt_close,

ioctl_rt: xen_cpciea221_rt_ioctl,

ioctl_nrt: NULL,

read_rt: NULL,

read_nrt: NULL,

write_rt: NULL,

write_nrt: NULL,

},

device_class: RTDM_CLASS_SERIAL,

device_sub_class: 0, //222, //????

driver_name: DRV_NAME, //informational driver name

peripheral_name: "CPCI EA221 rtdm", // Informational name

// the device is attached to

provider_name: "Ondrej Cevan", // Informational name

};

/**

* This function is called by the PCI core when

* it has a struct pci_dev that it thinks this driver wants to control.

*

* Purpose: initialize the device properly

* +set up the rtdm_device structure for this device

*/

static int __devinit cpci_ea221_probe(struct pci_dev *dev,

const struct pci_device_id *id)

{

69

Page 74: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

/*

* @TODO when initializing module, this function "probe" runs two times

* WHY?

* and as a side effect base address is initialized two times...

*

* the following if-condition avoids two time execution

*/

struct rtdm_device *rtdm_dev;

int ret_val = 0;

printk( "cpci_ea221 card found!\n" );

//if-condition evaluates to true in the second loop

if(first_loop){

//first_loop =1;

return ret_loop_val;

}

first_loop=1;

//wake up the device

ret_val = pci_enable_device(dev);

if(ret_val!=0){

printk( KERN_WARNING

"xen_cpciea221_driver: function pci_enable_device failed\n");

goto pci_enable_device_err;

}

printk(KERN_DEBUG "device woke up!\n");

//initialization of location and mem_size

xen_cpciea221_card->location = pci_resource_start(dev,CPCI_EA221_BAR);

xen_cpciea221_card->mem_size = pci_resource_len(dev,CPCI_EA221_BAR);

//alloc mem for rtdm structure

rtdm_dev = kmalloc(sizeof(struct rtdm_device), GFP_KERNEL);

if(!rtdm_dev){

printk(KERN_WARNING "xen_cpciea221_driver: kmalloc failed\n");

ret_val = -ENOMEM; //Insufficient storage space is available.

goto kmalloc_err;

}

//copy the structure to the new memory

memcpy(rtdm_dev, &xen_cpciea221_driver, sizeof(struct rtdm_device));

70

Page 75: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

//create filename

snprintf(rtdm_dev->device_name,

RTDM_MAX_DEVNAME_LEN, "rtser%d", 0 /*i*/);

rtdm_dev->device_id = 0; //i;

//define two other members of the rtdm_device structure

rtdm_dev->proc_name = rtdm_dev->device_name;

//allocate memory region for CPCI card

if(request_mem_region(xen_cpciea221_card->location,

xen_cpciea221_card->mem_size, rtdm_dev->device_name)==NULL){

printk(KERN_WARNING

"xen_cpciea221_driver: device memory allocation failed!\n");

ret_val = -EBUSY;

goto request_mem_region_err;

}

printk( KERN_DEBUG "going to map memory to kernel space\n");

//map IO mem to kernel space

xen_cpciea221_card->base_address=ioremap(xen_cpciea221_card->location,

xen_cpciea221_card->mem_size);

if(!xen_cpciea221_card->base_address){

printk(KERN_WARNING

"xen_cpciea221_driver: cannot remap memory region\n");

ret_val = -ENODEV;

goto ioremap_err;

}

printk(KERN_DEBUG "base address@length is %p @ %lx\n",

xen_cpciea221_card->base_address, xen_cpciea221_card->mem_size);

//software reset of the board

iowrite32(0x80000000,xen_cpciea221_card->base_address+0x08);

iowrite32(0x00,xen_cpciea221_card->base_address+0x08);

ret_val = rtdm_dev_register(rtdm_dev);

if(ret_val < 0){

printk(KERN_WARNING

"xen_cpciea221_driver: cannot register device\n");

goto dev_register_err;

}

71

Page 76: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

xen_cpciea221_card->xen_device = rtdm_dev;

return ret_val;

//clean up code in case of errors

dev_register_err:

iounmap(xen_cpciea221_card->base_address);

ioremap_err:

release_mem_region(xen_cpciea221_card->location,

xen_cpciea221_card->mem_size);

request_mem_region_err:

kfree(rtdm_dev);

kmalloc_err:

pci_enable_device_err:

//to get the same ret_val in the second unwanted loop...

ret_loop_val = ret_val;

// @TODO: now if in this function an error occurs,

// the driver module will stay loaded

// in the kernel and will wait for another cpci card.

// It is so because of the plugin

// feature of the driver.

// the question is: should the module be unloaded in case

// of an error?

return ret_val;

}

static void __devexit cpci_ea221_remove(struct pci_dev *dev)

{

//@TODO:when removing module/driver this function runs two times

//WHY?

//the next if condition avoids executing the body of the function

//more than one time. if-condition evaluates to true in the second

//loop.

if(!first_loop){

72

Page 77: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

//first_loop=0;

return;

}

first_loop=0;

printk( KERN_DEBUG "removing...\n" );

//remove char device from the system

//unregister RTdriver

rtdm_dev_unregister(xen_cpciea221_card->xen_device, 1000);

//software reset of the board

iowrite32(0x80000000,xen_cpciea221_card->base_address+0x08);

iowrite32(0x00,xen_cpciea221_card->base_address+0x08);

printk( KERN_DEBUG "removing base address@lenght %p @ %lx\n", \

xen_cpciea221_card->base_address, xen_cpciea221_card->mem_size);

//release virtual memory

iounmap(xen_cpciea221_card->base_address);

//release memory region

release_mem_region(xen_cpciea221_card->location,

xen_cpciea221_card->mem_size);

//free allocated memory

kfree(xen_cpciea221_card->xen_device);

}

/**

* Init module function

*/

static int xen_cpciea221_driver_init_module(void)

{

static int ret_val;

printk( KERN_DEBUG "Module xen_cpciea221_driver init\n" );

//ret_val = allocate_device_number(); //not needed for rtdm

ret_val = pci_register_driver(&cpci_ea221_driver);

return ret_val;

}

/**

* Exit module function

*/

73

Page 78: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

static void xen_cpciea221_driver_exit_module(void)

{

printk( KERN_DEBUG "Module xen_cpciea221_driver exit\n" );

pci_unregister_driver(&cpci_ea221_driver);

}

module_init(xen_cpciea221_driver_init_module);

module_exit(xen_cpciea221_driver_exit_module);

74

Page 79: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

7.3.2 xen cpciea221 driver.h

/*=====================================================================*/

/* Traineeship-Project: Implementation of Xenomai */

/* Title: real-time driver for CPCI EA221 */

/* Purpose: - real-time driver that enables to write to the card’s */

/* registers from user space */

/* */

/* File: xen_cpciea221_driver.c */

/* Version: 0.1 */

/* Date: 08/2006 */

/* */

/* Description: needed macros for .c file of the driver */

/* Software: Linux Debian kernel 2.6.17.6 with Xenomai-2.2.0 ext. */

/* Hardware: Sicomp SMP16, CPCI EA221 card */

/* */

/* Author: Ondrej Cevan (TU Vienna) */

/* Note: - based on the code from the webpage www.captain.at, */

/* and from the book "Linux Device Drivers, Third Edition" by */

/* Alessandro Rubini, Jonathan Corbet and Greg Kroah-Hartman, */

/* published by O’Reilly & Associates . */

/* */

/* No warranty is attached, we cannot take responsibility */

/* for errors or fitness for use. */

/*=====================================================================*/

#ifndef _XEN_CPCIEA221_DRIVER_

#define _XEN_CPCIEA221_DRIVER_

/**

* include files

*/

#include <linux/ioctl.h> //needed for creating ioctl-cmd numbers

//#define DEV_FILE "xen_cpciea221_0"

//#define DEV_FILE_NAME "xen_cpciea221_device"

#define DRV_NAME "xen_cpciea221_driver"

#define CPCI_VENDOR_ID_SIEMENS 0x10EE

#define CPCI_SUBVENDOR_ID_SIEMENS 0x110A

#define CPCI_DEVICE_ID_EA221 0x0300

#define CPCI_SUBDEVICE_ID_EA221 0x4018

#define CPCI_EA221_BAR 0x00

75

Page 80: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

static int __devinit cpci_ea221_probe(struct pci_dev *dev, \

const struct pci_device_id *id);

static void __devexit cpci_ea221_remove(struct pci_dev *dev);

/**

* IOCTL stuff

*/

struct ioc_param{

uint32_t bv; /* 32bit vector to write to register, */

/* when reading from register, */

/* the value will be stored there */

uint8_t offset; /* offset address of the register we */

/* want to read from or write to */

};

typedef struct ioc_param ioc_param_struct;

#define EA221_IOC_MAGIC 0xEE

//define numbers of our ioctl functions

#define EA221_IOCRESET _IO(EA221_IOC_MAGIC, 0)

#define EA221_IOCWRITE _IOW(EA221_IOC_MAGIC, 1, ioc_param_struct)

#define EA221_IOCREAD _IOWR(EA221_IOC_MAGIC, 2, ioc_param_struct)

//the highest ordinal number of our IOCTL functions

#define EA221_IOC_MAXNR 2

#endif /* _XEN_CPCIEA221_H_ */

76

Page 81: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

7.3.3 xen led timer.c

/*=====================================================================*/

/* Traineeship-Project: Implementation of Xenomai */

/* Title: Xenomai user-space test application */

/* Purpose: - to test the Xenomai real-time driver module */

/* */

/* File: xen_led_timer.c */

/* Version: 0.1 */

/* Date: 08/2006 */

/* */

/* Description: - read/write periodically from/to device registers */

/* - the read value is XORed and written to output */

/* Software: Linux Debian kernel 2.6.17.6 with Xenomai-2.2.0 ext. */

/* Hardware: Sicomp SMP16, CPCI EA221 card */

/* */

/* Author: Ondrej Cevan */

/* TU Vienna */

/* Note: - Based on the code from the webpage www.captain.at */

/* */

/* No warranty is attached, we cannot take responsibility */

/* for errors or fitness for use. */

/*=====================================================================*/

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/ioctl.h>

#include <sys/fcntl.h>

#include <signal.h>

#include <errno.h>

#include <native/task.h>

#include <native/timer.h>

#include <rtdm/rtdm.h>

#include <sys/mman.h>

#include "xen_led_timer.h"

int fd = -1;

77

Page 82: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

unsigned int my_state = 0;

const char *cmnd = "<not yet set>";

RT_TASK toggle_led_task;

/**

* close RT driver

*/

static int close_file(int fd) {

int ret,i=0;

do {

i++;

ret = rt_dev_close(fd);

switch(-ret){

case EBADF: printf("%s -> invalid fd or context\n",cmnd);

break;

case EAGAIN: printf("%s -> EAGAIN (%d times)\n",cmnd,i);

rt_task_sleep(50000); // wait 50us

break;

case 0: printf("%s -> closed\n",cmnd);

break;

default: printf("%s -> ???\n",cmnd);

break;

}

} while (ret == -EAGAIN && i < 10);

return ret;

}

/**

* clean up

* close opened file descriptor and delete rt task

*/

void cleanup_all(void) {

if (my_state & STATE_FILE_OPENED) {

close_file(fd);

my_state &= ~STATE_FILE_OPENED;

}

if (my_state & STATE_TASK_CREATED) {

printf("delete task %s\n", cmnd);

rt_task_delete(&toggle_led_task);

my_state &= ~STATE_TASK_CREATED;

}

}

/**

78

Page 83: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

* signal handler

*/

void catch_signal(int sig) {

my_state |= SHUTDOWNNOW;

ioctl(fd,EA221_IOCRESET);

cleanup_all();

printf("exit\n");

return;

}

/**

* REAL TIME TASK

*/

void toggling_led_proc(void *arg) {

int ret = 0;

RTIME task_period_ns= 1000000000;

#ifndef SIMPLE

unsigned long new_input = 0x0;

unsigned long old_input = 0x0;

#endif

// Software-reset of the card

rt_dev_ioctl(fd,EA221_IOCRESET);

// init I/O register, no output, no input

ioc_param_struct data1 = {0x00000000, 0x00};

rt_dev_ioctl(fd, EA221_IOCWRITE, &data1);

// LED register 0, if any output the LED 1 will switch on

ioc_param_struct data2 = {0x0000FFFF, 0x28};

rt_dev_ioctl(fd, EA221_IOCWRITE, &data2);

// LED register 1, if any input high LED 3 on,

// if any input low LED 4 on

ioc_param_struct data3 = {0xFFFFFFFF,0x2C};

rt_dev_ioctl(fd, EA221_IOCWRITE, &data3);

/* Make a task periodic by programing its first release point and

its period in the processor time line.

*/

ret = rt_task_set_periodic(NULL, TM_NOW,

rt_timer_ns2ticks(task_period_ns));

if (ret) {

79

Page 84: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

printf("%s: error while set periodic, code %d\n",cmnd, ret);

return;

}

while(!(my_state & SHUTDOWNNOW)){

// Wait for the next periodic release point

ret = rt_task_wait_period(NULL);

if (ret) {

printf("%s: error while rt_task_wait_period, code %d\n",cmnd, ret);

return;

}

#ifdef PRINTF

printf("message from testtask\n");

fflush(NULL);

#endif

#ifdef SIMPLE //we don’t care about input

//read I/O register

rt_dev_ioctl(fd, EA221_IOCREAD, &data1);

//toggle output state

data1.bv ^= 0x0000FFFF;

rt_dev_ioctl(fd, EA221_IOCWRITE, &data1);

#else //little more complex task

//read I/O register

rt_dev_ioctl(fd, EA221_IOCREAD, &data1);

new_input = (long)(data1.bv >>16);

if(new_input == old_input)

//XOR the old output state, keep the same input

data1.bv = ((data1.bv & 0xFFFF) ^ 0xFFFF) | (new_input << 16);

else{

// if input changed put the new states of input pins on

// the output pins

old_input = new_input;

data1.bv = new_input;

}

rt_dev_ioctl(fd, EA221_IOCWRITE, &data1);

#endif

}

printf("exit\n");

80

Page 85: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

}

/**

* main function

*

* initialize what’s needed, open char device, start rt-task

*/

int main (int argc, char* argv[]){

int ret = 0;

cmnd = argv[0];

signal(SIGTERM, catch_signal);

signal(SIGINT, catch_signal);

/* no memory-swapping for this programm */

mlockall(MCL_CURRENT | MCL_FUTURE);

/* open haracter device */

// it’s not in /dev/directory!!!

fd = rt_dev_open(DEV_NAME, O_RDWR);

if (fd < 0) {

printf("%s: cannot open %s\n", cmnd, DEV_NAME);

goto error;

}

my_state |= STATE_FILE_OPENED;

printf("%s: %s opened\n",cmnd, DEV_NAME);

/* create and start my_task */

ret = rt_task_spawn(&toggle_led_task, "toggling_led",

STACK_SIZE, STD_PRIO, 0,

&toggling_led_proc, NULL);

if (ret) {

printf("%s: failed to create and start RT task, code %d\n",cmnd, ret);

goto error;

}

my_state |= STATE_TASK_CREATED;

printf("%s: RT task created and started\n", cmnd);

//suspend the thread until a signal is received

pause();

return 0;

81

Page 86: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

error:

cleanup_all();

fflush(NULL);

return ret;

}

82

Page 87: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

7.3.4 xen led timer.h

/*=====================================================================*/

/* Traineeship-Project: Implementation of Xenomai */

/* Title: Xenomai user-space test application */

/* Purpose: - to test the Xenomai real-time driver module */

/* */

/* File: xen_led_timer.h */

/* Version: 0.1 */

/* Date: 08/2006 */

/* */

/* Description: - header file to xen_led_timer.c */

/* Software: Linux Debian kernel 2.6.17.6 with Xenomai-2.2.0 ext. */

/* Hardware: Sicomp SMP16, CPCI EA221 card */

/* */

/* Author: Ondrej Cevan */

/* TU Vienna */

/* Note: - Based on the code from the webpage www.captain.at */

/* */

/* No warranty is attached, we cannot take responsibility */

/* for errors or fitness for use. */

/*=====================================================================*/

#include <linux/ioctl.h> //needed for creating ioctl-cmd numbers

//#define SIMPLE // run just simple task, as test task for linux kernel

//#define PRINTF //if you want to use printf in rt task, not recommended

#define STACK_SIZE 0

#define STD_PRIO 99

#define BUFFER_LENGTH 40

struct ioc_param {

unsigned long bv;

unsigned short offset;

};

typedef struct ioc_param ioc_param_struct;

#define EA221_IOC_MAGIC 0xEE

83

Page 88: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

#define EA221_IOCRESET _IO(EA221_IOC_MAGIC, 0)

#define EA221_IOCWRITE _IOW(EA221_IOC_MAGIC, 1, ioc_param_struct)

#define EA221_IOCREAD _IOWR(EA221_IOC_MAGIC, 2, ioc_param_struct)

#define DEV_NAME "rtser0"

#define STATE_FILE_OPENED 1

#define STATE_TASK_CREATED 2

#define SHUTDOWNNOW 4

84

Page 89: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

7.3.5 Makefile

####################################################################

# MAKEFILE TEMPLATE FOR KERNEL MODULES AND USER SPACE APPLICATIONS #

# USING XENOMAI #

# V0.3 (C) 2006 www.captain.at #####################################

####################################################################

### Names of the kernel module(s) and user space application(s) ####

### separated by spaces ##########################################

### (omit names if no module(s) or application(s) is(are) built) ###

KERNELMODULES ?= xen_cpciea221_driver

USERAPPS ?= xen_led_timer

### SKIN = xeno, posix etc.

SKIN ?= xeno

UNAME := $(shell uname -r)

KERNELSOURCEDIR ?= /lib/modules/$(UNAME)/build

### Xenomai directory, xeno-config and library directory ###########

XENO_DIR ?= /usr/xenomai

XENO_CONFIG ?= $(XENO_DIR)/bin/xeno-config

XENO_LIB_DIR ?= $(shell $(XENO_CONFIG) --library-dir)

### User space application compile options #########################

USERAPP_LIBS ?= -lnative -lrtdm

USERAPP_LDFLAGS ?= $(shell $(XENO_CONFIG) --$(SKIN)-ldflags)

USERAPP_CFLAGS ?= $(shell $(XENO_CONFIG) --$(SKIN)-cflags)

### General configuration stuff ####################################

CC = $(shell $(XENO_CONFIG) --cc)

####################################################################

#### Usually you don’t need to modify this file below this line ####

####################################################################

################ KERNEL MODULE START ###############################

OBJS := ${patsubst %, %.o, $(KERNELMODULES)}

CLEANTHIS := ${patsubst %, .%*, $(KERNELMODULES)}

ifneq ($(KERNELMODULES),)

ifeq ($(findstring 2.6,$(KERNELSOURCEDIR)),2.6)

85

Page 90: 57842309 Xenomai Implementation

7 Listings 7.3 Xenomai- RTDM Driver

### KERNEL 2.6 #####################################################

obj-m := $(OBJS)

EXTRA_CFLAGS := -I$(XENO_DIR)/include

PWD := $(shell pwd)

all:: $(USERAPPS)

$(MAKE) -C $(KERNELSOURCEDIR) SUBDIRS=$(PWD) modules

else ###############################################################

### KERNEL 2.4 #####################################################

HELP := -I$(KERNELSOURCEDIR)/include/xenomai/compat -I$(XENO_DIR)/include

INCLUDE := -I$(KERNELSOURCEDIR)/include $(HELP)

CFLAGS := -O2 -Wall -DMODULE -D__KERNEL__ -DLINUX $(INCLUDE)

all:: $(OBJS) $(USERAPPS)

endif

endif

clean::

$(RM) $(CLEANTHIS) *.cmd *.o *.ko *.mod.c *~

$(RM) -R .tmp*

################ KERNEL MODULE END #################################

################ USER APPLICATIONS START ###########################

ifneq ($(USERAPPS),)

USERSP := ${patsubst %, %.c, $(USERAPPS)}

$(USERAPPS): $(USERSP)

$(CC) -o $@ $< $(USERAPP_CFLAGS) $(USERAPP_LDFLAGS) $(USERAPP_LIBS)

endif

clean::

$(RM) $(USERAPPS)

################# USER APPLICATIONS END ############################

.PHONY: clean

86

Page 91: 57842309 Xenomai Implementation

References

[ade] Adeos- Project Website. http://home.gna.org/adeos.

[CRKH05] Jonathan Corbet, Alessandro Rubini, and Greg Kroah-Hartman.Linux Device Drivers, Third Edition. O′Reilly & Associates, USA,2005. The book is available at http://lwn.net/Kernel/LDD3, theprograms at ftp://ftp.ora.com/pub/examples/linux/drivers.

[Fou] Free Software Foundation. GRUB Manual. Available at www.gnu.org/software/grub/manual.

[Ger04] Philippe Gerum. White Paper, 2004. Available at www.xenomai.

org in submenu Documentation.

[Ger05] Philippe Gerum. Life with Adeos, 2005. Available at www.xenomai.org in submenu Documentation.

[Gro04] The Open Group. Base Specifications Issue 6, 2004. http://www.

opengroup.org/onlinepubs/009695399.

[kera] Homepage of Kernel Hacking. www.kernelhacking.org.

[kerb] Homepage of Linux Kernel. http://kernel.org.

[Kis05] Jan Kiszka. The Real-Time Driver Model and First Applications.University of Hannover, Germany, 2005. www.linuxdevices.com/

files/rtlws-2005/JanKiszka.pdf.

[Len] Romain Lenglet. Installation of Xenomai Real TimeLinux. www.csg.is.titech.ac.jp/~lenglet/howtos/

realtimelinuxhowto/index.html.

[lxc] Kernel Hacking on Linuxchix Webpage. www.linuxchix.org/

content/courses/kernel_hacking.

[rta] Official Website of RTAI. www.rtai.org.

87

Page 92: 57842309 Xenomai Implementation

References References

[Siea] Siemens. CPCI-EA221 Technical Description. Available athttps://support.automation.siemens.com/GB/llisapi.dll,search for the cpci-ea221 with the search engine.

[Sieb] Siemens. Sicomp. http://www.automation.siemens.com/

sicomp/index_76.htm.

[Sko] Miroslav Skoric. LILO Manual. Available at http://en.tldp.

org/HOWTO/LILO.html.

[Unia] Captain’s Universe. Linux Kernel Compilation HowTo. www.

captain.at/programming/kernel.

[Unib] Captain’s Universe. Xenomai. www.captain.at/xenomai.php.

[xena] GNA Summary of Xenomai Project. https://gna.org/projects/xenomai.

[xenb] Homepage of Xenomai Project. www.xenomai.org.

[xenc] Xenomai- API. http://snail.fsffrance.org/www.xenomai.

org/documentation/trunk/html/api%/index.html.

88