Vous êtes sur la page 1sur 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

Installation and Implementation of Xenomai on an Industrial Component and Porting a Driver for an Industrial IO-Card to the Xenomai Infrastructure
Xenomai is a new real-time operating system emulation framework based on GNU/Linux. It was designed with the goal to help application designers using traditional and proprietary real-time operating systems to move as smoothly as possible to a GNU/Linux based execution environment. The aim of this work is to establish a Xenomai environment on an industrial computer from Siemens and write a simple device driver for the digital I/O card CPCI-EA221 using the Real-Time Driver Model (RTDM) which is integrated in Xenomai. To achieve this goal, rst a simple real-time userspace task under Xenomai was created. As a second step a Linux device driver 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 Xenomai real-time environment in the industry. This work is supposed to be used for further research and working on Xenomai at the university mainly in the automation sphere.

Contents
1 Introduction 1.1 Xenomai . . . . 1.2 Workplace . . . 1.3 Source Code . . 1.4 Structure of the . . . . . . . . . . . . . . . . . . . . . . . . . . . Project Report . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2 2 4 5 6 6 6 8 9 12 12 13 14 15 19 . . . . . . . . . . . . Argument . . . . . . . . . . . . Driver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Structure: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 21 23 24 24 25 26 26 28 28 28

2 Xenomai an Installation Guide 2.1 System Requirements . . . . . . . . . . . 2.1.1 Preinstallation Steps . . . . . . . 2.2 Downloading and Unpacking the Sources 2.3 Compiling . . . . . . . . . . . . . . . . . 2.4 Setting up Bootloader and Testing . . . 2.4.1 Conguring LILO . . . . . . . . . 2.4.2 Conguring GRUB . . . . . . . . 2.4.3 Testing . . . . . . . . . . . . . . . 2.4.4 Error Handling . . . . . . . . . . 3 Xenomai- First Application 4 Linux Driver 4.1 Programming the Driver 4.2 API . . . . . . . . . . . 4.2.1 IOCTL Code and 4.2.2 Examples: . . . . 4.3 Test Application . . . . 5 Xenomai RTDM Driver 5.1 Programming the RTDM 5.2 API . . . . . . . . . . . 5.2.1 Examples: . . . . 5.3 Test Application . . . .

6 Conclusion 30 6.1 Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 6.2 Outlook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

ii

7 Listings 7.1 Xenomai First Application . 7.1.1 timer 1.c . . . . . . . . 7.1.2 timer 2.c . . . . . . . . 7.1.3 Makele . . . . . . . . 7.2 Linux Driver . . . . . . . . . . 7.2.1 cpci ea221 driver.c . . 7.2.2 cpci ea221 driver.h . . 7.2.3 test led.c . . . . . . . . 7.2.4 test led.h . . . . . . . 7.2.5 Makele . . . . . . . . 7.2.6 cpci ea221 load.sh . . . 7.2.7 cpci ea221 unload.sh . 7.3 Xenomai- RTDM Driver . . . 7.3.1 xen cpciea221 driver.c 7.3.2 xen cpciea221 driver.h 7.3.3 xen led timer.c . . . . 7.3.4 xen led timer.h . . . . 7.3.5 Makele . . . . . . . . References

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

32 33 33 36 39 40 40 51 54 57 58 60 62 63 63 75 77 83 85 87

iii

1 Introduction
This project report is intended to be used as a manual for establishing Xenomai environment which runs together with Linux distribution and for writing rst Linux device driver and Xenomai device driver for CPCI1 cards. We chose the way of writing this report as a manual because this project should explain installing the Xenomai environment and make the step-in phase for programming device drivers mainly in Xenomai smoother. The following sections will briey introduce the major facts on our working environment and then the next chapters will show you the way to the Xenomai driver for a CPCI card.

a standard based on PCI, see section 1.2

Figure 1.1: the workplace

1 Introduction

1.1 Xenomai

