: Introduction to Real-time ... Day 3 Table of Contents 1 The Proc File System ...... 1 1.1 The Proc File System ...... 1 1.2 Creating a Proc Entry ...... 2 1.3 Creating a Proc Directory Entry ...... 3 1.4 Lab: Adding a Proc inerface to the FIFO example ...... 3 2 Real-time Interrupts ...... 4 2.1 Interrupt Handlers ...... 4 2.2 Interrupt Handler Arguments ...... 4 2.2.1 Interrupt Setup ...... 4 2.2.2 Interrupt Service ...... 4 2.3 Real-time Interrupts ...... 5 2.4 Real Time and Linux Interrupt Handlers ...... 6 2.4.1 Exercise 3.0: Interrupts and Their Latencies ...... 7 2.4.2 Information ...... 7 2.4.3 Assignment ...... 7 2.4.4 Tips ...... 7 2.4.5 Interrupt Latency example ...... 7 2.5 Lab: Servo Interrupt Latency ...... 10 3 Real-time Floating Point ...... 11 3.1 Floating Point ...... 11 3.2 Exercise: Floating-point Numbers in Real-Time ...... 11 3.2.1 Information ...... 11 3.2.2 Assignment ...... 11 3.2.3 Tips ...... 11 3.3 Lab: Servo Floating-Point ...... 12 4 Real-time User Code ...... 13 4.1 LXRT: Linux Real-time ...... 13 4.2 "Hard" Real Time versus "Soft" Real Time ...... 13 4.3 LXRT Object Name Space ...... 13 4.4 LXRT Mailboxes and Semaphores ...... 14 4.5 Hard Real-time from User Space ...... 14 4.6 Exercise 3.0: Real-time in User Space with LXRT ...... 14 4.6.1 Instructions ...... 14 4.6.2 Assignment ...... 14 4.6.3 Tips ...... 15 4.6.4 LXRT ( User Space Real Time) example ...... 15 4.7 Lab: Servo LXRT Lab ...... 17 5 Real Time POSIX ...... 18 5.1 POSIX Introduction ...... 18 5.2 Posix Threads and Processes ...... 19 5.2.1 Posix API ...... 19 5.3 RTAI 1.6 Problem ...... 20 5.4 POSIX Mutexes ...... 22 5.4.1 Exercise: Protecting Critical Regions with Mutexes ...... 22 5.4.2 Information ...... 22 5.4.3Lineo Assignment . Education...... Services...... 22

- i - 5.4.4 Tips ...... 23 5.4.5 Mutex Example ...... 23 5.5 Lab: POSIX Mutex Lab ...... 29 5.6 Exercise 3.4: Real-time with POSIX functions ...... 29 5.6.1 Information ...... 29 5.6.2 Assignment ...... 30 5.6.3 Tips ...... 30 5.6.4 RTAI POSIX Example ...... 30 5.7 Lab: Servo Posix Lab ...... 33 5.8 POSIX Message Queues ...... 34 5.8.1 POSIX Message Queue API ...... 34 6 Coding Standards ...... 35 6.1 Linux Kernel Coding Style ...... 35 6.1.1 Indentation ...... 35 6.1.2 Placing Braces ...... 35 6.1.3 Naming ...... 36 6.1.4 Functions ...... 37 6.1.5 Commenting ...... 37 6.1.6 You’ve made a mess of it ...... 37 7 Real Time Code Structure ...... 39 7.1 Real-time Design Overview ...... 39 7.2 Real-time Abstraction and Simulation ...... 40 7.2.1 Abstraction ...... 40 7.2.2 Simulation ...... 40 8 Combined Real Time Driver ...... 41 8.1 What We Are Trying to Do ...... 41 8.2 The Data Section ...... 42 8.3 The Fifo Section ...... 42 8.4 The Control Section ...... 42 8.5 The Driver Section ...... 42 8.6 The User Section(s) ...... 42 8.7 The Proc Section ...... 43 8.8 The IRQ Section ...... 43 8.9 Overall Summary ...... 43 9 Real Time - Futher Information ...... 44 9.1 Instructions on how to use the RTAI CVS tree ...... 44 9.1.1 Accessing the repository ...... 44 9.1.2 Updating the local tree ...... 44 9.1.3 Using the new Makefiles: ...... 44

Lineo Education Services

- ii - Lineo: Introduction to Real-time Linux ... Day 3 1 The Proc File System

1 The Proc File System

1.1 The Proc File System The proc file system provides a means of quick access to core kernel data. A simple access method is provided which can be used in user tasks.

The basic Proc file system is shown: dr-xr-xr-x 3 root root 0 August 25 15:23 1 dr-xr-xr-x 3 root root 0 August 25 15:23 2 dr-xr-xr-x 3 root root 0 August 25 15:23 3 dr-xr-xr-x 3 bin root 0 August 25 15:23 303 dr-xr-xr-x 3 nobody nobody 0 August 25 15:23 416 dr-xr-xr-x 3 daemon daemon 0 August 25 15:23 434 dr-xr-xr-x 3 xfs xsf 0 August 25 15:23 636 dr-xr-xr-x 4 root root 0 August 25 15:23 bus -r--r--r-- 1 root root 0 August 25 15:23 cmdline -r--r--r-- 1 root root 0 August 25 15:23 cpuinfo -r--r--r-- 1 root root 0 August 25 15:23 devices -r--r--r-- 1 root root 0 August 25 15:23 filesystems dr-xr-xr-x 2 root root 0 August 25 15:23 fs dr-xr-xr-x 4 root root 0 August 25 15:23 ide -r--r--r-- 1 root root 0 August 25 15:23 interrupts -r--r--r-- 1 root root 0 August 25 15:23 ioports

Lineo Education Services

- 1 - 1.2 Creating a Proc Entry

1.2 Creating a Proc Entry The code for generating the data seen in a proc entry is shown here:

#include

RTIME start_time = 0;

int rt_servo_proc_read (char *buf, char **start, off_t offset, int count, int *eof , void * data) { RTIME elapsed; int i; char * sp;

int len = 0; /* write up to 4096 bytes to buf return bytes written in len */

elapsed = rt_get_time_ns() - start_time; len = sprintf (buf, "Servo proc time: %d\n", (int) elapsed); /* * if we had more channels for (i = 0; i < N_CHANS; i++) { sp = &buf[len];

len += sprintf (sp, "Channel %d:, position = %d ns, \n", i, channel[i].position); } */ sp = &buf[len]; i = 0; len += sprintf (sp, "Channel %d:, position = %d ns, \n", i, position);

return len; } Lineo Education Services

