: Introduction to Real-time ... Day 1 Table of Contents 1 Introduction ...... 1 1.1 Introduction ...... 1 1.2 Is Linux Real-time ? ...... 1 1.3 The Solution ...... 1 1.4 Course Outline ...... 3 1.4.1 Code Examples ...... 3 1.4.2 Course Project ...... 3 1.5 Class Network Setup ...... 4 2 Kernel Module Programming ...... 6 2.1 Kernel Modules, Introduction ...... 6 2.2 Kernel Modules ...... 6 2.3 Module Basics ...... 7 2.4 Module Basics Version 2.4 ...... 7 2.5 Module Compilation ...... 7 2.6 Module Symbols ...... 8 2.7 Module Versions ...... 8 2.8 Module Parameters ...... 8 2.9 Simple Kernel Module Lab ...... 9 3 Device Driver Programming ...... 10 3.1 Device Drivers, Introduction ...... 10 3.2 Device Drivers Picture ...... 11 3.3 The /dev directory ...... 11 3.4 Major Numbers ...... 12 3.4.1 Table 1 ...... 12 3.5 Minor Numbers ...... 13 3.6 Driver Private Data ...... 13 3.7 Device Classes ...... 14 3.8 Registering a Device ...... 14 3.9 File Operations Structure (fops) ...... 15 3.10 Transferring Data ...... 16 4 Example Device Drivers ...... 17 4.1 Simple Char Mode Device Driver ...... 17 4.2 Lab: Simple Char Mode Device Driver ...... 18 4.3 More Complex Char Device Driver ...... 19 4.4 Lab: More Complex Char Device Driver ...... 20 4.5 Blocking I/O and Wait Queues ...... 21 4.6 Blocking I/O Code Example ...... 22 5 Device Driver I/O ...... 23 5.1 I/O Port API ...... 23 5.2 Single Transfers ...... 23 5.3 Block Transfers ...... 23 6 IRQs ...... 25 6.1 Interrupt Handlers ...... 25 6.2 Interrupt Handler Arguments ...... 25 6.2.1 Interrupt Setup ...... 25 6.2.2Lineo Interrupt Service Education...... Services...... 25

- i - 6.3 Deferred Interrupt Processing: Bottom Halves and Task Queues ...... 26 7 DMA and Memory ...... 27 7.1 Memory in General ...... 27 7.2 Addressing Memory ...... 28 7.3 I/O Memory ...... 28 7.4 DMA ...... 30 7.5 Allocating Memory for DMA ...... 30 7.6 DMA virtual and physical memory ...... 30 7.7 DMA Programming calls ...... 31 7.8 DMA Example ...... 31 8 RTAI ...... 32 8.1 RTAI Features ...... 32 8.2 RTAI Overview ...... 33 8.3 Architecture. The RTHAL ...... 34 8.4 RTAI Installation under RH 6.2 ...... 35 8.5 RTAI Installation under RH 6.2 part 2 ...... 37 8.6 RTAI Installation under RH 7.1 (Kernel Upgrade) ...... 38 8.7 RTAI Installation under RH 7.1 part 2 ...... 40 8.8 RTAI Install Lab ...... 40 8.9 Lab: Install RTAI from Lineo V3.0 CD (2.2.x) ...... 41 8.10 Install RTAI II ...... 42 8.11 Install RTAI III (2.2.x) ...... 42 8.12 Build RTAI ...... 43 9 Real-Time Scheduling ...... 44 9.1 Task Structure ...... 44 9.2 RTAI Scheduling ...... 45 9.3 One-shot vs. Periodic ...... 46 9.4 RTAI Scheduling ...... 46 9.5 RTAI Scheduling API ...... 46 9.6 Real-Time Timer Functions ...... 48 10 PWM Servo Controller Project ...... 49 10.1 Signal Generation with a Simple Real-time Task ...... 49 10.2 Exercise: Basic Controller ...... 49 10.2.1 Assignment ...... 49 10.2.2 Tips ...... 49 10.2.3 Basic RT Module Example ...... 50 10.3 Lab: Basic Servo Driver ...... 53 10.3.1 Basic RT Module Example Answer ...... 53 10.3.2 Basic RT Module Example Notes ...... 55

Lineo Education Services

- ii - Lineo: Introduction to Real-time Linux ... Day 1 1 Introduction

1 Introduction

1.1 Introduction Real-time systems are concerned with when things happen as well as what happens. A real-time program must not only produce a correct answer, it must do so "on time." A late answer is a wrong answer.

Real-time systems can be further characterized as either "soft" real-time or "hard" real-time.

A hard real-time system absolutely must meet its scheduling deadline every time. Failure to meet the deadline may have catastrophic consequences, including loss of life. A fly-by-wire aircraft control system is an example of hard real-time. The control algorithms depend on regular sampling intervals. If sampling is delayed, the algorithm can become unstable.

A soft real-time system may cause cosmetic or annoying disturbances, but in general it will be more tolerant of missed deadlines. A video playback system may not look good when missing deadlines, but it should not cause serious damage to a process.

1.2 Is Linux Real-time ? For these several reasons, standard Linux isn’t capable of a real-time response:

Coarse-grained synchronization.

Paging.

The scheduler tries to be "fair."

Linux reorders requests from multiple processes to make it more efficient.

Linux will perform "batch" or "queued" operations to make more efficient use of hardware.

1.3 The Solution Whenever Linux tries to disable interrupts, the real-time kernel intercepts the request,Lineo records it. and returnsEducation to Linux. Services

- 1 - 1.3 The Solution

But, it does not disable interrupts!

When a hardware interrupt occurs, the real-time kernel first determines where it is directed:

Real-Time Task. Then schedule the task.

Linux. Check the interrupt flag:

If enabled, invoke the appropriate Linux interrupt handler.

If disabled, note that the interrupt occurred, and deliver it when Linux re-enables interrupts.

Lineo Education Services

- 2 - Lineo: Introduction to Real-time Linux ... Day 1 1.4 Course Outline

1.4 Course Outline This outline is not necessarily in the order of presentation. Introduction Kernel Modules Device Drivers Interrupts, DMA, and I/O Ports RTAI FIFOs Scheduling Memory Allocation and Shared Memory Inter-process Communication LXRT Debugging in User Space The /proc File System POSIX The Linux Trace Toolkit (LTT) 1.4.1 Code Examples Code examples are found on the /mnt/train/code/ClassLabs directory. Under this main directory you will find a subdirectory for each project. 1.4.2 Course Project A program to control a small Radio Control (RC) Servo will be used as a project during the course. This unit plugs in to the parallel port outlet on the PC and will drive one or more small RC Servos, a 5 volt LED. It will also allow an interrupt to be tied back into the PC via the parallel port. Lineo Education Services

- 3 - 1.5 Class Network Setup