1.1 Xenomai
There are dierent real-time operating systems (RTOS) these days using different APIs, but one can nd common features and behaviors among many of them. Major embedded RTOS, such as VRTX, VxWorks, pSOS+ and a few others, have implemented a real-time kernel behavior which has become a de facto standard. Xenomai uses these similarities to build a nucleus oering set of generic services. These services build an architecture-neutral abstraction layer, that provides the foundation for developing emulation modules of real-time application programming interfaces, which mimic the corresponding real-time kernel APIs. One can set the Xenomais emulators or APIs for realtime operating systems at conguring the kernel in the kernel menu [Real-time sub-system->Interfaces]. We will come to it in the next chapter. Of course there is also the possibility to use the Xenomais native API, which was used at programming the applications for this project. For a detailed report on implementing a RTOS emulation framework on GNU/Linux see the paper [Ger04]. The aim which Xenomai wants to achieve with this approach, using the similarities among traditional RTOS, is to oer the application designers a smooth and comfortable way for migrating the real-time applications from traditional RTOS to GNU/Linux. To make Xenomais tasks hard real-time in GNU/Linux a Real-Time Application Interface (RTAI) [rta] co-kernel is used. It allows to run real-time tasks seamlessly aside of the hosting GNU/Linux system while the tasks of the regular Linux kernel can be seen as running in a low-priority mode. The RTAI co-kernel shares hardware interrupts and system-originated events like traps and faults with the Linux kernel using the Adaptive Domain Environment for Operating Systems (Adeos)2 [ade, Ger05] virtualization layer, which in turn ensures RTAI low interrupt latencies. You can nd 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 in Emden, Germany, in July- August 2006. At our disposal we had a Siemens industrial computer Sicomp SMP16, gure 1.2, and a Siemens digital I/O card CPCI-EA221, gure 1.3.
2 3

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

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 modular, open board architecture system that allows hardware systems to be tailored individually to a task and combined with customer developments. Our Sicomp had a SMP16 CPCI central processing unit (CPU) with the 1266MHz power and a CPCI bus interface. The hardware is PC compatible and has been expanded with additional safety functions. Compact PCI (CPCI) is a high-performace standard based upon peripheral component interconnect (PCI) technology and is a norm of the PICMG (PCI

1 Introduction

1.3 Source Code

Figure 1.4: input/output control box Industrial Computer Manufactures Group). Because it is based on PCI, device drivers for CPCI can use the same design approach as normally used for PCI device drivers. The CPCI-EA221 card is a plug-in computer card. It has 16 input and 16 output pins and 4 control LEDs on the front panel. The control LEDs signal the state of the input/output pins, depending on the conguration setting. The possible congurations and other technical information on the device can be found in [Siea]. To test the inputs and outputs of the card we used a device connected to the port of the card, picture 1.4. It was also connected to a 24V source as the input voltage of the card is that high. On the leds of this test device we could observe the states of all of the 16 output pins and set the state of every of the input pin with the devices buttons. On our Sicomp computer a Linux distribution, Debian GNU/Linux, release 3.1 (sarge), with Linux kernel 2.6.17.6 was installed and run together with Xenomai. The way how to establish the Linux environment together with the Xenomai is described in the next chapter 2.

1.3 Source Code


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

1 Introduction

1.4 Structure of the Project Report

functions one should read the relevant APIs. It would make no sense to write detailed description of the used operations into the source code nor into this project report. One can see some free places in the code that should be lled before the driver can be said to be complete. When we found such places we wrote there TODO and noted what we think there should be done. It is important to remark that the code listed at the end of this project report does not have the ambition to be complete and ready for distribution. The basic aim was to get the understanding of how to write simple device drivers for Xenomai and to document that way, so that this document can be used as a starting point for further developing of a driver for Xenomai. We wrote the Linux device driver to obtain basic knowledge needed for writing drivers also under Xenomai. We didnt get into its precise code writing. Nevertheless, the code is functional and we think and hope that it, together with the documentation, will be helpfull source for future developers who want to start programming device drivers under Xenomai.

1.4 Structure of the Project Report


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

2 Xenomai an Installation Guide


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

2.1 System Requirements


This installation guide assumes that the user is running a functional GNU/Linux environment, best with a Debian or a Debian-like (e.g. Ubuntu) distribution. The user should be also familiar with the basic Linux commands and working in shell, because the whole installation process explained is realized in a terminal using CLI1 . Since we will need the existing kernel cong le of the Linux OS we are working in, it is necessary to ensure that the recent kernel version is 2.6.xx.yy. The best when the version is as close as possible to the 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
1

Command Line Interface

2 Xenomai an Installation Guide

2.1 System Requirements

It is worth to checkup if /etc/apt/sources.list has a good source where these 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 distribution. To get more help on conguring apt and its sources.list see man-pages or help on internet. If the list you will get contains a kernel image with a newer version number than your current kernel, we recommend to install it. Take care to check that the kernel image is suitable for your CPU architecture. If you are not sure about 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 congured automatically by the program apt-get and oer you the possibility at boot-up to return back to the previous kernel version in case the new one will not work. We recommend that you check up that your bootloader is congured properly. See GRUB or LILO documentation for more info. It is also important before starting with the compilation to check out if you have the correct version of the compiler. Make sure you have gcc-3.2 or newer. Older versions can have problems compiling newer versions of Linux kernel. First nd the path to it and then check the symbolic link gcc if it is showing on the right compiler. If not, download the new one, and make a link to 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:

2 Xenomai an Installation Guide Downloading and Unpacking the Sources 2.2

# 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 Xenomai with dierent compilers or dierent compilers versions, it could make Linux kernel and Xenomai incompatible.

2.2 Downloading and Unpacking the Sources


For establishing an Xenomai environment we need three les to download: the Linux kernel source code the Xenomai source code and the Adeos ipipe patch, since Xenomai relies on the Adeos microkernel which is implemented as a patch. Download the latest stable version of Linux kernel source, linux2.6.xx.yy.tar.gz, from http://kernel.org/ and save it to /usr/src. The main page [kerb] informs on the latest stable version. Next we will need the latest stable version of Xenomai. Download it from http://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 Debian GNU/Linux was the Adeos patch provided in the package kernel-patch-adeos, but this package has not been updated for kernel versions more recent than 2.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/

2 Xenomai an Installation Guide

2.3 Compiling

lftp lftp lftp lftp lftp #

:~> :~> :~> :~> :~>

lftp http://download.gna.org/xenomai/stable get xenomai-2.2.0.tar.bz2 -o /usr/src/ lftp http://download.gna.org/adeos/patches/v2.6/i386 get adeos-ipipe-2.6.17-i386-1.3-08.patch -o /usr/src/ exit