- 2 - Lineo: Introduction to Real-time Linux ... Day 3 1.3 Creating a Proc Directory Entry

1.3 Creating a Proc Directory Entry This will create the rt_servo proc directory entry at the proc root:

/* add this in init_module */ proc_rt_servo = create_proc_entry("rt_servo", 0, 0); proc_rt_servo->get_info = rt_servo_proc_read;

/* add this in cleanup_module */ remove_proc_entry("rt_servo", &proc_root);

1.4 Lab: Adding a Proc inerface to the FIFO example

cd /$HOME/ClassLabs/Servo/proc cp /mnt/train/code/ClassLabs/Servo/proc/* ./ Edit servoproc.c to add the items listed above. make insmod servoproc.o cd /proc.

*Note that there is now an entry called "rt_servo." cat /proc/rt_servo

Lineo Education Services

- 3 - 2 Real-time Interrupts

2 Real-time Interrupts

2.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.

2.2 Interrupt Handler Arguments 2.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)

2.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

- 4 - Lineo: Introduction to Real-time Linux ... Day 3 2.3 Real-time Interrupts

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);

2.3 Real-time Interrupts A group of flags introduced by RTAI are used to detect the state of the system.

We are running either a real-time task or a normal Linux task.

void rt_switch_to_linux(int cpuid) { TRACE_RTAI_SWITCHTO_LINUX(cpuid); set_bit(cpuid, &global.used_by_linux); processor[cpuid].intr_flag = processor[cpuid].linux_intr_flag; }

void rt_switch_to_real_time(int cpuid) { TRACE_RTAI_SWITCHTO_RT(cpuid); processor[cpuid].linux_intr_flag = processor[cpuid].intr_flag; processor[cpuid].intr_flag = 0; clear_bit(cpuid, &global.used_by_linux); }

When an interrupt is serviced, it is passed to a dispatch_global_irq routine.

The logic here is as follows: Lineo Education Services

- 5 - 2.4 Real Time and Linux Interrupt Handlers

If you have a global interrupt assigned (by a real-time task), then use it. Otherwise set_bit(irq, &(global.pending_irqs)); If you are running Linux, use linux_sti() to trigger the interrupt service.

// extract from linux_sti()

rt_spin_lock_irq(&(global.data_lock)); if ((irq = global.pending_irqs & ~global.activ_irqs)) { irq = ffnz(irq); set_bit(irq, &global.activ_irqs); clear_bit(irq, &global.pending_irqs); rt_spin_unlock_irq(&(global.data_lock)); cpu->intr_flag = 0; do_linux_irq(global_vector[irq]); clear_bit(irq, &global.activ_irqs); cpu->intr_flag = intr_enabled; } else { rt_spin_unlock_irq(&(global.data_lock)); }

2.4 Real Time and Linux Interrupt Handlers You can chain a Linux interrupt after a real time interrupt

rt_pend_linux_irq(7) - will trigger a Linux irq

Claim the IRQ using request_irq(7,irq_handler,0,"servo",NULL);

Look at the code in /$HOME/ClassLabs/Servo/irq/servoirq2.c:

Lineo Education Services

- 6 - Lineo: Introduction to Real-time Linux ... Day 3 2.4.1 Exercise 3.0: Interrupts and Their Latencies

2.4.1 Exercise 3.0: Interrupts and Their Latencies 2.4.2 Information While building the previously proposed architecture, the signal source line DATA7 (Pin 9) short-circuited with the interrupt input /ACK (Pin 10) of the parallel port. A low-to-high transition causes an interrupt, providing that bit 4 (interrupt enable) has been set high in the control register of the parallel port (PORT+ 2, e. g. 0x37a).

2.4.3 Assignment Complete the (example) solution from Exercise 1.0 based on an RTAI interrupt handler designed to measure maximum interrupt latency time. The signal generator task takes care of the necessary low-> high transition. Nevertheless, determine the starting time of the positive impulse as the basis for the latency time evaluation in the interrupt handler. When the module is unloaded, you can utilize printk() to obtain the maximum latency.

2.4.4 Tips The interrupt handler function has no parameters. There is also no return value.

2.4.5 Interrupt Latency example

// lesson 3.0: interrupts; servo1.c from example 1.0; compile with // gcc -O2 -I/usr/src/rtai/include -D__KERNEL__ -DMODULE -c servoirq.c

#include #include #include #include

#include #include

#define PORT 0x378

#define TIME1ms 1000000 #define TIME19ms 19000000

RT_TASKLineo task_data; Education Services

- 7 - 2.4.5 Interrupt Latency example

static int position= 0; MODULE_PARM( position," i");

RTIME t0; static int tmax=0;

void task_code( int arg) {

RTIME now=rt_get_time_ns();

/* start an infinite loop */ while (1) { t0=rt_get_time_ns();

outb(0xff,PORT);

rt_sleep_until(nano2count(now+=(TIME1ms+position))); outb(0x00,PORT); rt_sleep_until(nano2count(now+=(TIME19ms-position)));

}

} void rt_irq_handler() { int tdiff; tdiff=rt_get_time_ns()-t0; if(tdiff>tmax) tmax=tdiff; } int init_module( void) {

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

outb(0x10,PORT+2); rt_startup_irq(7); rt_request_global_irq(7,rt_irq_handler);

rt_task_init(&task_data,task_code,0,3000,10,0,0); rt_task_resume(&task_data); return 0; } Lineo Education Services

- 8 - Lineo: Introduction to Real-time Linux ... Day 3 2.4.5 Interrupt Latency example

void cleanup_module(void) { rt_task_delete(&task_data); rt_free_global_irq(7); rt_shutdown_irq(7); outb(0x00,PORT+2); stop_rt_timer(); printk("servoirq: stopped, position=%i\n",position); printk("servoirq: max latency=%i\n",tmax); }

Lineo Education Services

- 9 - 2.5 Lab: Servo Interrupt Latency

2.5 Lab: Servo Interrupt Latency