1.5 Class Network Setup This is the set up for the class network. If DHCP is used the instructor will have to ask you for your IP Address if you need help. You can get your IP Address as follows:

/sbin/ifconfig

It is assumed that the network is running and that you can ping the class server from the your computers.

( example ) ping 192.168.1.100 ping

You will then want to allow the instructor to establish a telnet session into your system.

Note the use of xinetd is only for RedHad 7.1 systems and above

edit /etc/xinetd.d/telnet set disable = no Reread run killall -USR1 xinetd configuration. run tail -f Watch for the login. /var/log/messages

Expected results at this stage:-

May 8 17:02:47 devhp1 xinetd[717]: daytime disabled, removing May 8 17:02:47 devhp1 xinetd[717]: comsat disabled, removing May 8 17:02:47 devhp1 xinetd[717]: chargen-udp disabled, removing May 8 17:02:47 devhp1 xinetd[717]: chargen disabled, removing May 8 17:02:47 devhp1 xinetd[717]: amidxtape disabled, removing May 8 17:02:47 devhp1 xinetd[717]: Reconfigured: new=1 old=0 dropped=0 (services) May 8 17:03:29 devhp1 login(pam_unix)[1154]: session opened for user philw by (uid=0) May 8 17:03:29 devhp1 -- philw[1154]: LOGIN ON pts/1 BY philw FROM 192.168.1.231 May 8 17:08:50 devhp1 su(pam_unix)[1197]: session opened for user root by philw(uid=500)

Now you can attempt to mount the NFS shared directory from the class server: ( Note the "192.168.1.100" address may change )

Lineo Education Services

- 4 - Lineo: Introduction to Real-time Linux ... Day 1 1.5 Class Network Setup

Initial mount -t nfs 192.168.1.100:/home/train /mnt/train mount request.

[root@devhp1 philw]# ls -l /mnt/train Look at total 26259 directory -rw-r--r-- 1 root root 25755214 May 8 12:18 linux-2.4.3.tar.gz contents. -rw-r--r-- 1 root root 1025242 May 8 12:18 rtai-24.1.4.tgz

Lineo Education Services

- 5 - 2 Kernel Module Programming

2 Kernel Module Programming

2.1 Kernel Modules, Introduction Kernel modules are effectively extensions to the kernel that can be introduced into a running system.

Dynamic loading and symbol look-up techniques allow this process to alter kernel operation significantly during the init_module call.

The additional resources that were introduced into the kernel during module load can be removed when the module is unloaded. Any resources still in use at the unload time have to be tracked.

Figure 1 : Module overview

2.2 Kernel Modules As extensions of the kernel, modules execute at Privilege Level 0 and have access to the kernel’s symbol table.

These are the implications of the previous statement:

A module can execute privileged instructions such as I/O. A module can call any global kernel function. A module can easily bring down the entire system. A moduleLineo that fails toEducation install cannot be removed. Services There’s no protection against anything at Privilege Level 0.

- 6 - Lineo: Introduction to Real-time Linux ... Day 1 2.3 Module Basics

2.3 Module Basics These are the basic module user functions:

/sbin/insmod ./hello my_string="Hello" my_int=6 Insert a module into the kernel and set parameters.

/sbin/lsmod List currently loaded modules.

/sbin/rmmod hello Remove a module "if possible."

2.4 Module Basics Version 2.4 #include New include

static int __init mytest_init (void) Must return 0

static void __exit mytest_exit (void) Define a clean-up task.

module_init(mytest_init) (void) Create a module_init function.

module_exit(mytest_exit) (void) Create a cleanup_module function.

2.5 Module Compilation These are the include files and compile flags required for module "C" code.

#include #include #include // 2.4 Kernel

define MODULE and __KERNEL__

gcc -O2 -D__KERNEL__ -DMODULE -c hello.c Lineo Education Services

- 7 - 2.6 Module Symbols

2.6 Module Symbols A module can choose if it wants to exporting its global symbols with the following options:

EXPORT_NO_SYMBOLS Stops the global symbols being exported

EXPORT_SYMTAB Allows all the module global symbols to be exported

EXPORT_SYMBOL(name) Exports a symbol by name

2.7 Module Versions The kernel can mangle function names. Use cat /proc/ksyms to test.

We normally remove module versioning for development.

You can see the Kernel Version with cat /proc/version

2.8 Module Parameters A module can be loaded with values given to named parameters.

char *my_string; int my_int;

MODULE_PARM(my_string, "s"); MODULE_PARM(my_int, "i");

printk Used like printf but in kernel space.

Lineo Education Services

- 8 - Lineo: Introduction to Real-time Linux ... Day 1 2.9 Simple Kernel Module Lab

2.9 Simple Kernel Module Lab

