Captain's Universe Home
Captain's Universe Home
Cosmic Ray Muon DetectorTeleGarden Pages
Time on MarsBryophyllum Plants
Jupiter Radio AstronomyAncient Pages
Salzburg Tourist GuideEarth Magnetometer
  H O M E     AJAX & MORE     LINUX & MORE     RTAI     XENOMAI     ADEOS IPIPE      
    JAVA & BROWSERS     *NIX     ELECTRONICS     REVIEWS     ARTEMIA     FAIRY SHRIMP      


Adeos Nano Kernel Linux Hard Real Time Parallel Port Interrupt Handler Kernel Module - extended standalone version

This example show how to capture the parallel port interrupt with an ADEOS (www.adeos.org) patched vanilla Linux kernel. Using the Adeos nano kernel makes this example running in hard real time, since the Linux kernel is running only as Adeos idle task. This means, if there is nothing to do in any Adeos domain, Linux can do it's stuff.

UPDATE: If you don't have an APIC on your system, check this page ADEOS IPIPE Hard Real Time - 8254 Timer Programming


This example is basically the same as Adeos Parallel Port Interrupt - standalone version with the difference that this example modifies the APIC (you can set the periodicity of the timer interrupt with "mytick"), also prints some time information and reading/writing to a /proc file entry is possible for kernel-userspace communication. The original IRQ 0 functionality remains - if "#define APIC_TIMER" is present, APIC is modified, if "#define APIC_TIMER" is removed, the regular Linux tick is used (100Hz - 10ms). UPDATE: The new version below automatically gets the current APIC frequency and restores it when exiting the module. Furthermore you can now set the timer frequency with SAMPLE_FREQUENCY.

For a method on how to read data from kernel space in user space see:
Linux Device Driver Kernel Module FIFO (circular buffer)
Adeos Nano Kernel Hard Real Time Double Buffering FIFO

Compile "parint.c" with "make".
Make a "null-modem" for the parallel port - connect any output pin (pin 2-9) with the IRQ pin (pin 10 = ACK).
Load the kernel module and you'll see an output like this in your kernel log file:
Jul 12 00:31:35 localhost kernel: Adeos: Domain TestDomain registered.
Jul 12 00:31:35 localhost kernel: Domain TestDomain started.
Jul 12 00:31:35 localhost kernel: SAVED APIC FREQ. 6264500
Jul 12 00:31:35 localhost kernel: CURRENT APIC FREQ. 208816600
Jul 12 00:31:35 localhost kernel: USING APIC
Jul 12 00:31:36 localhost kernel: timer=7599926981544
Jul 12 00:31:36 localhost kernel: >>> PARALLEL PORT INT HANDLED: counter=0 latency=6264
Jul 12 00:31:37 localhost kernel: timer=467999262
Jul 12 00:31:37 localhost kernel: >>> PARALLEL PORT INT HANDLED: counter=1 latency=4466
Jul 12 00:31:38 localhost kernel: timer=468000450


Read from the /proc entry with:
# cat /proc/parint/data
24
...and write to it with:
# echo "test" > /proc/parint/data
You'll see this in the kernel log:
Jul 11 00:31:43 localhost kernel: write_data: test

Developed and tested with kernel 2.4.31 (adeos r18c1). Also tested with kernel 2.6.10 (adeos r12).


parint.c

#include <linux/version.h>
#include <linux/module.h>
#include <asm/io.h>
#include <asm/apic.h>
#include <asm/uaccess.h>
#include <linux/proc_fs.h>

#define BASEPORT 0x378
#define PAR_INT 7

#ifdef LINUX_24
// values for kernel 2.4
#define APIC_TIMER_VECTOR 0xe9
#define APIC_TIMER_IPI 201
#else
// values for kernel 2.6
#define APIC_TIMER_VECTOR 0xf5
#define APIC_TIMER_IPI 213
#endif

// stuff for restoring original APIC frequency
long apic_freq;
#define FREQ_APIC       (apic_freq)
#define APIC_ICOUNT	  ((FREQ_APIC + HZ/2)/HZ)

#define SAMPLE_FREQUENCY 3
#define mytick FREQ_APIC / SAMPLE_FREQUENCY

#define APIC_TIMER

static adomain_t this_domain;
#ifndef APIC_TIMER
static adsysinfo_t sys_info;
#endif
struct proc_dir_entry *proc_parint;
struct proc_dir_entry *proc_parint_data;

long long parcounter = 0;
long long timercounter = 0;
int noproc = 0;
unsigned long long timeold, timenew, timediff, timeint, latency;

#define rtai_rdtsc() ({ unsigned long long t; __asm__ __volatile__( "rdtsc" : "=A" (t)); t; })

void handler(unsigned irq) {
	timeint = rtai_rdtsc();
	latency = timeint - timenew;
	printk(">>> PARALLEL PORT INT HANDLED: counter=%lld latency=%lld\n", parcounter, latency);
	parcounter++;
	adeos_control_irq(PAR_INT,0,IPIPE_ENABLE_MASK);
	adeos_propagate_irq(irq);
}