Action Description Get the code. mkdir -p Make a working directory. /$HOME/ClassLabs/Servo/irq/ cd /$HOME/ClassLabs/Servo/irq/ Go to the working directory. cp -av /mnt/train/code/ClassLabs/Servo/irq/* Copy the files. ./

Complete the code. kedit servoirq.c Edit the code to make the correct irq set up calls. Make the target. cc -I/usr/src/rtai/include -O2 -DMODULE -D__KERNEL__ -c servoirq.c (As root) sh servoirq_load 400000 Load the driver. This lists all the kernel modules presently loaded and /sbin/lsmod who is using them. You should see servoirq at the top of the list, because it was the last module loaded. (As root) sh servoirq_unload Unload the driver. Type dmesg Display "printk" messages.

Lineo Education Services

- 10 - Lineo: Introduction to Real-time Linux ... Day 3 3 Real-time Floating Point

3 Real-time Floating Point

3.1 Floating Point The Linux kernel does not allow the use of floating-point numbers at the kernel level. As tasks switch, the floating point-information is saved, however, kernel level code is not supposed to have any floating-point overhead.

This is not often the case with real-time tasks. The basic rt_task_init function has a parameter which decides if the floating-point registers are saved. If set, the extra overhead is added to the task switch time. Another function is provided which can also set the floating-point use flag after task init.

3.2 Exercise: Floating-point Numbers in Real-Time 3.2.1 Information As a rule, the use of floating-point numbers is avoided in real-time systems, since the controller must be equipped with an Floating Point Unit (FPU) and the use of these additional registers must be taken into consideration during context changes. However, with more complex real-time systems, the use of floating-point numbers may be required.

In order to turn the FPU support on and off, RTAI offers this function: rt_task_use_fpu( RT_TASK* task, int use_fpu_flag);

3.2.2 Assignment Modify the (example) solution so that the current servo position is not directly set with the FIFO, but make the output sinusoidal with the servo position proportional to sin(t). The frequency of this output is now determined by a value supplied by the FIFO.

3.2.3 Tips omega=2*PI*f ( omega=radian frequency, f= frequency) M_PI is defined in the include file math.h. you will have to link in the fp library. The servo cannot follow wave frequencies greater than 1 Hz. The parameterLineo from us_appl Education to servofp should now be ofServices type float.

- 11 - 3.3 Lab: Servo Floating-Point

3.3 Lab: Servo Floating-Point

Action Description Get the code. mkdir -p Make a working directory. /$HOME/ClassLabs/Servo/fp/ cd /$HOME/ClassLabs/Servo/fp/ Go to the working directory. cp -av /mnt/train/code/ClassLabs/Servo/fp/* Copy the files. ./ Complete the code. kedit servofp.c Edit the code to make the correct floating point set up calls. Make the targets. cc -I/usr/src/rtai/include -O2 -DMODULE -D__KERNEL__ -DRTAI_RTF_NAMED -c servofp.c cc -I/usr/src/rtai/include -O2 -DRTAI_RTF_NAMED -o us_appl us_appl.c As root sh servofp_load Load the driver. This lists all the kernel modules presently loaded and /sbin/lsmod who is using them. You should see servofp at the top of the list, because it was the last module loaded. As root sh sfreq1 Set up the frequency 0.3 Hz. As root sh sfreq2 Set up the frequency 0.8 Hz. As root sh sfreq3 Set up the frequency 1.8 Hz. As root sh servofp_unload Unload the driver. As root type dmesg Displays "printk" messages.

Lineo Education Services

- 12 - Lineo: Introduction to Real-time Linux ... Day 3 4 Real-time User Code

4 Real-time User Code

4.1 LXRT: Linux Real-time The real-time scheduler API is available only to real_time tasks installed as modules.

The system known as LXRT makes that same API available to user space. This means that you can develop your real-time tasks in protected user space, making use of conventional debug tools.

Then, rebuild them as kernel modules.

4.2 "Hard" Real Time versus "Soft" Real Time Real-time tasks running as kernel modules implement hard real-time because the real-time scheduler task-switching time is small and the scheduling uncertainty has a finite (and reasonably small) upper bound.

Tasks running under "normal" LXRT must be considered soft real-time tasks because they are actually running under the Linux scheduler with its much larger scheduling uncertainty. They are permitted to use real-time services, however.

Tasks running under "hard" LXRT must be considered true, hard real-time actually running under the real-time scheduler. They are not permitted to use regular Linux services.

4.3 LXRT Object Name Space User space tasks don’t have access to the kernel symbol table and, more importantly, don’t have access to the symbols representing other tasks or communication objects defined in other processes. To get around this, and to allow user-space tasks to communicate with each other, LXRT defines a "name space" that allows objects (tasks, mailboxes, semaphores, etc.) to be referenced by a name.

LXRT works by creating real-time "agents" that act on behalf of the user-space tasks. The user-space task creates a real-time agent task to interact with other agent tasks. Lineo Education Services

- 13 - 4.4 LXRT Mailboxes and Semaphores

4.4 LXRT Mailboxes and Semaphores Mailboxes and semaphores are initialized in a similar fashion.

SEM *rt_sem_init (int name, int value); Initialize a semaphore.

MBX *rt_mbx_init (int name, int buf_size); Initialize a mailbox.

4.5 Hard Real-time from User Space The following functions permit the user task to switch into a soft, or hard, real-time mode:

void rt_make_hard_real_time (void) Schedule the task from the real time scheduler.

void rt_make_soft_real_time (void) Revert to using the regular Linux scheduler.

void rt_allow_nonroot_hrt (void) Allows a non-root user to run hard real time.

void print_to_screen (const char *format, ...) This provides a real-time safe printk ( printf ) function.

4.6 Exercise 3.0: Real-time in User Space with LXRT 4.6.1 Instructions

A programming mistake in kernel space often leads to a system crash. With LXRT, at least the user-space storage protection mechanism of the Linux kernel can be utilized, thereby helping to save development time. The real-time application is then launched as a normal user-space program, before being "converted" into real-time space.

4.6.2 Assignment Complete the /$HOME/ClassLabs/Servo/lxrt/servolxrt.c file based on the program given to produce signal generation of the RC servo from Exercise 1.0.

Lineo Education Services

- 14 - Lineo: Introduction to Real-time Linux ... Day 3 4.6.3 Tips

4.6.3 Tips

Before the first execution, load the kernel module lxrt. Use program us_appl.c from the shared memory exercise to set the high impulse time (in nanoseconds).

4.6.4 LXRT ( User Space Real Time) example

// lesson 3.3: Using LXRT; servolxrt.c ; compile with // gcc -O2 -I/usr/src/rtai/include -o servolxrt servolxrt.c

#include #include #include

#include #include #include #include "rtai_lxrt.h" #include

#define PORT 0x378 #define TIME1ms 1000000 #define TIME19ms 19000000 int main() { int *position_shm; RT_TASK *task_data; struct sched_param mysched;

position_shm=rtai_malloc(0x1234,sizeof(int)); *position_shm=500000;

mysched.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1; sched_setscheduler(0,SCHED_FIFO,&mysched);

mlockall(MCL_CURRENT | MCL_FUTURE);

task_data = rt_task_init(0x1000, 1, 0, 0);

ioperm(PORT,1,1);

rt_set_oneshot_mode(); start_rt_timer(0); rt_make_hard_real_time();Lineo Education Services

- 15 - 4.6.4 LXRT ( User Space Real Time) example

{ RTIME now=rt_get_time_ns();

while(*position_shm!=-1) { outb(0xff,PORT); rt_sleep_until(nano2count(now+=(TIME1ms+*position_shm))); outb(0x00,PORT); rt_sleep_until(nano2count(now+=(TIME19ms-*position_shm))); } }

rt_make_soft_real_time(); rt_task_delete(task_data); stop_rt_timer();

ioperm(PORT,1,0);

rtai_free(0x1234,position_shm);

return 0; }

We also need this user task to set up the position in shared memory:

// lesson 3.3: lxrt; us_appl.c taken from lesson 3.0; compile with // gcc -O2 -I/usr/src/rtai/include -o us_appl us_appl.c

#include #include

#include #include int main(int argc,char* argv[]) { int position=(argc==2)? atoi(argv[1]) : 0;

int* position_shm=rtai_malloc(0x1234,sizeof(int)); *position_shm=position; rtai_free(0x1234,position_shm);

return 0; } Lineo Education Services

- 16 - Lineo: Introduction to Real-time Linux ... Day 3 4.7 Lab: Servo LXRT Lab

4.7 Lab: Servo LXRT Lab

Action Description Get the code. mkdir -p Make a working directory. /$HOME/ClassLabs/Servo/lxrt/ cd /$HOME/ClassLabs/Servo/lxrt/ Go to the working directory. cp -av /mnt/train/code/ClassLabs/Servo/lxrt/* Copy the files. ./

Complete the code. kedit servolxrt.c Edit the code to make the correct LXRT calls. Make the targets. cc -I/usr/src/rtai/include -O2 -o servolxrt servolxrt.c cc -I/usr/src/rtai/include -O2 -o us_appl us_appl.c As root sh servolxrt_load Loads the driver and its required modules. The LXRT task has allowed the real-time kernel to take over the user code. No signals are serviced. We will Open another window need to use the user task in another window to communicate. cd /$HOME/ClassLabs/Servo/lxrt/ Go to the working directory. Type sh spos1 Set the position to 200000. Type sh spos2 Set the position to 800000. Type sh spos_quit Set the position to -1, causes the LXRT task to quit. Type sh servolxrt_unload Unload the driver. Type dmesg Display "printk" messages.

Lineo Education Services

- 17 - 5 Real Time POSIX

5 Real Time POSIX

5.1 POSIX Introduction The POSIX API is intended to be a small portable interface that allows applications to be easily transported between different operationg systems.

POSIX number Description 1003.lb Real-time extensions Priority scheduling Process memory locking Semaphores Shared Memory Real-time signal extensions Clocks and timers Messages 1003.lc Pthreads 1003.ld Further real-time extensions

Lineo Education Services

- 18 - Lineo: Introduction to Real-time Linux ... Day 3 5.2 Posix Threads and Processes

5.2 Posix Threads and Processes

In conventional UNIX, a process consists of code and resources, such as memory, files, etc, that the code manipulates. The resources of a process are owned by that process and cannot be directly accessed by any other process.

5.2.1 Posix API int pthread_create (pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);

void pthread_exit (void *retval);

pthread_t pthread_self (void);

int sched_yield (void);

int pthread_attr_init (pthread_attr_t *attr);

int pthread_attr_destroy (pthread_attr_t *attr);

int pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate);

int pthread_attr_getdetachstate (const pthread_attr_t *attr, int *detachstate);Lineo Education Services

- 19 - 5.3 RTAI 1.6 Problem

int pthread_attr_setschedparam (pthread_attr_t *attr, const struct sched_param *param);

int pthread_attr_getschedparam (const pthread_attr_t *attr, struct sched_param *param);

int pthread_attr_setschedpolicy (pthread_attr_t * attr, int policy);

int pthread_attr_getschedpolicy (const pthread_attr_t *attr, int *policy);

int pthread_attr_setinheritsched (pthread_attr_t *attr, int inherit);

int pthread_attr_getinheritsched (const pthread_attr_t *attr, int *inherit);

int pthread_attr_setscope (pthread_attr_t *attr, int scope);

int pthread_attr_getscope (const pthread_attr_t *attr, int *scope);

5.3 RTAI 1.6 Problem RTAI-1.6 did not have a complete POSIX cond timed wait interface. This is being introduced into RTAI-1.7.

We would like to use this but are wary of using the rest of RTAI-1.7 until we have a chance to test it all out.

A quick question on the mailing list rtai.org gets an answer that we could safely steal the code from RTAI-1.7 and insert it into the cond_timed_wait code for rtai-1.6

This diff file shows the modifications we will make to the original.

Lineo Education Services

- 20 - Lineo: Introduction to Real-time Linux ... Day 3 5.3 RTAI 1.6 Problem

--- /usr/src/rtai/posix/rtai_pthread2.c Fri Jun 15 05:03:00 2001 +++ /usr/src/rtai/posix/rtai_pthread.c Fri Jun 15 05:04:48 2001 @@ -1084,7 +1084,36 @@

} // End function - pthread_cond_wait

+int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime) { + + unsigned long flags, semret; + RTIME time; + RT_TASK *current_task; + + semret = 0; + time = nano2count((1000000000LL)*abstime->tv_sec + abstime->tv_nsec); + current_task = rt_whoami(); + flags = rt_global_save_flags_and_cli(); + current_task->resume_time = time; + current_task->state |= (SEMAPHORE | DELAYED); + current_task->blocked_on.sem = &(cond->c_waiting); + cond_enqueue_task(cond); + rt_global_restore_flags(flags); + pthread_mutex_unlock(mutex); + flags = rt_global_save_flags_and_cli(); + LXRT_SUSPEND; + if (current_task->blocked_on.sem) { + current_task->blocked_on.sem = 0; + semret = 1; + } + rt_global_restore_flags(flags); + pthread_mutex_lock(mutex); + return (semret ? ETIMEDOUT : 0); + +} // End function - pthread_cond_timedwait

+#if 0 int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) {

@@ -1097,6 +1126,7 @@

} // End function - pthread_cond_timedwait

+#endif

int pthread_cond_signal(pthread_cond_t *cond) {

Lineo Education Services The lab will copy a corrected version of the code into the rtai-1.6 system.

- 21 - 5.4 POSIX Mutexes

5.4 POSIX Mutexes The term mutex is short for mutual exclusion. A mutex is very much like a binary semaphore. Like a semaphore, the mutex is used to control and synchronize access to shared resources. A thread wishing to use a shared resource (a shared memory buffer for example) must first lock the mutex. The thread is blocked until it receives the lock. After using the resource, the thread unlocks the mutex.

5.4.1 Exercise: Protecting Critical Regions with Mutexes 5.4.2 Information With the help of mutual exclusions, a task can temporarily prevent its resources from being used by other tasks. In this way, a mutex functions like a "Do Not Disturb" sign on a hotel door; only after explicit release can the resource be utilized by another task.

5.4.3 Assignment Complete the program servomutex.c from your (example) solution in Exercise 2.3 based on the following code. Be sure that task B is activated at module loading and deactivated when the module is unloaded.

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void taskB_code( int arg) { for(;;) { pthread_mutex_lock(& mutex); rt_busy_sleep( nano2count( 30000000)); pthread_mutex_unlock(& mutex); rt_sleep( nano2count( 3000000)); } }

Task B should have a lower priority than the signal generator task. Assume that the outb() calls in the signal generator task were areas that needed protection and modify the necessary code.

What effect can be expected? What is this effect called?

Lineo Education Services

- 22 - Lineo: Introduction to Real-time Linux ... Day 3 5.4.4 Tips

5.4.4 Tips For mutexes, the header file rtai_pthread.h and the kernel module rtai_prthread are required.

The program us_appl can again be used for parameter passing.

5.4.5 Mutex Example

// mutexes; servomutex.c ; compile with // gcc -O2 -I/usr/src/linux/include -I/usr/src/rtai/include // -I/usr/src/rtai/posix/include // -D__KERNEL__ -DMODULE -DRTAI_RTF_NAMED -c servomutex.c

#include #include #include #include

#include #include #include #include

#define PORT 0x378

RT_TASK taskA_data; RT_TASK taskB_data;

int position= 0; MODULE_PARM( position," i"); int fifo=0;

#define TIME1ms 1000000 #define TIME19ms 19000000 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void taskA_code(Lineo int Educationarg) { Services

- 23 - 5.4.5 Mutex Example

RTIME nowns = rt_get_time_ns();

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

/* lock the mutex */

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