Action Description mkdir -p /$HOME/ClassLabs/Module Make a working directory. cd /$HOME/ClassLabs/ Go to the working directory. cp /mnt/train/code/ClassLabs/Rules.make . Copy Make common rules. cd /$HOME/ClassLabs/Module Go to the working directory. cp -av /mnt/train/code/ClassLabs/Module/* . Copy files. more hello.c Look at a simple module. more Makefile Look at a simple module Makefile. make Make the module. su "su" to root. /sbin/insmod ./hello.o my_string="hi" Load the module (as root). my_int=8 /sbin/lsmod List the loaded modules. /sbin/rmmod hello Unload the module. dmesg List the module messages.

Lineo Education Services

- 9 - 3 Device Driver Programming

3 Device Driver Programming

3.1 Device Drivers, Introduction In the module, the role of init_module() is to "register" one or more capabilities with the kernel. This typically consists of calling an appropriate kernel registration function, passing pointers to one or more functions internal to the module and perhaps including some other parameters. A device driver registers capabilities into the kernel using a key called a major number. The driver type (block or char) is also used as a key.

Lineo Education Services

- 10 - Lineo: Introduction to Real-time Linux ... Day 1 3.2 Device Drivers Picture

3.2 Device Drivers Picture The user process uses a hook called the file descriptor to refer to an index into a file operations table to satisfy a sys_call for a given device operation.

3.3 The /dev directory This is one place where major numbers are defined: brw-rw---- 1 root disk 3,0 May 5 1998 hda brw-rw---- 1 root disk 3,1 May 5 1998 hda1 brw-rw---- 1 root disk 3,10 May 5 1998 hda10 . brw-rw---- 1 root disk 3,64 May 5 1998 hdb brw-rw---- 1 root disk 3,0 May 5 1998 hdb1 . Irwxrwxrwx 1 root root 5, May 5 10:00 Mouse->psaux . crw-rw-r 1 root root 10,1 May 5 17:14 psaux . crw-rw-rw- 1 root root 5,0 May 5 1998 tty crw------1 root root 4,0 May 5 1998 tty0 crw------1 root tty 4,1 Feb 23 20:06 tty1 crw-rw----Lineo 1 root Education uucp 19,0 Apr Services 17 1999 ttyC0

- 11 - 3.4 Major Numbers

3.4 Major Numbers Note: The same major number can be used by both a char and a block driver. 3.4.1 Table 1

Major Character Devices Block Devices

0 unnamed for NFS, network, etc. 1 Memory (mem) RAM disk 2 Pseudo Terminals (pty) Floppy disks (fd*) 3 ttyp IDE hard disks (hd*)

4 Terminals (ttyS) 5 Terminals and AUX (cua) 6 Parallel interfaces 7 Virtual consoles (vcs*) 8 SCSI hard disks (sd*) 9 SCSI tape drives (st*) 10 misc devices 11 SCSI CD-ROM (scd*) 12 QIC02 tape 13 PC speaker driver XT 8-bit hard disks (xd*)

The major number provides the main link between the "device," the kernel driver, and the user software. The user opens a device represented by a special file (created by mknod). This special file was given a major number when it was created. The major number refers to a device driver that was registered in the kernel as servicing that major number. Looking at /proc/devices, you can see all the currently registered major numbers for both char and block devices. Lineo Education Services

- 12 - Lineo: Introduction to Real-time Linux ... Day 1 3.5 Minor Numbers

3.5 Minor Numbers The minor number is also specified by the special file (created by mknod). The main driver is referenced by the major number, but the minor number can be used to pick a particular device controlled by the driver. A serial driver may control several serial ports; the minor number directs the driver to a particular port. In some cases the minor number can refer to a completely different set of read/write operations.

[root@bierk /root]# ls -l /dev/random crw-r--r-- 1 root root 1, 8 May 5 1998 /dev/random [root@bierk /root]# ls -l /dev/zero crw-rw-rw- 1 root root 1, 5 May 5 1998 /dev/zero

The random and zero devices both have the same major number, but the different minor numbers result in different responses to read/write operations.

[root@bierk /root]# ls -l /dev/ttyS* crw------1 root tty 4, 64 May 28 13:08 /dev/ttyS0 crw-rw-rw- 1 root tty 4, 65 Jun 7 23:08 /dev/ttyS1 crw------1 root tty 4, 66 Apr 28 09:47 /dev/ttyS2 crw------1 root tty 4, 67 May 5 1998 /dev/ttyS3

The serial driver uses the same software for each serial port. The minor number is used to change the actual physical address of the device being referenced. 3.6 Driver Private Data The open call enters the device driver software with a structure unique to that particular instance of the open call. This structure has a slot into which the driver Lineocan place its own Educationprivate data. Services

- 13 - 3.7 Device Classes

The structure (and hence the pointer) is unchanged by the system software for all driver functions after the initial open call. Each call to open can have a different private data area assigned to this pointer.

This private data area can be set up in many ways:

A structure shared between all minor numbers for this device A specific structure for this minor device A special structure for this instance of the open call.

In the last case the private data structure is normally "kmalloc’d" in the open call.

If this is the case, do not forget to "kfree" it when the device is closed. If a task exits all open devices are closed automatically by the system software.

3.7 Device Classes Devices come in three "flavors": Character, Block and Network. Character: Serial Access Single word (byte) at a time Block: Random Access Multiple words (bytes) at a time Blocking Network: Interrupt Push Different "device" structure Manipulation Tools 3.8 Registering a Device These two functions are used to associate the fops table with a Major number and a device "name". The "name" is used for information only. Lineo Education Services

- 14 - Lineo: Introduction to Real-time Linux ... Day 1 3.9 File Operations Structure (fops)

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

int register_blkdev(unsigned int major, const char *name, struct file_operations *fops);

3.9 File Operations Structure (fops) These are some typical functions used in a FOPS table.

loff_t (*llseek)(struct file *,loff_t, int); move the file pointer to a required position.

ssize_t (*read)(struct file *, char *, size_t, loff_t *); read a number of characters into a user buffer.

ssize_t (*write)(struct file *, const char *, size_t,loff_t *); write a number of characters from a user buffer.

int (*ioctl)(struct inode *, struct file *, unsigned int, unsigned long); A collection of misc operations on the device.

int (*open)(struct inode *, struct file *); set up the device for reads , writes and misc operations.

int (*release)(struct inode *, struct file *,); close or release the device.

Lineo Education Services

- 15 - 3.10 Transferring Data

3.10 Transferring Data The Linux kernel provides a pair of functions to carry out data transfer between user space and kernel space:

unsigned long copy_from_user(unsigned long to, unsigned long from, unsigned long length);

unsigned long copy_to_user(unsigned long to, unsigned long from, unsigned long length);

Lineo Education Services

- 16 - Lineo: Introduction to Real-time Linux ... Day 1 4 Example Device Drivers

4 Example Device Drivers

4.1 Simple Char Mode Device Driver This is a simple character driver and is used as a brief example in writing device drivers. This driver doesn’t service any real hardware. It simply treats an area of memory as a device. The first version of the driver simply reads a buffer. The Second version allows you to replace the first few characters in that buffer with your own data. The objective is to examine the interface between the kernel and a char driver. The driver performs the following functions: Sets up some memory areas Registers the device with the kernel Provides simple device read/write routines Cleans up and deregisters the device with the kernel

Lineo Education Services

- 17 - 4.2 Lab: Simple Char Mode Device Driver

4.2 Lab: Simple Char Mode Device Driver

Action Description Get the code. mkdir -p Make a working directory. /$HOME/ClassLabs/Driver cd /$HOME/ClassLabs/Driver Go to the working directory. cp -av /mnt/train/code/ClassLabs/Driver/* Copy the files. ./ Make the target. cc -O2 -DMODULE This compiles the scmd_read.c code. -D__KERNEL__ -c scmd_read.c look at scmd_read_load. This is a script used to load the scmd driver and create a set of device nodes for it. We use awk to find the appropriate entry in /proc/devices and extract the major number from it. As root sh scmd_read_load. This lists all the kernel modules presently loaded and /sbin/lsmod who is using them. You should see scmd_read at the top of the list, because it was the last module loaded. cat /dev/scmd This reads the scmd_read device, returning what was preloaded into the array. /sbin/rmmod scmd_read This removes the module. Make the read/write target. cc -O2 -DMODULE This compiles the scmd_rw.c code. -D__KERNEL__ -c scmd_rw.c As root sh ./scmd_rw_load. echo "this is my driver" >/dev/scmd This redirects the words this is my driver to the scmd device. cat /dev/scmd This reads the scmd_read device, returning what was preloaded into the array plus the data we added to the buffer. more scmd_read.c Take a look at the read source. more scmd_rw.c. Take a look at the read/write source. Lineo Education Services

- 18 - Lineo: Introduction to Real-time Linux ... Day 1 4.3 More Complex Char Device Driver

4.3 More Complex Char Device Driver The driver is named scull, which stands for Simple Character Utility for Loading Localities. It’s a simplified version of the scull driver described in Linux Device Drivers by Alessandro Rubini. Again Scull doesn’t service any real hardware. It simply treats an area of memory as a device. The objective of this driver is to examine the various options that can be included when creating even a simple driver.

Lineo Education Services

- 19 - 4.4 Lab: More Complex Char Device Driver

4.4 Lab: More Complex Char Device Driver

Action Description Get the code. mkdir -p Make a working directory. /$HOME/ClassLabs/Driver cd /$HOME/ClassLabs/Driver Go to the working directory. cp -av /mnt/train/code/ClassLabs/Driver/* Copy the files. ./ Make the target. look at scull_load . This is a script used to load the scull driver and create a set of device nodes for it. Note that scull uses dynamic major number assignment, and so before creating device nodes, we have to determine which major number was assigned. We use awk to find the appropriate entry in /proc/devices and extract the major number from it. As root sh ./scull_load . ls -l /dev/scull* You see that scull_load has created four inodes for the scull device plus a link to the first one. This script lists all the kernel modules presently loaded /sbin/lsmod and who is using them. You should see scull at the top of the list since it was the last module loaded. ls -l /dev/scull* >/dev/scull This redirects the directory listing to the scull device. cat /dev/scull This reads the scull device, returning what was previously written. more scull.c Take a look at the source.

Lineo Education Services

- 20 - Lineo: Introduction to Real-time Linux ... Day 1 4.5 Blocking I/O and Wait Queues

4.5 Blocking I/O and Wait Queues Note there were some queue changes in V2.4.

What happens if we try to read a device that has no data available? The "preferable" option is to put the calling process to sleep until data is available. The calling process is blocked and doesn’t return from the read call until data becomes available. In the meantime, other processes can execute.

A process can be put to sleep by calling one of the following functions:

void interruptible_sleep_on (struct wait_queue **q) void sleep_on (struct wait_queue **q);

Later on, the process can be awakened by using one of these functions:

void wake_up_interruptible (struct wait_queue **q); void wake_up (struct wait_queue **q);

The equivalents for version 2.4 are:

void interruptible_sleep_on (struct wait_queue_head_t *q) void sleep_on (struct wait_queue_head_t *q);

And to wake up a version 2.4 process:

void wake_up_interruptible (struct wait_queue_head_t *q); void wake_up (struct wait_queue_head_t *q);

Lineo Education Services

- 21 - 4.6 Blocking I/O Code Example

4.6 Blocking I/O Code Example Note there are some changes in V2.4 queues.

This is a simple pseudo-code example of blocking I/O. struct wait_queue *wq=NULL; /* must be NULL at the beginning */ ssize_t length; ssize_t block_read (struct file *flip, char *buff, size_t count, loff_t *off) { if ( length == 0 ) { if ( filep->flags & O_NONBLOCK ) return -EAGAIN; interruptible_sleep_on(&wq); if ( signal_pending(current)) return -ERESTARTSYS; } /* copy data buffer to user space */ return length; } ssize_t block_write (struct file *flip, char *buff, size_t count, loff_t *off) { /* copy data to buffer */ /* set length */ length = chars; wake_up_interruptible(&wq); return length; }