Note that the version numbers can dier, but take care that the rst three version numbers of the patch do match with the rst three version numbers of the 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 informs the Linux kernel about Xenomai and the Adeos ipipe patch. The script will install 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 Adeos patch. 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 cong le of the recently running kernel. You should nd it in /boot/ and copy it to /usr/src/linux-2.6.17.6/ There are more possibilities how to congure the kernel before compiling, one of them is with the command make menucong or you can also edit directly the le, e.g. vi .cong which can be practical if you are searching for some specic attributes. With make menucong you will jump into a text-based menu-style congurator, where you can descend into the menus

2 Xenomai an Installation Guide

2.3 Compiling

and change the conguration options which interest you. IMPORTANT: if you are a newcomer in the kernel conguration and you dont want to spend lot of time with recompiling the kernel it is wise not to change anything in the conguration. The result will be that the kernel will have many modules you will maybe never need and therefore it will be bigger and need more time to compile, also higher latencies in the real-time environment of Xenomai could occur. The good thing is that it should run just after one compilation without freezing and kernel panic at boot-up. As a newcomer do just this in the conguration: In the menucong main-menu you should see a sub-menu Real-time subsystem, enter it and make sure Xenomai is marked for installing as a build-in, meaning that it will be build directly into the kernel. In Real-time sub-system > Interfaces make sure Native API is chosen for installing. Make sure you dont 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, but be sure, that you know that it will not make your kernel unable to run on your hardware. It is recommended to exclude all modules and build-ins, that you will not need later while working with the system, e.g. you could exclude Joystick interface, Touchscreens or ISDN support. Also set the le systems you 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 (essentially some power management features) that would ruin the latencies in the real-time kernel : In Processor type and features: Check that Interrupt pipeline is enabled (this is to enable the Adeos implementation - I-pipe).2 Check that Local APIC support on uniprocessors and IO-APIC support on uniprocessors are enabled. In Power management options (ACPI, APM):
2

In newer versions of Xenomai is this option not congurable.

10

2 Xenomai an Installation Guide

2.3 Compiling