/* unlock the mutex */

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

/* lock the mutex */

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

/* unlock the mutex */

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

/* Lineoend of loop Education */ Services

- 24 - Lineo: Introduction to Real-time Linux ... Day 3 5.4.5 Mutex Example

} } void taskB_code(int arg) { for(;;) { pthread_mutex_lock(&mutex); rt_busy_sleep(nano2count(30000000)); pthread_mutex_unlock(&mutex); rt_sleep(nano2count(30000000)); } }

/* fifo handler (we have done all this before) */ int fifo_handler(unsigned int f) { int data,got; position_t p;

while((got=rtf_get(fifo,&data,sizeof(int)))==sizeof(int)) { p.pos=data;

}

if(got !=0) return -EINVAL; return 0; }

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(" servomutex: started, position=% i\n", position); rt_set_oneshot_mode(); start_rt_timer( 0);

/* set up fifos */ fifo=rtf_create_named("myfifo"); rtf_create_handler(fifo,fifo_handler);Lineo Education Services

- 25 - 5.4.5 Mutex Example

/* Set up the driver task */ rttask_struct = &taskA_data; rttask_code = taskA_code; rttask_param = 0; rttask_stack = 3000; rttask_priotity =10; rttask_use_fp = 0; rttask_signal = 0;

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