Lineo Education Services

- 22 - Lineo: Introduction to Real-time Linux ... Day 1 5 Device Driver I/O

5 Device Driver I/O

5.1 I/O Port API Direct access to I/O ports is accomplished through the functions shown here. You can transfer single bytes, words (or double words), and blocks of bytes, words or double words. 5.2 Single Transfers unsigned inb (unsigned port); Byte input.

void outb (unsigned char byte, unsigned port); Byte output.

unsigned inw (unsigned port); Word input.

void outw (unsigned short word, unsigned port); Word output.

unsigned inl (unsigned port); Double input.

void outl (unsigned long, unsigned port); Double output.

5.3 Block Transfers void insb (unsigned port, void *addr, unsigned long count); Byte input.

void outsb (unsigned port, void *addr, unsigned long count); Byte output.

void insw (unsigned port, void *addr, unsigned long count); Word input.

void outsw (unsigned port, void *addr, unsigned long count); WordLineo output. Education Services

- 23 - 5.3 Block Transfers

void insl (unsigned port, void *addr, unsigned long count); Double input.

void outsl (unsigned port, void *addr, unsigned long count); Double output.

Lineo Education Services

- 24 - Lineo: Introduction to Real-time Linux ... Day 1 6 IRQs

6 IRQs

6.1 Interrupt Handlers The standard way of using interrupts in Linux is to "wake up" a task that is waiting for some resource that is being provided by the system generating the interrupt.

A process blocks through a call to interruptible_sleep_on().

A hardware interrupt occurs.

The corresponding interrupt handler then executes the wake_up_interruptible() function.

Like the device driver, an interrupt handler must be "registered" by a call to request_irq(). This establishes the link between the interrupt handler and the hardware IRQ.

6.2 Interrupt Handler Arguments 6.2.1 Interrupt Setup

int request_irq( unsigned int irq_num, void (*handler)(int, void * , struct pt_regs *), unsigned long irq_flags, const char * device, void * dev_id)

6.2.2 Interrupt Service

void service_irq( unsigned int irq_num, void * dev_id, struct pt_regs *) irq_flags: A set of flags used to define the handler’s behavior. Lineo Education Services

- 25 - 6.3 Deferred Interrupt Processing: Bottom Halves and Task Queues

SA_INTERRUPT. When set, run with interrupts disabled. SA_SHIRQ. When set, this is a shared interrupt.

In the case of a shared interrupt all the drivers registered for that interrupt are called in the order in which they were registered.

The code that services the interrupt handlers follows:

// extract from /usr/src/linux/arch/i386/kernel/irq.c

do { status |= action->flags; action->handler(irq, action->dev_id, regs); action = action->next; } while (action);

6.3 Deferred Interrupt Processing: Bottom Halves and Task Queues In Linux, this deferred interrupt processing is called a bottom half. The interrupt service routine itself is referred to as the top half. There can be up to 32 bottom halves, which are registered in a table of function pointers. The bottom halves are run at every system call exit with interrupts enabled.

The bottom half API consists of:

void init_bh (int nbr, void (*function)(void)); Assign a function to a bottom half.

void disable_bh (int nbr); Disable a bottom half.

void enable_bh (int nbr); Enable a bottom half.

void mark_bh (int nbr); Flag a bottom half as being ready to run. This is the primary call to schedule a bottom half.

Lineo Education Services

- 26 - Lineo: Introduction to Real-time Linux ... Day 1 7 DMA and Memory

7 DMA and Memory

7.1 Memory in General In version 2.2.x of the Linux kernel, the memory that could be used by the system was limited to 1 GB. The address space used was, in fact, 4 GB, with 3 GB available to user processes. The physical memory was all mapped into the kernel space starting at address 0xC0000000, as shown in the following figure:

