UPDATE: Click here if you have problems with the interrupt
and check the changed syntax of request_irq in the kernel
Serial Port (RS232) Interrupt Kernel Module Examples - Simple Device Driver:
Two kernel modules (RTAI hard real time and normal soft real time) for capturing the serial port interrupt when modem status lines like
Carrier Detect (CD), Data Set Ready (DSR) and Clear To Send (CTS) change their state. This can
be used i.e. for counting events.
In order to test this module you need a simple cable:
Connect one of the parallel port data output pins (D0-D7; pin2-9) with Clear To Send (CTS) on the first serial port
(pin8 on a SUB-D9; pin5 on a SUB-D25). Ground (GND) is i.e. pin18 on the parallel port and
pin5 on a SUB-D9; pin7 on a SUB-D25 on the serial port.
Connect the cable to the parallel port and ttyS0 (1. serial port), compile the modules,
load one and check the kernel log file for the generated messages.
First one "ctsint.c" measures the parallel port interrupt latency with RTAI (real-time linux).
The second example shows how to handle the irq's without hard real time.
Since a serial interrupt is generated when the parallel port signal goes high *and* goes low,
there are 2x2 serial port messages in the kernel log. Only the first latency is correct, since
the start time for the second latency is not correct. Change this if you want.
This examples are derived from the parallel port kernel modules and therefore
also measure the parallel port latency if one data pin of the par port is connected to the INT/ACK line (pin10) of the parallel port.
For further details on the pin-out and registers of the serial port see: Serial (RS232) Port Pins and Registers
Developed on kernel 2.6.7 / RTAI 3.1 with gcc 3.3.4 and gcc 2.95.4
Example "ctsint.c" (RTAI hard real time)
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <rtai.h>
#include <rtai_sched.h>
#define PARPORT 0x378
#define SERPORT 0x3F8
static int time;
static int time2;
static int timex;
static int timex2;
static int stime;
static int stime2;
static int stimex;
static int stimex2;
static void handler(void)
{
int timediff;
time2 = rt_get_time_ns();
timex2 = rt_get_cpu_time_ns();
timediff = time2 - time;
rt_printk(">>> parallel port interrupt latency = %d ns\n", timediff);
timediff = timex2 - timex;
rt_printk(">>> parallel port interrupt latency = %d ns\n", timediff);
rt_ack_irq(7);
}
static void handler2(void)
{
int tmp;
int timediff;
stime2 = rt_get_time_ns();
stimex2 = rt_get_cpu_time_ns();
timediff = stime2 - stime;
rt_printk(">>> serial port interrupt latency = %d ns\n", timediff);
timediff = stimex2 - stimex;
rt_printk(">>> serial port interrupt latency = %d ns\n", timediff);
tmp = inb_p( SERPORT + 5);
tmp = inb_p( SERPORT + 6);
rt_ack_irq(4);
}
int xinit_module(void)
{
int ret;
ret = rt_request_global_irq(7, (void *)handler);
if (ret) { printk ("##### error requesting irq 7: returned %d\n", ret); }
rt_enable_irq(7);
outb_p(0, SERPORT + 3); // reset DLAB
outb_p(0, SERPORT + 1);
ret = rt_request_global_irq(4, (void *)handler2);
if (ret) { printk ("##### error requesting irq 4: returned %d\n", ret); }
rt_enable_irq(4);
outb_p(0, SERPORT + 3);
outb_p(0xC7, SERPORT + 2);
outb_p(0x0B, SERPORT + 4);
// Bit 3 Enable Modem Status Interrupt
// Bit 2 Enable Receiver Line Status Interrupt
outb_p(0x0C, SERPORT + 1);
outb_p(0x10, PARPORT + 2); //set port to interrupt mode; pins are output
rt_printk("Generating interrupt now on all output pins (intr/ACK = pin 10)\n");
//generate interrupt
outb_p(0, PARPORT);
rt_set_oneshot_mode();
time = rt_get_time_ns();
timex = rt_get_cpu_time_ns();
stime = rt_get_time_ns();
stimex = rt_get_cpu_time_ns();
outb_p(255, PARPORT);
outb_p(0, PARPORT);
rt_printk("Interrupt generated. You should see the latency messages\n");
return 0;
}
void xcleanup_module(void)
{
rt_printk("Unloading serial port test\n");
rt_disable_irq(4);
outb_p(0, SERPORT + 3); // disable DLAB
outb_p(0, SERPORT + 1); // disable serial ints
rt_free_global_irq(4);
rt_disable_irq(7);
rt_free_global_irq(7);
}
module_init(xinit_module);
module_exit(xcleanup_module);
MODULE_LICENSE("GPL");
Makefile for ctsint.c: (kernel 2.4)
TARGET := ctsint
INCLUDE := -I/lib/modules/`uname -r`/build/include -I/usr/realtime/include
CFLAGS := -O2 -Wall -DMODULE -DUSEFIFO -D__KERNEL__ -DLINUX
CC := gcc
${TARGET}.o: ${TARGET}.c
$(CC) $(CFLAGS) ${INCLUDE} -c ${TARGET}.c
Makefile for ctsint.c: (kernel 2.6)
obj-m := ctsint.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
EXTRA_CFLAGS := -I/usr/realtime/include -I/usr/include/
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
Insert the modules with: (kernel 2.4)
#!/bin/sh
TMP=$PWD
cd /usr/realtime/modules/
check=`lsmod | grep -c "^rtai_hal"`
if [ $check == 0 ]
then
insmod ./rtai_hal.o
fi
check=`lsmod | grep -c "^rtai_ksched"`
if [ $check == 0 ]
then
insmod ./rtai_ksched.o
fi
cd $TMP
check=`lsmod | grep -c "^ctsint"`
if [ $check == 0 ]
then
insmod ./ctsint.o
else
rmmod ctsint
insmod ./ctsint.o
fi
Insert the modules with: (kernel 2.6)
#!/bin/sh
TMP=$PWD
cd /usr/realtime/modules/
check=`lsmod | grep -c "^rtai_hal"`
if [ $check == 0 ]
then
insmod ./rtai_hal.ko
fi
check=`lsmod | grep -c "^rtai_up"`
if [ $check == 0 ]
then
insmod ./rtai_ksched.ko
fi
cd $TMP
check=`lsmod | grep -c "^ctsint"`
if [ $check == 0 ]
then
insmod ./ctsint.ko
else
rmmod ctsint
insmod ./ctsint.ko
fi
Remove the modules with:
#!/bin/sh
rmmod ctsint
rmmod rtai_ksched
#rmmod rtai_up
rmmod rtai_hal
Example "ctsint.c" (soft real-time):
#include <linux/module.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#define PARPORT 0x378
#define SERPORT 0x3F8
static int handler(void)
{
// do stuff
int tmp;
printk(">>> SERIAL PORT CTS INT HANDLED\n");
// read Line Status Register & Modem Status Register to ACK IRQ
tmp = inb_p( SERPORT + 5);
tmp = inb_p( SERPORT + 6);
return IRQ_HANDLED;
}
static int handler2(void)
{
// do stuff
printk(">>> PARALLEL PORT INT HANDLED\n");
return IRQ_HANDLED;
}
int xinit_module(void)
{
int ret;
int ret2;
ret = request_irq(7, handler2, SA_INTERRUPT, "parallelport", handler2);
if (ret) { printk ("##### error requesting irq 7: returned %d\n", ret); }
enable_irq(7);
outb_p(0, SERPORT + 3); // reset DLAB
outb_p(0, SERPORT + 1);
ret = request_irq(4, handler, SA_INTERRUPT, "serialport", handler);
if (ret) { printk ("##### error requesting irq 4: returned %d\n", ret); }
enable_irq(4);
outb_p(0, SERPORT + 3);
outb_p(0xC7, SERPORT + 2);
outb_p(0x0B, SERPORT + 4);
// Bit 3 Enable Modem Status Interrupt
// Bit 2 Enable Receiver Line Status Interrupt
outb_p(0x0C, SERPORT + 1);
//set port to interrupt mode; pins are output
outb_p(0x10, PARPORT + 2);
printk("Generating interrupt now on all PARALLEL PORT output pins\n");
printk(" Serial Port: CTS = pin 8 on SUB-D9\n");
printk(" Parallel Port: intr/ACK = pin 10\n");
//generate interrupt
outb_p(0, PARPORT);
outb_p(255, PARPORT);
outb_p(0, PARPORT);
printk("Interrupt generated. You should see the handler-messages\n");
return 0;
}
void xcleanup_module(void)
{
disable_irq(4);
outb_p(0, SERPORT + 3); // disable DLAB
outb_p(0, SERPORT + 1); // disable serial ints
free_irq(4, handler);
disable_irq(7);
free_irq(7, handler2);
printk("MODULE REMOVED!\n");
}
module_init(xinit_module);
module_exit(xcleanup_module);
MODULE_LICENSE("GPL");
Makefile for ctsint.c: (kernel 2.4)
TARGET := ctsint
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 ctsint.c: (kernel 2.6)
obj-m := ctsint.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 ./ctsint.o
Insert the module with: (kernel 2.6)
# insmod ./ctsint.ko
Remove the module with:
# rmmod ctsint
Last-Modified: Tue, 17 Jun 2008 18:30:08 GMT