/* Set up the driver task */ rttask_struct = &taskB_data; rttask_code = taskB_code; rttask_param = 0; rttask_stack = 3000; rttask_priotity =20; rttask_use_fp = 0; rttask_signal = 0;

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

/* Start up the rt A task */ rt_task_resume(&task_data);

/* Start up the rt B task */ rt_task_resume(&taskB_data);

return 0; } void cleanup_module( void) { RT_TASKLineo * rttask_struct; Education Services

- 26 - Lineo: Introduction to Real-time Linux ... Day 3 5.4.5 Mutex Example

/* stop the receiver task */ rttask_struct = &taskA_data; rt_task_delete(rttask_struct);

/* stop the disrupter task */ rttask_struct = &taskB_data; rt_task_delete(rttask_struct);

rtf_destroy(fifo);

/* what about the mutex */

**delete the mutex somehow** ??

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

}

A user task is needed.

// posix mutex; us_appl.c; compile with: // gcc -Wall -O2 -I/usr/src/rtai/include // -DRTAI_RTF_NAMED -o us_appl us_appl.c

#include #include #include #include char* fifo_device[]={"/dev/rtf0","/dev/rtf1","/dev/rtf2","/dev/rtf3", "/dev/rtf4","/dev/rtf5","/dev/rtf6","/dev/rtf7"}; int main(int argc,char* argv[]) { int position=(argc==2)? atoi(argv[1]) : 0; int fifo_number;

/* open the fifo */ fifo_number=rtf_getfifobyname("myfifo"); int fd=open(fifo_device[fifo_number],O_WRONLY); write(fd,&position,sizeof(int));Lineo Education Services

- 27 - 5.4.5 Mutex Example

close(fd); return 0; }

Lineo Education Services

- 28 - Lineo: Introduction to Real-time Linux ... Day 3 5.5 Lab: POSIX Mutex Lab

5.5 Lab: POSIX Mutex Lab