The 2.4 kernels changed all this and allowed a far greater address space and more physical memory to exist in the system. This was done at the expense of increasing the complexity of kernel memory mapping.

Lineo Education Services

- 27 - 7.2 Addressing Memory

7.2 Addressing Memory There are, three ways to address memory:

Physical: What the CPU "sees" when it places an address on the memory address bus Virtual: The address the CPU uses to place an address on the memory address bus Bus: The address an external device uses to get to a specified physical address

Linux developers have taken great pains to ensure that the many different architectures all look and feel the same within the kernel.

The following code is used to look up all these translations:

#include

phys_addr = virt_to_phys(virt_addr); virt_addr = phys_to_virt(phys_addr); bus_addr = virt_to_bus(virt_addr); virt_addr = bus_to_virt(bus_addr);

Use the virt_addr to access the memory inside the kernel.

Give the bus_addr to an I/O device to let it access memory.

The phys_addr is used in memory remapping functions.

7.3 I/O Memory Having mastered some of those concepts you now have to contend with a different type of memory.

Peripherals usually have their own memory buffers that you may need to access.

This memory is called a number of names:

PCI memory Shared memory I/O memory Lineo Education Services

- 28 - Lineo: Introduction to Real-time Linux ... Day 1 7.3 I/O Memory

To access this memory, you must use the ioremap function to produce a kernel mapping of the area, as shown in this example:

remapping and writing: /* * remap framebuffer PCI memory area at 0xFC000000, * size 1MB, so that we can access it: We can directly * access only the 640k-1MB area, so anything else * has to be remapped. */ char * baseptr = ioremap(0xFC000000, 1024*1024);

/* write a ’A’ to the offset 10 of the area */ writeb(’A’,baseptr+10);

/* unmap when we unload the driver */ iounmap(baseptr);

Lineo Education Services

- 29 - 7.4 DMA

7.4 DMA Direct memory access in contemporary PCs comes in two "flavors":

The traditional "native" mode DMA implemented by the 8237 DMA controller chips for devices on the ISA bus

"Master Mode" DMA implemented by PCI devices that have bus master capability

Linux provides an API that supports native mode DMA. The protocol for master mode DMA is defined by the devices themselves and is beyond the scope of Linux standardization.

This API changed in the 2.4 kernels. The use of virt_to_bus and bus_to_virt has been superseded by some new PCI DMA functions.

7.5 Allocating Memory for DMA The DMA controllers deal in physical, contiguous address spaces. Additionally, the ISA bus and associated DMA controllers are limited to a 24-bit address; that is, the lower 16 MB of memory. A common way to allocate memory for DMA transfers is to use the kernel’s dynamic memory allocation function, kmalloc. void *kmalloc (size_t size, int flags); This allocates a buffer of size rounded up to the next 4 KB. Blocks larger than 4 KB are guaranteed to be contiguous. Among other things, flags can specify where the allocated block is in physical memory. If the flag GFP_DMA is specified, the allocated memory is guaranteed to be in the lower 16 MB of physical space. 7.6 DMA virtual and physical memory These functions are used for the 2.2.x series kernels. See /usr/src/linux/Documentation/DMA-mapping.txt for details regardingLineo the 2.4.x seriesEducation kernels. Services

- 30 - Lineo: Introduction to Real-time Linux ... Day 1 7.7 DMA Programming calls

Use (unsigned long) virt_to_bus (volatile void * addr) to convert the virtual address obtained from kmalloc into a physical bus address. Use (unsigned void *) bus_to_virt (unsigned long addr) to convert a physical bus address into a virtual address. 7.7 DMA Programming calls

Action Description int request_dma(unsigned int dma_chan, Assign a DMA channel (dev_id appears in cat void * dev_id) /proc/dma). int free_dma(unsigned int dma_chan) Release a DMA channel. int set_dma_mode(unsigned int dma_chan, Set DMA mode to DMA_MODE_READ or char mode) DMA_MODE_WRITE. int set_dma_addr(unsigned int dma_chan, Set DMA address (must be a bus address). unsigned int addr) int set_dma_count(unsigned int dma_chan, Set DMA count in number of bytes. unsigned int count) int enable_dma(unsigned int dma_chan) Enable (start) DMA. int disable_dma(unsigned int dma_chan) Disable (stop) DMA.

7.8 DMA Example Here is an example from /usr/src/linux-2.4.3/drivers/char/esp.c.

Note that it still uses the deprecated virt_to_bus interface:

flags=claim_dma_lock(); disable_dma(dma); clear_dma_ff(dma); set_dma_mode(dma, DMA_MODE_READ); set_dma_addr(dma, virt_to_bus(dma_buffer)); set_dma_count(dma, dma_bytes); enable_dma(dma); release_dma_lock(flags); Lineo Education Services

- 31 - 8 RTAI

8 RTAI

We now move on to discuss real-time Linux extensions. The system we use is called the Real Time Applications Interface (RTAI).

8.1 RTAI Features POSIX 1003.1c compatible pthreads including mutexes and condition variables

POSIX 1003.1c compatible pqueues

Traditional Real Time (RTOS) Inter-Process Communications (IPCs) including, Semaphores (SEM), Mailboxes (MBX), First In First Out queues (FIFO), Shared Memory (SM), and Remote Procedure Calls (RPC)

Dynamic memory allocation which is non-blocking in the real-time domain

PERL Bindings. Allows scheduling of soft real-time tasks from the PERL scripting language

/proc file interface. Run time information

LXRT. Use RTAI from user space

Multi Processor. Uniprocssor (UP), Multi Uniprocessor (MUP), and Symmetric Multi-Processor (SMP) Scheduling support

FPU Support. Floating point in real-time tasks

One-Shot and periodic schedulers

Lineo Education Services

- 32 - Lineo: Introduction to Real-time Linux ... Day 1 8.2 RTAI Overview

8.2 RTAI Overview To achieve Real Time Performance, RTAI inserts a "real-time" interrupt handler and scheduler into the kernel.

Figure 3: RTAI Overview

Lineo Education Services

- 33 - 8.3 Architecture. The RTHAL

8.3 Architecture. The RTHAL This is the abstraction layer or list of functions that are normally satisfied by regular Linux functions. When the RTAI module is loaded the actual function calls are replaced by real-time versions of these calls. struct rt_hal { struct desc_struct *idt_table; void (*disint)(void); void (*enint)(void); unsigned int (*getflags)(void); void (*setflags)(unsigned int flags); void (*mask_and_ack_8259A)(unsigned int irq); void (*unmask 8259A_irq)(unsigned int irq); void (*ack_APIC-irq)(void); void (*mask_IO_APIC_irq)(unsigned int irq); void (*unmask_IO_APIC_irq)(unsigned int irq); unsigned long *io_apic_irqs; void *irq_controller_lock; void *irq_desc; int *irq_vector; void *irq_2_pin; void *ret_from_intr; struct desc_struct *gdt_table; int *idle_weight; };