In ACPI (Advanced Conguration 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 shield support. Enable Watchdog support (this is sometimes useful). In Scalability: Optionally, if your applications create many RT threads, enable O(1) scheduler. In Machine (x86): Only if your system has FPU3 , enable FPU support to improve latency. In SMI workaround: Only if your system uses SMI4 , AND you encouter high maximum latencies, enable Enable SMI workaround and Globally 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 you are compiling the kernel for the rst time, it may be appropriate to inform you, that it takes, dependend from the computing power of your computer, one to three hours. Shell commands to congure and compile the kernel:
3

Look for FPU in /proc/cpuinfo le SMI- System Maintenance Interrupt 5 If 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 try to re-enable some SMI interrupts in Xenomais SMI workaround conguration, one by one. Try to enable an SMI interrupt, compile and then disable it again if it has no eect. See also [Unib, High latencies with SMI not disabled].
4

11

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 conguration. It is wise to get help from somebody who has already some experiences in it if you want to optimize the kernel. For more info or getting help with conguring, compiling and hacking the kernel, visit also webpages that are concerned with Linux kernel compilation, editing and hacking. As a starting point we 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 kernel to LILO or GRUB, dependend on what is your system using.

2.4.1 Conguring LILO


Keep in mind that you are adding new kernel, not changing the settings of the old one. When there will be problem booting-up the new kernel you can still return 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:
6

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

12

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 le with running the command lilo. Afterwards you can reboot into your new kernel. If you observe any problems at boot-up please read the sub-section 2.4.4.

2.4.2 Conguring GRUB


Keep in mind that you are adding new kernel, not changing the settings of the old one. When there will be problem booting-up the new kernel you can still return to the old one. Say your menu.lst 7 has something like this at the end of the le: ... title root kernel initrd savedefault boot ...

Debian, kernel 2.6.15-1-486 (hd0,0) /boot/vmlinuz-2.6.15-1-486 root=/dev/hda1 ro /boot/initrd.img-2.6.15-1-486

After adding the new kernel it should look like this: ... title root kernel initrd savedefault boot
7

Debian, kernel 2.6.15-1-486 (hd0,0) /boot/vmlinuz-2.6.15-1-486 root=/dev/hda1 ro /boot/initrd.img-2.6.15-1-486

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

13

2 Xenomai an Installation Guide

2.4 Setting up Bootloader and Testing

title root kernel savedefault boot ...

Xenomai, kernel 2.6.17.6 (hd0,0) /boot/vmlinuz-2.6.17.6-xenomai root=/dev/hda1 ro

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

2.4.3 Testing
After successfully logged into the system with the new kernel rst check if Adeos is working with the command dmesg | grep I-pipe. You should get this 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: Xenomai: Xenomai: Xenomai: hal/x86 started real-time nucleus v2.2.0 (Seven String) loaded starting POSIX services. starting RTDM services.

if Xenomai was loaded properly. Also execute /usr/xenomai/bin/xeno-test. If no error occurs, any Xenomai application should execute ne also. The test measures 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 to execute high load on the system, therefore it is wise to checkout if the program was cancelled together with the test. Type ps to see if dd is still running, if yes type killall dd to kill that process. Also it is recommended to look on the other programs delivered with Xenomai 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

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 we observed while installing the Xenomai. Kernel Panic at Boot-up It is possible that you will get this error message at boot-up: VFS:Cannot open root device or Kernel panic- not syncinc: VFS: Unable to mount root fs on unknows-blocks(3,3). In that case, you may need to make the initial RAMdisk. 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 species the le to be generated and the second argument 2.6.17.6-xenomai species 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 into the /etc/lilo.conf le. ... 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 le. ... title root

Xenomai, kernel 2.6.17.6 (hd0,0)

15

2 Xenomai an Installation Guide

2.4 Setting up Bootloader and Testing

kernel initrd savedefault boot ... And reboot.

/boot/vmlinuz-2.6.17.6-xenomai root=/dev/hda1 ro /boot/initrd.img-2.6.17.6-xenomai ## new line

Testprograms not Working If you get an error message like Xenomai: native skin or CONFIG XENO OPT PERVASIVE disabled. check the kernel log with the command 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 do it in lilo.conf le 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
8

Xenomai, kernel 2.6.17.6

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

16

2 Xenomai an Installation Guide

2.4 Setting up Bootloader and Testing

root kernel initrd savedefault boot ... and reboot.

(hd0,0) /boot/vmlinuz-2.6.17.6-xenomai root=/dev/hda1 ro lapic /boot/initrd.img-2.6.17.6-xenomai

NOTE: The reason why we didnt tell you to write all these options to the bootloaders in subsections 2.4.1, 2.4.2 is that with some architectures and kernel congurations it may be working also without them. The initrd option- initial RAM-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 should be able to boot-up also without it. See man pages of mkinitrd and lilo.conf for more help. The cause of the need to pass lapic as a bootparameter seems to be a bug in Xenomai. As we found in the mailing-list9 it should have been xed, but as of 07.07.2006, we still had to add that parameter in spite of the fact that we have enabled it while conguring the linux kernel. On the web page [Len], that is of an older date, is the explanation: Xenomai requires to have local APIC enabled 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 nd more information concerning LILO on the man pages of lilo or viewing the webpage [lxc, lesson4] or [Sko]. For GRUB manual see [Fou]. Failed to Start Menucong During our installation process we also observed a problem with starting the menucong because the ncurses libs were not installed. So if you will get error messages saying that some ncurses les are missing, just type: # apt-get install ncurses-dev to install them. If you still will have problems starting make menucong, you can edit directly /usr/src/linux-2.6.17.6/.config le or as a last possibility try make cong, which is pure line by line text mode and will ask you lot of questions.
9

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

17

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 le: No such le or directory, add /usr/xenomai/lib to /etc/ld.so.conf and run /sbin/ldcong . If you have some other problems installing Xenomai you should visit the ocial Xenomai webpages [xenb, xena] and use an internet search engine.

18

3 Xenomai- First Application


After the Xenomai environment was successfully established we can start to write our rst application. The best way is to look at an existing noncomplex code and analyze its Xenomai operations. One can nd some examples of simple Xenomai codes on the webpage [Unib] and Xenomai actual API on [xenc] or in 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 found in the section 7.1 of this paper. As one can see, the main() function prepares the environment for the real-time task execution, starts the task and waits for a signal that cancels that task. In Xenomai versions before 2.2.0 also the timer had to be started explicitly in the main function, now it is done by the operating system itself when a real-time task begins its execution. The realtime task is stand-alone and self-contained. It denes its variables, congures itself and executes the real-time functions. It is important to note that these two examples of codes use function printf which is not real-time and though can disturb the hard real-time feature. Therefore the use of printf is not recommended in the real hard real-time tasks. In the real-time task of timer 1.c we used in the while-loop the rt task set mode function. At this point we want to note that the tasks created in Xenomai can run either in the primary or in the secondary execution mode. The former means that the threads can run in the context of the highest priority domain in the pipeline, like kernel-based Xenomai threads. The latter means that the tasks can run in the regular Linux space but still are considered as real-time by Xenomai. More on these modes can be found in [Ger05, section 2.1.]. The basic dierence between the two example codes is that they use dierent timing modes. The rst one timer 1.c uses the aperiodic (one-shot) and the second one timer 2.c the periodic timing mode. The rst one is enabled by default when conguring the kernel before kernel compilation and the second one must be set explicitly in the kernel conguration menu following these steps in the kernel conguration menu: Real-time sub-system -->

19

3 Xenomai- First Application

Timing --> [ ] Use periodic timer hardware In periodic mode, clock ticks are interpreted as periodic jies. In oneshot mode, clock ticks are interpreted as nanoseconds. The, by default, aperiodic mode has much better accuracy for dierent time scales that cannot be easily expressed as multiples of a single base tick. The periodic mode makes the hardware timer tick at constant frequency and rounds the timeouts to a constant time slice. Therefore it is recommended to use aperiodic (oneshot) mode for precise timing. Before compiling one of the codes with Makele you have to add <xenoinstall>/bin to your PATH variable. Thats 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 Makele when you add the name to the TASK variable. Compile it and run in the super-user mode. Both programs will print out periodically (every second) a message on the stdout.

20

4 Linux Driver
Before we begin to write a hard real-time driver for our CPCI-EA221 card under Xenomai we rst focus on writing a simple driver under Linux system without Xenomai extension. The reason not to go directly to program real-time driver is that we could not nd any good reference, any tutorial for programming our CPCI driver under Xenomai. Luckily this was not the case with Linux and we soon have found an excellent source of the needed information. The book Linux Device Drivers, Third Edition [CRKH05], which is also freely available for download. In the next sections will be described what you need to know to create the CPCI driver and at the end of the chapter also a short description of the functions our driver oers to the user space (API) will be dened. It is also important to note that our driver was build to serve only to one CPCI-EA221 device, to extend its functionality for more devices is one of the more points that 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 it even at run time, so you dont have to restart the system to get your module working with the kernel. Thats a very nice feature since the development process can progress faster. Our driver is also a module and therefore the rst thing we need to know is how to dene and initialize kernel modules. To obtain that knowledge we recommend to read [CRKH05, chapter 2, Building and 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 nd the device the driver is supposed to work on. The recommended way is to inform the kernel in what hardware device we are interested. We export the structure dening our device to the module loading system so that the system knows which driver to call when it nds the specied device. As it was already mentioned in the chapter 1, CPCI is a high-performance standard based upon peripheral component interconnect

21

4 Linux Driver

4.1 Programming the Driver

(PCI) technology and therefore we can initialize any CPCI device like described in [CRKH05, chapter 12, PCI Drivers]. Also, there is a helpful pci skeleton code pci skel.c provided in the programs package of the book. The rst three needed steps to do in the device initialization function .probe() are to wake up the device with pci enable device(), allocate a memory region with request mem region() and to map the allocated memory to the kernel space virtual memory with ioremap(). Afterwards you can already write to the device registers using functions iowrite8(), iowrite16() or iowrite32(). To study how to allocate and map the memory read [CRKH05, chapter 9, Communicating with Hardware, section: Using I/O Memory] and [CRKH05, chapter 8, Allocating Memory, section: vmalloc and Friends]. In [CRKH05, chapter 12, PCI Drivers] you will nd how to nd the physical location (memory address) of the device. Do not forget to deallocate, to free always the memory you allocated in case of an error and in the exit() function of the driver. So far we have initialized the driver module to be able to read from and to write to the registers of the device. What we need now is to dene and make available some functions the driver will oer to the kernel and that way also to the (user-space) applications. Our device falls into the category of char devices and such devices are accessed through names in the lesystem. To learn how to create a name in the lesystem for a device read [CRKH05, chapter 3, Char Drivers, section: Major and Minor Numbers]. Basically, by creating of such an entry point, the device will get its major and minor number and also its name. Afterwards you can check the successful allocation viewing the le /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 lesystem that serves as an entry point for the applications that want to use the device and on the other side the device numbers must be connected to the drivers internal functions that implement the operations on the device. Creation of the device node with the allocated major number is best done by the loading script. In our CPCI-EA221 driver we use the script cpci ea221 load.sh to insert the module to the kernel and to create the device node. The script cpci ea221 unload.sh is used to remove the device nodes and the driver module from the kernel. Note that the minor number is hardcoded, 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 you can do it at compile time when you change the appropriate macro in the header le of the driver or you can also specify the number at module loading time with the command ./cpci ea221 load.sh cpci ea221 major=xx To know how to connect the device numbers to the internal functions of the device which implements the devices operations read [CRKH05, chapter 3, Char Drivers, sections: Some Important Data Structures and Char Device

22

4 Linux Driver

4.2 API

Registration]. It is needed to dene the le operations structure with the functions the driver will implement. A pointer to such structure is called fops. In our module we just use open(), close() and ioctl(). The important method is ioctl() in which we dene specic hardware control operations [CRKH05, chapter 6, Advanced Char Driver Operations, section: ioctl]. In this case we just wanted to enable to read from and to write to the registers of the device. Because with these two functions the device can be congured also from the user-space we dont need to think about all possible congurations of the device and write those congurations as separate ioctl() operations. The functions are described in 4.2 and to be able to congure the device you will need the CPCI-EA221 technical manual [Siea]. In our ioctl() operations we could not use the recommended fast functions put user(), get user() because our parameter structure didnt t in one of the specic sizes needed when using these functions, thats 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 last operation, in which we set up the important cdev structure and add that structure to the other cdev structures that are available to the kernel. Immediately afterwards the kernel can handle operations on the device, thats why it is important to call the function cdev add() rst when the driver is completely ready to 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.
1

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

23

4 Linux Driver

4.2 API

4.2.1 IOCTL Code and Argument Structure:


You should include into your code the le cpci_ea221_driver.h or add the following code into your header le: #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 #define EA221_IOCWRITE #define EA221_IOCREAD _IO(0xEE, 0) _IOW(0xEE, 1, ioc_param_struct) _IOWR(0xEE, 2, ioc_param_struct)

The bv member of the ioc param struct is a place where to store the 32-bits long vector you want to write into a register in the CPCI-EA221 card. Also the driver will store into the bv variable the value from a register you wanted to read. The oset is for storing the value that must be added to the base address (BA) of the device to obtain the full address of the register. You will nd these oset 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 how exactly 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, we want to write the vector 0x0000FFFF in the register with the address BA+0x28. We simply declare and initialize one variable in the driver.c le as 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

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 our ioctl() functions are working properly also from the user-space. Everything important is in the main() task. First we need to open the character device with the open() function. After opening the device le, or, in other words, after opening the entry point to the device, we have the le descriptor on which we can apply the ioctl() functions. In the while loop the process will change the output of the device after every pressing enter button on the keyboard. So one time 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 will be HIGH the LED #1 will be also HIGH (switched on), if the outputs will be LOW, the LED #1 will be o.

25

5 Xenomai RTDM Driver


Now we know some basic functions of Xenomai and know also how to write a simple driver module under Linux kernel. This chapter will explain how to use the Xenomai features in the driver module, so that in the end we will have a driver module which runs in hard real-time. It will be shown that some things that are important in Linux drivers are not used in Xenomai drivers. At this point we would like to note, that drivers under Xenomai are using the RealTime Driver Model (RTDM) skin that can be loaded together with the main interface, so make sure you enabled the installation of RTDM in the kernel conguration menu. In this chapter we will use instead of the name Xenomai driver the name RTDM driver as it represents the used API better. As a starting point we recommend to read an article on RTDM by Jan Kiszka [Kis05] from which we cite the goal of the RTDM The Real-Time Driver Model (RTDM) is an approach to unify the interfaces for developing device drivers and associated applications 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 module xeno 16550A.ko 1 , further with reading the code on the Captains webpage [Unib, mostly the section: Hard Real Time Driver Example Tutorial with MMAP using the RTDM (Real Time Driver Model)] and studying the Xenomai and RTDM API on [xenc]. With these three sources of information and the background we gained by programming the Linux driver in the previous chapter we could achieve our next goal, to write our own CPCI-EA driver module with Xenomai(RTDM) features. As for the Linux driver, in the chapter 4, as well for this driver holds that it was build to serve only to one CPCI-EA221 device, to extend its functionality for more devices is one of the more points that 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 denition and initialization of the driver module is made the same way as in the Linux driver. Alike is it with dening the device, we are interested in, to
1

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

26

5 Xenomai RTDM Driver

5.1 Programming the RTDM Driver

the module loading system. You can even use the same PCI skeleton code as introduced in the chapter 4 and you should also read the rst two paragraphs of the section Programming the driver from the same chapter. Next we need to dene the RTDM device description that will be passed lately to rtdm dev register() function. The description is specied within the structure rtdm device. An overview of central parameters that have to be specied 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 blank the device name and proc name because it will be dened in the .probe() function. This approach will be usefull later when the module will be adapted to support more devices. Another important structure used in our code is the xen cpciea221 context structure. It is used as an internal source of information on our device, we simply store there what we think will be needed in further work with the device. When the RTDM device structure is dened we can continue with working on the .probe() function. After enabling the pci device with pci enable device() and obtaining the physical location and size of the physical memory of the CPCIEA221 card, the memory for the RTDM structure will be allocated and subsequently the structure will be copied into that new memory space. Then the name of the RTDM device is specied, given to the RTDM structure and used for allocating the memory region for CPCI card with request mem region(). The memory region is afterwards mapped to the virtual kernel space with ioremap(). The name of the RTDM device given to the device structure is important because it is the only one access point to our RTDM device, thus it should be dierent for every device using this driver. The approach used in the Linux driver, that each driver has a node in the le system that serves as an access point to the device, is not followed in RTDM drivers. For this reason we dont need to allocate major and minor numbers of the device nor to write an extra loading script for the drivers module. From that follows also that we will not nd the RTDM device name in the /dev/ directory, but you can nd it listed in the /proc/iomem le. In the section 5.3 will be shown how to access the RTDM device. After the memory was successfully mapped to the kernel space, the last thing needed to be done is to register the RTDM structure with rtdm dev register(). Immediately afterwards the kernel can call the functions dened in the RTDM structure, so make sure that the function is called rst when everything is prepared for the proper device operation. The functions rt open() and rt close() are not doing anything necessary at this state of the development of the driver. Their bodies have now rather a demonstrative character. To every open device instance is a device context structure associated. In the rt open() is its use depicted. One can see how to access data from the rtdm device struct or from the context structure of the

27

5 Xenomai RTDM Driver

5.2 API

device we dened ourselves as a place where to store the data that could be needed by other functions handling on the device. In further development this functions should be used to allocate or deallocate additional memory for the driver, 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 the used operations were replaced by the RTDM operations. Otherwise all what was said on ioctl() in the chapter 4 holds also in this module. See 5.2 for API of 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 replaced by rt dev ioctl() function.

5.3 Test Application


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

28

5 Xenomai RTDM Driver

5.3 Test Application

and also there is the real-time task which embodies the most important part of the application. The real-time task supports dierent modes, which can be set up in the header le with uncommenting or commenting the two macros #dene PRINTF and #dene SIMPLE. With PRINTF enabled a message will be printed on stdout, but because printf function brings high latencies and corrupts the precise timing it is not recommended to use it. If SIMPLE is enabled, just a simple real-time task will be executed in which no care on input register is taken, all output pins will change their common state periodically. Otherwise, if SIMPLE is not enabled, the little bit more complex task will be executed that takes care on the input register. That means that the state of the output register will change periodically considering the the state of the input. This application proves that it is possible to write to and to read from the registers of the device under timing constraints and from the user-space. One more note to opening the device in the Xenomai environment. The device is opened with the rt dev open(dev name, ags) function in which the dev name stands for the name we dened and used in the .probe() function of the RTDM driver when calling request mem region(). As already mentioned before, this is the access point to the driver and subsequently to the device. This seems to be the biggest dierence to the approach used in common Linux drivers.

29

6 Conclusion
This project showed that solutions for real-time execution of tasks under Linux that can run together with regular Linux kernel already exists. We showed how to establish such real-time environment, how to write the rst tasks using it and concerned on the development of a device driver. The rst steps needed for designing a driver under regular Linux and under Xenomai were explained. When comparing these device drivers one can see that they have some main dierences, like that the Xenomai device is not accessed per a node in the lesystem, and they have also many similarities, and in some cases what one needs 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 briey what problems we found at programming of our driver and what could be the next steps or possible extensions of our project.

6.1 Problems
We observed only two problems for that we didnt have time to solve. First is that while loading the device driver module into the kernel space, the important function .probe() is executed two times, except of being just one time. We observed the same also with unloading the module from the kernel. The problem is with allocating and mapping the memory in this function that is done in that case also two times. The rst allocation and mapping is the right one and we get wrong values in the second loop. We solved this problem with assuring that the mapping and allocation is executed only one time by giving a if condition at the beginning of the .probe() function. The same for unloading function of the module, because otherwise we would deallocate the same memory two times, what is not desired. We are sure this is not the right way to go, but it seems to be the easiest one at the time. This problem should be solved 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, which is not the case. We didnt come on the solution till deadline.

30

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 a simple user-space application that can congure the device, per device registers, how it wants and then use the cards services. The most important work what we did not come to is setting up and using the interrupts. We think that the next step in extending the functionality of the device driver under Xenomai should be the interrupt support. Therefore, it is necessary to establish hard real-time interrupt handlers and write some applications using them. Other extensions could be for example the support for multiple CPCI devices, some more sophisticated ioctl functions, establishing communication with external devices using the CPCI-EA221 card, etc. It would be also important to make tests on hard real-time, to measure the tasks execution and latencies to get to know and approve hard-real time features of the Xenomai system.

31

7 Listings

32

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 #include #include #include #include #include #include #include <stdio.h> <unistd.h> <stdlib.h> <string.h> <signal.h> <sys/time.h> <sys/io.h> <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

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

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 programs 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

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 #include #include #include #include #include #include #include #include #include #include <stdio.h> <unistd.h> <stdlib.h> <string.h> <signal.h> <sys/time.h> <sys/io.h> <sys/mman.h> <native/task.h> <native/queue.h> <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

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 dont 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 programs 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

7 Listings

7.1 Xenomai First Application

// wait for signal & return of signal handler pause(); fflush(NULL); return 0; }

38

7 Listings

7.1 Xenomai First Application

7.1.3 Makele
########################################################################## ## 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 dont 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

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 OReilly & 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

7 Listings

7.2 Linux Driver

#include #include #include #include #include #include #include #include

<asm/io.h> <linux/ioport.h> <linux/fs.h> <linux/kdev_t.h> <linux/cdev.h> <asm/uaccess.h> <linux/capability.h> <linux/sched.h>

#include "cpci_ea221_driver.h" MODULE_DESCRIPTION("cpci_ea221_driver"); MODULE_AUTHOR("Ondrej Cevan (e0226686@student.tuwien.ac.at)"); 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

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

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 dont decode * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()

43

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

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

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: cant 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

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

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

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

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

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 OReilly & 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

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 #define CPCI_EA221_MINOR #define CPCI_EA221_DEVS 0x00 0x00 0x01 /* /* /* /* dynamic major by default */ value other then null is not */ supported by the load script */ 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;

uint8_t offset;

/* /* /* /* /*

32bit vector to write to register. when reading from register, the value will be stored there 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 #define EA221_IOCWRITE #define EA221_IOCREAD _IO(EA221_IOC_MAGIC, 0) _IOW(EA221_IOC_MAGIC, 1, ioc_param_struct) _IOWR(EA221_IOC_MAGIC, 2, ioc_param_struct)

52

7 Listings

7.2 Linux Driver

//the highest ordinal number of our IOCTL functions #define EA221_IOC_MAXNR 2 #endif /* _CPCI_EA221_H_ */

53

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 cards */ /* 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

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

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

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 cards */ /* 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 #define EA221_IOCWRITE #define EA221_IOCREAD _IO(EA221_IOC_MAGIC, 0) _IOW(EA221_IOC_MAGIC, 1, ioc_param_struct) _IOWR(EA221_IOC_MAGIC, 2, ioc_param_struct)

57

7 Listings

7.2 Linux Driver

7.2.5 Makele
########################################################################## ## 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 OReilly & 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 dont 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

7 Listings

7.2 Linux Driver

# Assume the source tree is where # You should set KERNELDIR in the KERNELDIR ?= /lib/modules/$(shell # The current directory is passed PWD := $(shell pwd)

the running kernel was built environment if its elsewhere uname -r)/build to sub-makes as argument

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

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 OReilly & Associates. ## ########################################################################## module="cpci_ea221_driver" device="cpci_ea221" mode="666" # invoke insmod with all arguments we got # and use a pathname, as newer modutils dont look in . /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

by default

60

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

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 OReilly & 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

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 cards 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 OReilly & Associates . */ /* */ /* No warranty is attached, we cannot take responsibility */ /* for errors or fitness for use. */ /*=====================================================================*/ #include #include #include #include #include <linux/kernel.h> <linux/init.h> <linux/module.h> <linux/fs.h> <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

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 (e0226686@student.tuwien.ac.at)"); 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

7 Listings

7.3 Xenomai- RTDM Driver

.probe .remove };

= cpci_ea221_probe, = __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 drivers 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

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 dont decode

66

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

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: context_size: device_name: //dont try to find // RTDM_NAMED_DEVICE | RTDM_EXCLUSIVE, sizeof(struct xen_cpciea221_context), "", //DEV_FILE, DEV_FILE in /dev/ - its not there this is an RTDM internal identifier

68

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: open_nrt: ops: { close_rt: close_nrt: ioctl_rt: ioctl_nrt: read_rt: read_nrt: write_rt: write_nrt: }, device_class: device_sub_class: driver_name: peripheral_name: provider_name: }; xen_cpciea221_rt_open, xen_cpciea221_rt_open,