Action Description Get the code. mkdir -p /$HOME/ClassLabs/Servo/mutex/ Make a working directory. cd /$HOME/ClassLabs/Servo/mutex/ Go to the working directory. cp -av /mnt/train/code/ClassLabs/Servo/mutex/* ./ Copy the files. Complete the code. kedit servomutex.c Edit the code to make the correct calls. Make the targets. cc -I/usr/src/rtai/include -O2 -DMODULE -D__KERNEL__ -DRTAI_RTF_NAMED -c servomutex.c cc -I/usr/src/rtai/include -O2 -DRTAI_RTF_NAMED -o us_appl us_appl.c Load the driver and its As root sh servomutex_load required modules. This task still uses a Open another window. parameter to set the servo position. cd /$HOME/ClassLabs/Servo/mutex/ Go to the working directory. (As root) sh spos1 Set the position to 200000. (As root) sh spos2 Set the position to 800000. (As root) sh servomutex_unload Unloads the driver. (As root) type dmesg Display "printk" messages.

5.6 Exercise 3.4: Real-time with POSIX functions 5.6.1 Information The RTAI API is quite extensive, but in its basic form is not POSIX-compliant. Tested POSIX-compliant program code exists in many businesses, the ability to use and reuse it (with the help of a POSIX overlay module) is beneficial. RTAI offers a subset of the pthread task functions. Lineo Education Services

- 29 - 5.6.2 Assignment

5.6.2 Assignment Replace the following RTAI functions from the (sample) solution in Exercise 1.0 with POSIX-compliant instructions: rt_get_time_ns() rt_sleep_until() rt_task_init()

5.6.3 Tips The following code may be applicable for the solution: void add_ns( struct timespec *time, long ns) { time-> tv_nsec += ns; time-> tv_sec += time-> tv_nsec / ( 1000000000LL); time-> tv_nsec = time-> tv_nsec % ( 1000000000LL); };

5.6.4 RTAI POSIX Example

// lesson posix; servoposix.c from example 1.0; compile with // gcc -O2 -I/usr/src/rtai/include -D__KERNEL__ -DMODULE \ // -DRTAI_RTF_NAMED -c servoposix.c

#include #include #include #include

#include #include #include #include

#define PORT 0x378 pthread_t thread_data; pthread_mutex_t wait_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t wait_cond = PTHREAD_COND_INITIALIZER; Lineo Education Services

- 30 - Lineo: Introduction to Real-time Linux ... Day 3 5.6.4 RTAI POSIX Example

volatile int kill=0;

#define TIME1ms 1000000 #define TIME19ms 19000000 static int position= 0; MODULE_PARM( position," i"); static int fifo;

void add_ns(struct timespec *time,long ns) { time->tv_nsec+=ns; time->tv_sec += time->tv_nsec/(1000000000LL); time->tv_nsec = time->tv_nsec%(1000000000LL); }

void thread_code( int arg) {

struct timespec time; clock_gettime(CLOCK_REALTIME,&time);

/* start an infinite loop */ while (!kill) { outb(0xff,PORT); add_ns(&time,TIME1ms+position);

pthread_mutex_lock(&wait_mutex); pthread_cond_timedwait(&wait_cond,&wait_mutex,&time); pthread_mutex_unlock(&wait_mutex);

outb(0x00,PORT); add_ns(&time,TIME19ms-position);

pthread_mutex_lock(&wait_mutex); pthread_cond_timedwait(&wait_cond,&wait_mutex,&time); pthread_mutex_unlock(&wait_mutex);

}

kill=2; pthread_exit(0);

} Lineo Education Services

- 31 - 5.6.4 RTAI POSIX Example

int fifo_handler(unsigned int f) { int data,got;

while((got=rtf_get(fifo,&data,sizeof(int)))==sizeof(int)) position=data;

if(got !=0) return -EINVAL; return 0; }

int init_module( void) {

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

/* set up fifos */ fifo=rtf_create_named("myfifo"); rtf_create_handler(fifo,fifo_handler);

pthread_create(&thread_data,0,(void*)thread_code,0);

return 0; } void cleanup_module( void) {

kill=1; while(kill!=2);

// pthread_cond_destroy(&wait_cond); // pthread_mutex_destroy(&wait_mutex);

rtf_destroy(fifo); stop_rt_timer(); printk(" servoposix: stopped, position=% i\n", position);

}

Lineo Education Services

- 32 - Lineo: Introduction to Real-time Linux ... Day 3 5.7 Lab: Servo Posix Lab

5.7 Lab: Servo Posix Lab