Lineo Education Services

- 34 - Lineo: Introduction to Real-time Linux ... Day 1 8.4 RTAI Installation under RH 6.2

8.4 RTAI Installation under RH 6.2 This process outlines the installation process for 6.2. Other distributions may follow closely.

The files are on the class shared directory:

[root@devhp1 philw]# ls -l /mnt/train/62/ Look at total 26259 dir -rw-r--r-- 1 root root 25755214 May 8 12:18 linux-2.2.17.tar.gz contents. -rw-r--r-- 1 root root 1025242 May 8 12:18 rtai-1.6.tgz

Now unpack and configure the linux kernel.

cd /usr/src Move to src dir. tar xvzf Unpack linux. /mnt/train/62/linux-2.2.17.tar.gz

mv linux linux-2.2.17 Create version directory. Create link ln -s linux-2.2.17 linux (what you have -2- what you want). Select kernel options.

Code maturity level options ---> [*] allow experimental code

Loadable module support --->

[*] Enable loadable module support [ ] Set version information on all module symbols [*] Kernel module loader

Processor type and features ---> cd linux (cat /proc/cpuinfo ) make menuconfig (Pentium-Pro/Celeron/Pentium-II) Processor family [ ] Math emulation [*] MTRR (Memory Type Range Register) support [ ] Symmetric multi-processing support

Network device support ---> Ethernet (10 or 100Mbit) ---> ( cat /etc/modules.conf alias eth0 3c59x )

3c590/3c900 series (592/595/597) "Vortex/Boomerang" support

make dep clean Create a new kernel. make install make modules I always forget this step. make modules_install

Lineo Education Services

- 35 - 8.4 RTAI Installation under RH 6.2

boot=/dev/hda map=/boot/map install=/boot/boot.b prompt timeout=50 message=/boot/message linear

##################################### #we leave the original entries alone #####################################

default=linuxrt

vi /etc/lilo.conf image=/boot/vmlinuz-2.2.16-xxx label=linux read-only root=/dev/hda6

################################### # we copy the entry above and point # the image to our new kernel ###################################

image=/boot/vmlinuz-2.2.17 label=linuxrt read-only root=/dev/hda6

/sbin/lilo Remenber to run LILO. /sbin/shutdown -r now Reboot.

Lineo Education Services

- 36 - Lineo: Introduction to Real-time Linux ... Day 1 8.5 RTAI Installation under RH 6.2 part 2

8.5 RTAI Installation under RH 6.2 part 2 Having built and booted a new kernel you now add the RTAI layer. First unpack the RTAI source . cd /usr/src Move to src dir. tar xvzf /mnt/train/62/rtai-1.6.tgz Unpack rtai. Create link ln -s rtai-1.6 rtai (what you have -2- what you want). cd /usr/src/rtai/arch/i386/copyto/linux-2.2.17 Pactch the kernel. ./copyto cd /usr/src/linux make clean Create another new kernel and make install reboot. /sbin/lilo /sbin/shutdown -r now cd /usr/src/rtai Compile RTAI Sources. ./use_intel Note that there are two make make ( accept default answer ) passes. make -i all

Lineo Education Services

- 37 - 8.6 RTAI Installation under RH 7.1 (Kernel Upgrade)

8.6 RTAI Installation under RH 7.1 (Kernel Upgrade) This process outlines the installation process for Red Hat 7.1. Other distributions may follow closely.

The files are on the class shared directory:

[root@devhp1 philw]# ls -l /mnt/train/71 Look at total 26259 the dir -rw-r--r-- 1 root root 25755214 May 8 12:18 linux-2.4.3.tar.gz contents. -rw-r--r-- 1 root root 1025242 May 8 12:18 rtai-24.1.4.tgz

Now unpack and configure the linux kernel:

cd /usr/src Move to src dir. tar xvzf Unpack linux. /mnt/train/71/linux-2.4.3.tar.gz

mv linux linux-2.4.3 Create version directory. Create link ln -s linux-2.4.3 linux (what you have -2- what you want). select kernel options

Code maturity level options ---> [*] allow experimental code

Loadable module support --->

[*] Enable loadable module support [ ] Set version information on all module symbols [*] Kernel module loader

Processor type and features ---> cd linux (cat /proc/cpuinfo ) make menuconfig (Pentium-Pro/Celeron/Pentium-II) Processor family [ ] Math emulation [*] MTRR (Memory Type Range Register) support [ ] Symmetric multi-processing support

Network device support ---> Ethernet (10 or 100Mbit) ---> ( cat /etc/modules.conf alias eth0 3c59x )

3c590/3c900 series (592/595/597) "Vortex/Boomerang" support

make dep clean Create a new kernel. make install make modules I always forget this step. make modules_install

Lineo Education Services

- 38 - Lineo: Introduction to Real-time Linux ... Day 1 8.6 RTAI Installation under RH 7.1 (Kernel Upgrade)

boot=/dev/hda map=/boot/map install=/boot/boot.b prompt timeout=50 message=/boot/message linear

################################### #we use the line below as a model ###################################

default=linuxrt

vi /etc/lilo.conf image=/boot/vmlinuz-2.4.2-2smp label=linux read-only root=/dev/hda6

################################### # we copy the entry above and point # the image to our new kernel ###################################

image=/boot/vmlinuz-2.4.3 label=linuxrt read-only root=/dev/hda6

/sbin/lilo Remember to run LILO. /sbin/shutdown -r now Reboot.

Lineo Education Services

- 39 - 8.7 RTAI Installation under RH 7.1 part 2

8.7 RTAI Installation under RH 7.1 part 2 Having built and booted a new kernel, you now add the RTAI layer. First unpack the RTAI source. cd /usr/src Move to src dir. tar xvzf /mnt/train/71/rtai-24.1.4.tgz Unpack RTAI. Create link ln -s rtai-24.1.4 rtai (what you have -2- what you want). cd /usr/src/rtai/arch/i386/copyto/linux-2.4.3 Pactch the kernel. ./copyto cd /usr/src/linux make clean Create another new kernel and make install reboot. /sbin/lilo /sbin/shutdown -r now mv /usr/bin/gcc /usr/bin/gcc-2.96 Remove RedHat’s new compiler. ln -s /usr/bin/kgcc /usr/bin/gcc cd /usr/src/rtai ./use_intel Compile RTAI Sources. make ( accept default answer ) Note that there are two makes. make -i all

8.8 RTAI Install Lab Install RTAI Patch Kernel Rebuild Kernel and reboot Compile RTAI Test Lineo Education Services