xen_cpciea221_rt_close, xen_cpciea221_rt_close, xen_cpciea221_rt_ioctl, NULL, NULL, NULL, NULL, NULL,

RTDM_CLASS_SERIAL, 0, //222, //???? DRV_NAME, //informational driver name "CPCI EA221 rtdm", // Informational name // the device is attached to "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

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

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

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

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

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

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 cards */ /* 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 OReilly & 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 #define #define #define #define CPCI_VENDOR_ID_SIEMENS CPCI_SUBVENDOR_ID_SIEMENS CPCI_DEVICE_ID_EA221 CPCI_SUBDEVICE_ID_EA221 CPCI_EA221_BAR 0x10EE 0x110A 0x0300 0x4018 0x00

75

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;

uint8_t offset; };

/* /* /* /* /*

32bit vector to write to register, when reading from register, the value will be stored there 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

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

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

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

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 dont 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

7 Listings

7.3 Xenomai- RTDM Driver

} /** * main function * * initialize whats 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 */ // its 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

7 Listings

7.3 Xenomai- RTDM Driver

error: cleanup_all(); fflush(NULL); return ret; }

82

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

7 Listings

7.3 Xenomai- RTDM Driver

#define EA221_IOCRESET #define EA221_IOCWRITE #define EA221_IOCREAD #define #define #define #define