Action Description Get the code. mkdir -p /$HOME/ClassLabs/Servo/posix/ Make a working directory. cd /$HOME/ClassLabs/Servo/posix/ Go to the working directory. cp -av /mnt/train/code/ClassLabs/Servo/posix/* ./ Copy the files. Complete the code. kedit servoposix.c Edit the code to make the correct calls. Make the targets. cc -I/usr/src/rtai/include -O2 -DMODULE -D__KERNEL__ -DRTAI_RTF_NAMED -c servoposix.c cc -I/usr/src/rtai/include -O2 -DRTAI_RTF_NAMED -o us_appl us_appl.c The rtai pthread code Fix up the rtai code. does not contain the timed_wait service. In another window. Move to the rtai posix cd /usr/src/rtai/posix directory. cp /mnt/train/code/ClassLabs/Servo/posix/rtai_pthread_mod.c Modify the posix pthread rtai_pthread.c file. cd /usr/src/rtai/ Move to the rtai make directory and remake make install RTAI. Load the driver and any (As root) sh servoposix_load required modules. This task still uses a Open another window. parameter to set the servo position. cd /$HOME/ClassLabs/Servo/posix/ Go to the working directory. Set the position to (As root) sh spos1 200000. Lineo Education Services

- 33 - 5.8 POSIX Message Queues

Set the position to (As root) sh spos2 800000. (As root) sh servoposix_unload Unload the driver. Display "printk" Type dmesg messages.

5.8 POSIX Message Queues

POSIX message queues are similar in concept to the FIFOs we’ve already seen. The difference is that the data elements passed through queues are discrete messages. The maximum number of messages and maximum message size are specified when a queue is created.

5.8.1 POSIX Message Queue API mqd_t mq_open (char *mq_name, int oflags, mode_t permissions, struct mq_attr *mq_attr);

size_t mq_receive (mqd_t mq, char *msg_buffer, size_t buflen, unsigned int *msgprio);

int mq_send (mqd_t mq, const char *msg, size_t msglen, unsigned int msgprio);

int mq_close (mqd_t mq);

int mq_getattr (mqd_t mq, struct mq_attr *attrbuf);

int mq_setattr (mqd_t mq, const struct mq_attr *new_attrs, struct mq_attr *old_attrs);

int mq_notify (mqd_t mq, const struct sigevent *notification);

int mq_unlink (mqd_t mq);

Lineo Education Services

- 34 - Lineo: Introduction to Real-time Linux ... Day 3 6 Coding Standards

6 Coding Standards

Some Web references for standards can be found at www.cs.umd.edu/users/cml/cstyle/.

The Association of C and C++ Users (www.accu.org) (ACCU) is a non-profit organisation run by dedicated programmers in C, C++ and Java for the benefit of programmers. Many of its members are active on international standardisation committees for programming languages, and quite a few are willing to answer questions either personally or via mailing lists.

The Gnu standard is found here: http://www.gnu.org/prep/standards.html

6.1 Linux Kernel Coding Style

This is a rewrite of a document obtained from: www.linuxhq.org

This section describes the preferred coding style for the Linux kernel. Coding style is very personal, and there is no attempt to force these standards on anybody. At least consider the points made here.

6.1.1 Indentation

Tabs are 8 characters, and thus indentations are also 8 characters. There are systems that make indentations 4 (or even 2!) characters deep.

Rationale: The whole idea behind indentation is to clearly define where a block of control starts and ends. Especially when you’ve been looking at your screen for 20 straight hours, you’ll find it a lot easier to see how the indentation works if you have large indentations.

Some will claim that having 8-character indentations makes the code move too far to the right, and makes it hard to read on a 80-character terminal screen. The answer to that is that if you need more than 3 levels of indentation, you should fix your program.

In short, 8-char indents make things easier to read, and have the added benefit of warning you when you’re nesting your functions too deep.

6.1.2 Placing Braces

The other issue that always comes up in C styling is the placement of braces. Unlike the indent size, there are few technical reasons to choose one placement strategy over the other, but the preferred way, as shown to us by Kernighan and Ritchie, is to put the opening brace last on the line, and put the closing brace first, like this:

if (x is true) { we do y } Lineo Education Services

- 35 - 6.1.3 Naming

However, there is one special case, namely functions: they have the opening brace at the beginning of the next line, thus:

int function(int x) { body of function }

Functions are special (you can’t nest them in C).

Note that the closing brace is empty on a line of its own, except in the cases where it is followed by a continuation of the same statement, ie a "while" in a do-statement or an "else" in an if-statement, like this:

do { body of do-loop } while (condition); and

if (x == y) { .. } else if (x > y) { ... } else { .... }

Rationale: K&R.

Also, note that this brace-placement also minimizes the number of empty (or almost empty) lines, without any loss of readability.

6.1.3 Naming

C is a Spartan language, and so should your naming be. Unlike Modula-2 and Pascal programmers, C programmers do not use cute names like ThisVariableIsATemporaryCounter. A C programmer would call that variable "tmp", which is much easier to write, and not the least more difficult to understand.

HOWEVER, while mixed-case names are frowned upon, descriptive names for global variables are a must. To call a global function "foo" is poor practice.

GLOBAL variables (to be used only if you really need them) should have descriptive names, as do global functions. If you have a function that counts the number of active users, you should call that "count_active_users()" or similar, you should not call it "cntusr()". Lineo Education Services

- 36 - Lineo: Introduction to Real-time Linux ... Day 3 6.1.4 Functions

Encoding the type of a function into the name (so-called Hungarian notation) is not required, the compiler knows the types anyway and can check those, and it only confuses the programmer.

LOCAL variable names should be short, and to the point. If you have some random integer loop counter, it should probably be called "i". Calling it "loop_counter" is non-productive, if there is no chance of it being mis-understood. Similarly, "tmp" can be just about any type of variable that is used to hold a temporary value.

6.1.4 Functions

Functions should be short and do just one thing. They should fit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24, as we all know), and do one thing and do that well.

The maximum length of a function is inversely proportional to the complexity and indentation level of that function. So, if you have a conceptually simple function that is just one long (but simple) case-statement, where you have to do lots of small things for a lot of different cases, it’s OK to have a longer function.

However, if you have a complex function, and you suspect that a less-than-gifted first-year high-school student might not even understand what the function is all about, you should adhere to the maximum limits all the more closely. Use helper functions with descriptive names (you can ask the compiler to in-line them if you think it’s performance-critical, and it will probably do a better job of it that you would have done).

Another measure of the function is the number of local variables. They shouldn’t exceed 5-10, or you’re doing something wrong. Re-think the function, and split it into smaller pieces. A human brain can generally easily keep track of about 7 different things, anything more and it gets confused.

6.1.5 Commenting

Comments are good, but there is also a danger of over-commenting. NEVER try to explain HOW your code works in a comment: it’s much better to write the code so that the working is obvious, and it’s a waste of time to explain badly written code.

Generally, you want your comments to tell WHAT your code does, not HOW it does it. Also, try to avoid putting comments inside a function body. If the function is so complex that you need to separately comment parts of it, you should probably consider a redesign. You can make small comments to note or warn about something particularly clever (or ugly), but try to avoid excess. Instead, put the comments at the head of the function, telling people what it does, and possibly WHY it does it.

6.1.6 You’ve made a mess of it

That’s OK, we all do. You’ve probably been told by your long-time UNIX user helper that "GNU emacs" automatically formats the C sources for you, and you’ve noticed that yes, it does do that, but the defaults it uses are less than desirable. Lineo Education Services

- 37 - 6.1.6 You’ve made a mess of it

So, you can either get rid of GNU emacs, or change it to use saner values. To do the latter, you can stick the following in your .emacs file:

(defun linux-c-mode () "C mode with adjusted defaults for use with the Linux kernel." (interactive) (c-mode) (c-set-style "K&R") (setq c-basic-offset 8))

This will define the M-x linux-c-mode command. When hacking on a module, if you put the string -*- linux-c -*- somewhere on the first two lines, this mode will be automatically invoked. Also, you may want to add

(setq auto-mode-alist (cons ’("/usr/src/linux.*/.*\\.[ch]$" . linux-c-mode) auto-mode-alist)) to your .emacs file if you want to have linux-c-mode switched on automagically when you edit source files under /usr/src/linux.

But even if you fail in getting emacs to do sane formatting, not everything is lost: use "indent".

Now, again, GNU indent has the same default settings that GNU emacs has, which is why you need to give it a few command line options. However, that’s not too bad, because even the makers of GNU indent recognize the authority of K&R, so you just give indent the options "-kr -i8" (stands for "K&R, 8 character indents").