void timer_tick (unsigned irq) {
	timercounter++;
	if (timercounter < 100) {
		outb_p(0, BASEPORT);
		outb_p(255, BASEPORT);
		timenew = rtai_rdtsc();
		timediff = timenew - timeold;
		printk("timer=%lld\n", timediff);
		timeold = timenew;
	}
	adeos_propagate_irq(irq);
}

static inline void save_apic_frequency (void)
{
	// save current APIC freq. first
	apic_freq = apic_read(APIC_TMICT) * HZ;
	printk("SAVED APIC FREQ. %ld\n", apic_freq);
}

static inline void rtai_setup_periodic_apic (unsigned count, unsigned vector)
{
	long tmp;
	apic_read(APIC_LVTT);
	apic_write(APIC_LVTT, APIC_LVT_TIMER_PERIODIC | vector);
	apic_read(APIC_TMICT);
	apic_write(APIC_TMICT, count);

	tmp = apic_read(APIC_TMICT) * HZ;
	printk("CURRENT APIC FREQ. %ld\n", tmp);
}

static int read_data(char *buffer, char **start, off_t offset, int length) {
	int size;
	size = sprintf(buffer, "%lld\n", parcounter);
	*start = buffer + offset;
	size -= offset;
	return (size > length) ? length : (size > 0) ? size : 0;
}

static int write_data(struct file *file, const char *buffer,
                      unsigned long count, void *data) {
	char buf[10];
	int i;
	for (i=0; i<10; i++) buf[i] = 0;
	if (count > sizeof(buf))
		return -EINVAL;
	if (copy_from_user(buf, buffer, count))
		return -EFAULT;
	printk("write_data: %.9s\n", buf);
	return count;
}

void domain_entry (int iflag) {
	printk("Domain %s started.\n",adp_current->name);

	if (iflag) {

#ifdef APIC_TIMER
		adeos_virtualize_irq(APIC_TIMER_IPI,
			&timer_tick, NULL, IPIPE_DYNAMIC_MASK);
		save_apic_frequency();
		rtai_setup_periodic_apic(mytick, APIC_TIMER_VECTOR);
		printk("USING APIC\n");
#else
		// tmirq = timer irq: IRQ 0 on x86
		adeos_get_sysinfo(&sys_info);
		adeos_virtualize_irq(sys_info.archdep.tmirq,
			&timer_tick, NULL, IPIPE_DYNAMIC_MASK);
		printk("USING IRQ 0\n");
#endif

		// parallel port irq
		adeos_virtualize_irq(PAR_INT,&handler,NULL,IPIPE_DYNAMIC_MASK);
		//set port to interrupt mode; pins are output
		outb_p(0x10, BASEPORT + 2); 
		adeos_control_irq(PAR_INT,0,IPIPE_ENABLE_MASK);
	}

	proc_parint = proc_mkdir("parint", 0);
	if (!proc_parint) {
		printk (KERN_ERR "cannot create /proc/parint\n");
		noproc = 1;
	}
	proc_parint_data = create_proc_info_entry("parint/data", 0, 0, read_data);
	if (!proc_parint_data) {
		printk (KERN_ERR "cannot create /proc/parint/data\n");
		remove_proc_entry("parint", 0);
		noproc = 1;
	} else {
		proc_parint_data->write_proc = write_data;
	}
	for (;;)
		// This domain's idle loop
		adeos_suspend_domain(); // control back to ADEOS
}
        
static int __init mod_init (void) {
	adattr_t attr;
	attr.name = "TestDomain";
	attr.domid = 1;             // Adeos Domain ID: >0
	attr.entry = &domain_entry;
	attr.estacksz = 0;  // Adeos chooses a reasonable stack size
	attr.priority = ADEOS_ROOT_PRI + 1;
	attr.dswitch = NULL;    // Domain switch hook - always a C routine
	return adeos_register_domain(&this_domain,&attr);
}

static void __exit mod_exit (void) {
#ifdef APIC_TIMER
	rtai_setup_periodic_apic(APIC_ICOUNT, LOCAL_TIMER_VECTOR);
#endif
	if (noproc == 0) {
		remove_proc_entry("parint/data", 0);
		remove_proc_entry("parint", 0);
	}
	adeos_unregister_domain(&this_domain);
}

module_init(mod_init);
module_exit(mod_exit);
MODULE_LICENSE("GPL");
Makefile for parint.c: (kernel 2.4)
TARGET	:= parint
INCLUDE	:= -I/lib/modules/`uname -r`/build/include
CFLAGS	:= -O2 -Wall -DMODULE -D__KERNEL__ -DLINUX
CC	:= gcc

${TARGET}.o: ${TARGET}.c
	$(CC) $(CFLAGS) ${INCLUDE} -c ${TARGET}.c
Makefile for parint.c: (kernel 2.6)
obj-m	:= parint.o

KDIR	:= /lib/modules/$(shell uname -r)/build
PWD		:= $(shell pwd)

default:
	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
Insert the module with: (kernel 2.4)
# insmod ./parint.o
Insert the module with: (kernel 2.6)
# insmod ./parint.ko
Remove the module with:
# rmmod parint

Last-Modified: Thu, 14 Sep 2006 18:26:53 GMT

Google
 
Web www.captain.at
go to top
© 1996-2010 . All rights reserved.
No reproduction, distribution, publishing or transmission of the copyrighted materials at this site is permitted. Policy
go to top