- 40 - Lineo: Introduction to Real-time Linux ... Day 1 8.9 Lab: Install RTAI from Lineo V3.0 CD (2.2.x)

8.9 Lab: Install RTAI from Lineo V3.0 CD (2.2.x)

Action Description Insert the Lineo CD. cd /mnt/cdrom This should cause the CD to be mounted automatically sh zcdinst This brings up an installation dialog. Press enter to pass The installer displays a list of package over the startup groups from which you can select the screen. packages to be installed.

Lineo Education Services

- 41 - 8.10 Install RTAI II

8.10 Install RTAI II

Action Description Packages for installation are indicated by [*]. Select contents of a Installation status changes by selecting package group with the package with the arrow keys and the arrow keys and pressing the space bar. To see details on a press the space bar. package, select it with the arrow keys and press F1. Many packages are selected for installation by default. We use the defaults with one exception: Under "Additional Programs" we select the Data Display Debugger. The installer now prompts for installation directories. The defaults for Source, Press Tab to continue. Binary, and Kernel images should be correct.

8.11 Install RTAI III (2.2.x)

Action Description Installation will commence and the installer will Press Tab to display a progress screen similar to the one shown continue. by the standard Linux installation tool. Because you installed packages that include Linux kernel images, the installer asks us if you to update lilo.conf.

Select "Yes." The default root device should be correct.

Lineo Education Services

- 42 - Lineo: Introduction to Real-time Linux ... Day 1 8.12 Build RTAI

8.12 Build RTAI Now build the RTAI modules and some example applications. export Use this to add /sbin to your PATH=/sbin:$PATH path.

Make and run and example. cd /usr/src/rtai Move to RTAI source dir. make make install Make rtai and the rtai FIFO make dev (this may devices. fail) cd examples/frank make Run a simple test example. ./run

Lineo Education Services

- 43 - 9 Real-Time Scheduling

9 Real-Time Scheduling

9.1 Task Structure A task is defined by a data structure of type RTTASK that contains all the information the real-time kernel needs to know about the task.

// this is the version 2.4 task structure struct rt_task_struct { int *stack; int uses_fpu; int magic; int state; unsigned long runnable_on_cpus; int *stack_bottom; int priority; int base_priority; RTIME period; RTIME resume_time; struct rt_queue queue; union { struct rt_task_struct *task; SEM *sem;} blocked_on; struct rt_queue msg_queue; unsigned int msg; struct rt_queue ret_queue; void (*signal)(void); long fpu_reg[66]; // ( was 27 ) struct rt_task_struct *prev; struct rt_task_struct *next;

// appended for calls from LINUX int *stack_top; wait_queue_head_t waitq; // changed for 2.4 kernels wait_queue_t lnxtsk; long long retval; char *msg_buf; int max_msg_size; char task_name[16]; void *system_data_ptr; struct rt_task_struct *nextp, *prevp; };

Lineo Education Services

- 44 - Lineo: Introduction to Real-time Linux ... Day 1 9.2 RTAI Scheduling

A task requires a function that defines the task behavior. Its prototype is as follows:

void rt_task (int param) { // do something }

After a task is created, it is made known to the system, by calling this function:

int rt_task_init ( RT_TASK *task, void (*thread)(int), int data, int stack_size, int priority, int uses_fpu, void (*signal)(void));

These are the real-time task state flags:

READY SUSPENDED DELAYED SEMAPHORE SEND RECEIVE RPC RETURN RUNNING DELETED

These can be "or’d" together to determine the task state.

9.2 RTAI Scheduling UP - Uniprocessor scheduler.

SMP - Symmetric Multiprocessor scheduler. Lineo Education Services

- 45 - 9.3 One-shot vs. Periodic

MUP - Multi-Uniprocessor scheduler.

9.3 One-shot vs. Periodic RTAI makes this interesting distinction between periodic timing mode and one-shot timing mode:

In periodic mode the timer interrupts at the interval specified by the period. Periodic tasks are synchronized to multiples of this period. This is relatively efficient because the timer registers don’t need to be reloaded on each timeout.

The down side is that the resolution is limited to multiples of the timer period.

9.4 RTAI Scheduling The core of the scheduler is easy to understand:

// periodic ( not one-shot ) scheduler

while ((task = task->next)) { if (task->state == READY && task->priority < prio) { new_task = task; prio = task->priority; } }

One-shot and pre-emption do provide some added complexity.

If it is time for Linux to run, this is the code that does it:

if (new_task == &rt_linux_task) { rt_switch_to_linux(0); restore_cr0(linux_cr0); }

9.5 RTAI Scheduling API These functions deal with the RTAI scheduler: Lineo Education Services

- 46 - Lineo: Introduction to Real-time Linux ... Day 1 9.5 RTAI Scheduling API

void rt_task_yield (void) Stops this task and places it at the bottom of the run queue

int rt_task_suspend (RT_TASK *task) Stops this task and removes it from the run queue

void rt_busy_sleep (int nanosecs) Locks this (CPU) for a few nanoseconds, no interrupts allowed

void rt_sleep (RTIME delay) Waits for a specified period; allows other tasks to run

void rt_sleep_until (RTIME time) Waits until an absolute time; allows other tasks to run

void rt_set_oneshot_mode (void) Sets up the timer to reprogram every interval (more accurate but expensive)

void_set_periodic_mode (void) Sets up the timer to NOT reprogram

Lineo Education Services

- 47 - 9.6 Real-Time Timer Functions

9.6 Real-Time Timer Functions These functions control the real-time timer:

RTIME start_rt_timer (int period) Starts the real-time timer

void stop_rt_timer (void) Stops the real-time timer

RTIME count2nano (RTIME ticks) Turns an RTIME count (CPU ticks or timer cycles) into nanoseconds

RTIME nano2count (RTIME nanosecs) Turns a number of nanoseconds into an RTIME count (CPU ticks or timer cycles)

RTIME rt_get_time (void) Gets the CPU time in CPU Ticks.

RTIME rt_get_time_ns (void) Gets the CPU time in nanosecs

RTIME rt_get_cpu_time_ns (void) Gets the CPU time in nanosecs; always uses the CPU clock

RTIME next_period (void) Gets the CPU time in CPU ticks for the next period

Lineo Education Services

- 48 - Lineo: Introduction to Real-time Linux ... Day 1 10 PWM Servo Controller Project

10 PWM Servo Controller Project

10.1 Signal Generation with a Simple Real-time Task A Pulse Width Modulation (PWM)-like control signal is to be generated via the parallel port for a standard Radio Control (RC) servo operation. In order for this to happen, there should be a 1 to 2 millisecond(ms) high impulse every 20 ms: 1 ms for Full Left position, 2 ms Full Right position, and 1.5 ms for Middle position (see the following diagram).

This output is to be produced by an RTAI real-time task. The file servo1.c in the following notes already contains a basic framework with these components:-

Header files Init/Cleanup function (partially complete) The (empty) task

10.2 Exercise: Basic Controller 10.2.1 Assignment Complete the spaces and select the multiple-choice options in the source code with the correct program code.

10.2.2 Tips The parallel port can be addressed with this function: "outb(< value>,< port>)"

The real-time task can be launched with rt_task_resume() after its initialization.

While loading the module, the pulse width (including the position of the servo) can beLineo given in nanoseconds Education (1 ms = 1,000 ns). Services

- 49 - 10.2.3 Basic RT Module Example

10.2.3 Basic RT Module Example

// basic task; servobasic.c from scratch; compile with // gcc -O2 -I/usr/src/linux/include -I/usr/src/rtai/include // -D__ KERNEL__ -DMODULE -c servobasic.c

#include #include #include #include

#include #include

#define PORT 0x378 RT_TASK task_data; static int position = 0; MODULE_PARM( position," i");

(A) 1 #define TIME1ms (B) 1000 (C) 1000000

(A) 19 #define TIME19ms (B) 19000 (C) 19000000

void task_code( int arg) {

pick one pick one (A) RT_TASK (A) rt_get_time_ns(); (B) RTIME nowns = (B) rt_get_time(); (C) int (C) gettimeofday();

/* start an infinite loop */ for (;;) { /* Lineoset port output Education on */ Services

- 50 - Lineo: Introduction to Real-time Linux ... Day 1 10.2.3 Basic RT Module Example

outb(0xff,PORT);

/* wait a while ( on time ) */ nowns += TIME1ms + position;

pick one (A)rt_task_suspend (B)rt_sleep_until ((turn nanoseconds into counts) (nowns)) (C)rt_busy_sleep

/* set port output off */ outb(0x00,PORT);

/* wait a while ( off time ) */ nowns += TIME19ms - position;

(A)rt_task_suspend (B)rt_sleep_until ((turn nanoseconds into counts) (nowns)) (C)rt_busy_sleep

/* end of loop */ } } int init_module( void) {

RT_TASK * rttask_struct; void * rttask_code; int rttask_param; int rttask_stack; int rttask_priority; int rttask_use_fp; void * rttask_signal;

printk(" servobasic: started, position=% i\n", position); rt_set_oneshot_mode(); start_rt_timer( 0);

Lineo Education Services

- 51 - 10.2.3 Basic RT Module Example

/* Set up the rt task */ /* fill in the missing xxxxxx rttask_struct = xxxxxx ; rttask_code = xxxxxx ; rttask_stack = xxxxxx ; rttask_param = 0 ; rttask_priotity = xxxxxx ; rttask_use_fp = xxxxxx ; rttask_signal = xxxxxx ;

rt_task_init( rttask_struct, rttask_code, rttask_param, rttask_stack, rttask_priority, rttask_use_fp, rttask_signal);

/* Start up the rt task */ /* pick one */ (A) rt_task_start(rttask_struct); (B) rt_task_delay(rttask_struct); (C) rt_task_resume(rttask_struct);

return 0; } void cleanup_module( void) {

RT_TASK * rttask_struct;

/* Set up the rt task */ rttask_struct = ;

/* Stop the rt task */ /* pick one */ (A) rt_task_delete(rttask_struct); (B) rt_task_stop(rttask_struct); (C) rt_task_delay(rttask_struct); Lineo Education Services

- 52 - Lineo: Introduction to Real-time Linux ... Day 1 10.3 Lab: Basic Servo Driver

stop_rt_timer(); printk(" servobasic: stopped, position=% i\n", position);

}

10.3 Lab: Basic Servo Driver

Action Description Get the code. mkdir -p Make a working directory. /$HOME/ClassLabs/Servo/basic/ cd /$HOME/ClassLabs/Servo/basic/ Go to the working directory. cp -av /mnt/train/code/ClassLabs/Servo/basic/* Copy the files. ./ Complete the code. kedit (or vi) servobasic.c Edit the code to make the correct real-time calls. Make the target. make (As root) sh servobasic_load 100000. Loads the driver and sets the servo position. This lists all the kernel modules presently loaded /sbin/lsmod and who is using them. You should see servobasic at the top of the list, it was the last module loaded. (As root) sh servobasic_reload 200000 Reloads the driver and sets the position. (As root) sh servobasic_unload Unloads the driver.

10.3.1 Basic RT Module Example Answer

// basic task; servobasic.c from scratch; compile with // gcc -O2 -I/usr/src/linux/include -I/usr/src/rtai/include // -D__KERNEL__ -DMODULE -c servobasic.c

#include #include #include #includeLineo Education Services

- 53 - 10.3.1 Basic RT Module Example Answer

#include #include

#define PORT 0x378 RT_TASK task_data; static int position = 0; MODULE_PARM( position,"i");

#define TIME1ms 1000000 #define TIME19ms 19000000 void task_code( int arg) {

RTIME nowns = rt_get_time_ns();

/* start an infinite loop */ for (;;) {

/* set port output on */ outb(0xff,PORT);

/* wait a while (on time) */ nowns += TIME1ms + position; rt_sleep_until(nano2count(nowns));

/* set port output off */ outb(0x00,PORT);

/* wait a while (off time) */ nowns += TIME19ms - position; rt_sleep_until(nano2count(nowns));

/* end of loop */ } } int init_module( void) {

RT_TASK * rttask_struct; void * rttask_code; int rttask_param; int rttask_stack; int rttask_priority; int rttask_use_fp; voidLineo * rttask_signal; Education Services

- 54 - Lineo: Introduction to Real-time Linux ... Day 1 10.3.2 Basic RT Module Example Notes

printk(" servobasic: started, position=% i\n", position); rt_set_oneshot_mode(); start_rt_timer( 0);

/* Set up the rt task */ rttask_struct = &task_data ; rttask_code = task_code ; rttask_stack = 3000 ; rttask_param = 0 ; rttask_priotity = 0 ; rttask_use_fp = 0 ; rttask_signal = NULL ;

rt_task_init( rttask_struct, rttask_code, rttask_param, rttask_stack, rttask_priority, rttask_use_fp, rttask_signal);

/* Start up the rt task */ rt_task_resume(rttask_struct);

return 0; } void cleanup_module( void) {

RT_TASK * rttask_struct;

/* Set up the rt task */ rttask_struct = &task_data; rt_task_delete(rttask_struct); stop_rt_timer(); printk(" servobasic: stopped, position=% i\n", position);

}

10.3.2 Basic RT Module Example Notes

Simple Start Turn on (insmod) and off (rmmod) the LED Just init and delete the task do not resume it Run the task once WatchLineo the use of time Education Services Time the task but take care of int overrun

- 55 - 10.3.2 Basic RT Module Example Notes

Use printk to examine data

Lineo Education Services

- 56 -