The program "indent" has a lot of options, and especially when it comes to comment re-formatting you may want to take a look at the manual page. But remember: "indent" is not a fix for bad programming.

Lineo Education Services

- 38 - Lineo: Introduction to Real-time Linux ... Day 3 7 Real Time Code Structure

7 Real Time Code Structure

7.1 Real-time Design Overview

1. Identify library components : Prepare spec for function. Build into Module. Create test harness. 2. Define Data Structure: Make use of named areas. Open ended, self defining structure. Use semaphores for read and write. Use an update flag to indicate changes. 3. Create an abstraction Layer: Switch in system components as modules are loaded. Allow the user to simulate or use the real system. Try modifications at run time. 4. Shared Memory Used for state data. Needs global and area semaphore protection. Use an update counter to flag changes. Create watchdogs for critical processes. 5. User Setup task: LineoSets up and controls Education system states. Services

- 39 - 7.2 Real-time Abstraction and Simulation

Provides run-time corrections. Communicates via fifos. 6. User Grapics task: Create self defining data structures. Communicate via FIFOs for events, alarms, etc. Communicate via shared memory for status. 7. User Control task: If possible, uses self defining data structures. Communicate via FIFOs for events, alarms etc Communicate via shared memory for status

7.2 Real-time Abstraction and Simulation 7.2.1 Abstraction 1. Create a table of functions, possibly index by name Fill in with a dummy task. Allow overwrite of this task with subsequent modules. 2. Use /proc interface: Create data structures. Read/write to data. View owners of semaphores and tasks.

7.2.2 Simulation 1. Simulate layers of I/O: Start with simple I/O. Move on to more complex systems. Use a state based design. 2. Use /proc interface Observe data values. Control state. Adjust setpoints and variables.

Lineo Education Services

- 40 - Lineo: Introduction to Real-time Linux ... Day 3 8 Combined Real Time Driver

8 Combined Real Time Driver

8.1 What We Are Trying to Do We are going to try to combine all these concepts into a driver for our servo.

Here is a list of ideas:

1. Use a regular char driver to send instructions. 2. Control more than one servo. 3. Keep the fifo concept. 4. Keep the shared memory concept. 5. Use a proc interface. 6. Aloow the system to service an interrupt and chain a Linux interrupt.

We have the basics for a char driver:

Create a file ops table. Register the device. Combine the read/write code with the FIFO handler.

We want more than one servo and so we need a structure. Here are the components for a simple structure:

Servo name Current position Servo number Port "and" mask Port "or" mask for on

We can easily keep the shared memory concept, but we need a more complex command structure.

1. Command length 2. Servo number 3. Command type 1. Set position 2. Set name 4. Command data Lineo Education Services

- 41 - 8.2 The Data Section

Start with the data structure and abstraction layer design.

8.2 The Data Section This section will allocate the shared memory for the project and provide the driver HAL with an initial set up. The HAL concept is not fully developed here but provides a conceptual example.

The data section also sets up data access and initialization routines.

The code is in ClassLabs/Servo/comb/servodata.c

8.3 The Fifo Section This section will set up the FIFO and the FIFO handler. The FIFO handler will use the data decoder in the data section. Note that the checking of the message sync has been omitted for clarity.

The code is in ClassLabs/Servo/comb/servofifo.c

8.4 The Control Section This section will set up the output control. It uses information in the Data section to send out values to the parallel port. The system only needs the Data section and the Controller section to do any work. The other sections are there to monitor and modify the control data.

The code is in ClassLabs/Servo/comb/servocont.c

8.5 The Driver Section This section will set up a device driver to modify and access the system data. This is the same code as we found in our simple scmd_read.c file.

When it gets a command, it uses the "decode_command" function in the data section. The read function has not been fully developed.

The code is in ClassLabs/Servo/comb/servodriv.c

8.6 The User Section(s) There are two user space drivers:

One will use FIFOs to deliver data to the application, and the other uses our device Lineodriver code. The choiceEducation of which to use is arbitrary. Services

- 42 - Lineo: Introduction to Real-time Linux ... Day 3 8.7 The Proc Section

The code is in ClassLabs/Servo/comb/(us_appl.c and us_appl2.c)

8.7 The Proc Section This is an optional extra that allows you to see the data using the /proc file system. You can see each servo as a separate file, data on each servo is provided. An extension to this code would be to set the position of the servo by writing to the /proc file system.

The code is in ClassLabs/Servo/comb/servoproc.c

8.8 The IRQ Section This is another optional extra that allows you to trigger a Linux interrupt after completion of each frame of data. Provision is also there for a real-time interrupt to trigger a real-time event.

The code is in ClassLabs/Servo/comb/servoirq.c

8.9 Overall Summary This is a crude example of modular development with Linux. In the real world more protection and better data handling would be required.

Good flexibility is achieved by designing partitions within the application. Modules can be replaced, even at run time, without stopping the application’s work. You can even design a mechanism to load another control module and have it "kick in" when the initial control module is removed.

This ability to have multiple driver and data monitoring options gives an application great flexibility.

Lineo Education Services

- 43 - 9 Real Time - Futher Information

9 Real Time - Futher Information

9.1 Instructions on how to use the RTAI CVS tree 9.1.1 Accessing the repository

Log in (password is "anoncvs"):

cvs -d :pserver:[email protected]:/opt/cvsroot/rtai login

Check out the RTAI CVS tree:

cvs -d :pserver:[email protected]:/opt/cvsroot/rtai co rtai4 rtai-22 rtai-24

The CVS repository is called "rtai4", because it is the 4th CVS repository that we’ve used. The number has no correllation with the release version number.

9.1.2 Updating the local tree

Periodically, in order to keep up-to-date, you may want to download changes instead of an entire tree. In the rtai4 directory, run:

cvs update -dP

(The -d and -P options are convenient, but not necessary. See the CVS man page for details.)

* Creating patches:

This command will create a patch with all the local modifications that you have made:

cvs diff -u

9.1.3 Using the new Makefiles:

In the top directory (rtai4/), run: ./use_dsLineo Education Services

- 44 - Lineo: Introduction to Real-time Linux ... Day 3 9.1.3 Using the new Makefiles:

This checks out a different CVS branch on a bunch of files. This change is "sticky", meaning it is not affected by updates. Thus, updates and check-ins will only use the new Makefile branch, not the main branch.

Lineo Education Services

- 45 -