_IO(EA221_IOC_MAGIC, 0) _IOW(EA221_IOC_MAGIC, 1, ioc_param_struct) _IOWR(EA221_IOC_MAGIC, 2, ioc_param_struct)

DEV_NAME "rtser0" STATE_FILE_OPENED 1 STATE_TASK_CREATED 2 SHUTDOWNNOW 4

84

7 Listings

7.3 Xenomai- RTDM Driver

7.3.5 Makele
#################################################################### # 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 dont 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

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

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, the programs at ftp://ftp.ora.com/pub/examples/linux/drivers. [Fou] [Ger04] [Ger05] [Gro04] [kera] [kerb] [Kis05] Free Software Foundation. GRUB Manual. Available at www.gnu. org/software/grub/manual. Philippe Gerum. White Paper, 2004. Available at www.xenomai. org in submenu Documentation. Philippe Gerum. Life with Adeos, 2005. Available at www.xenomai. org in submenu Documentation. The Open Group. Base Specications Issue 6, 2004. http://www. opengroup.org/onlinepubs/009695399. Homepage of Kernel Hacking. www.kernelhacking.org. Homepage of Linux Kernel. http://kernel.org. Jan Kiszka. The Real-Time Driver Model and First Applications. University of Hannover, Germany, 2005. www.linuxdevices.com/ files/rtlws-2005/JanKiszka.pdf. Romain Lenglet. Installation of Xenomai Real Time Linux. www.csg.is.titech.ac.jp/~lenglet/howtos/ realtimelinuxhowto/index.html. Kernel Hacking on Linuxchix Webpage. content/courses/kernel_hacking. Ocial Website of RTAI. www.rtai.org. www.linuxchix.org/

[Len]

[lxc] [rta]

87

References

References

[Siea]

Siemens. CPCI-EA221 Technical Description. Available at https://support.automation.siemens.com/GB/llisapi.dll, search for the cpci-ea221 with the search engine. Siemens. Sicomp. http://www.automation.siemens.com/ sicomp/index_76.htm. Miroslav Skoric. LILO Manual. Available at http://en.tldp. org/HOWTO/LILO.html. Captains Universe. Linux Kernel Compilation HowTo. captain.at/programming/kernel. www.

[Sieb] [Sko] [Unia] [Unib] [xena] [xenb] [xenc]

Captains Universe. Xenomai. www.captain.at/xenomai.php. GNA Summary of Xenomai Project. https://gna.org/projects/ xenomai. Homepage of Xenomai Project. www.xenomai.org. Xenomai- API. http://snail.fsffrance.org/www.xenomai. org/documentation/trunk/html/api%/index.html.

88