Thread (5 messages) 5 messages, 3 authors, 2010-01-26

Re: Problem with interrupt handler

From: Uwe Kleine-König <hidden>
Date: 2010-01-26 11:00:50

Hello Julian,

On Mon, Jan 25, 2010 at 11:41:46PM +0100, Julian Fuchs wrote:
thank you very much for your answer.

Am 25. Januar 2010 20:31 schrieb Uwe Kleine-König
[off-list ref]:
quoted
On Mon, Jan 25, 2010 at 06:16:08PM +0100, Julian Fuchs wrote:
(...)
quoted
Did you try with IRQF_NODELAY on vanilla and without it in rt?
I tried it with IRQF_NODELAY in rt and without IRQF_NODELAY on vanilla
(vanilla won't compile with IRQF_NODELAY AFAIR).
quoted
Are you sure the irq fires in rt?
It fires in the normal kernel (i.e. the hardware is working). How can
I determine whether the interrupt fires in rt?
quoted
 Does your interrupt appear in /proc/interrupts?
Yes, it appears there (and the name of the module is listed correctly
in the same line).

On vanilla, the counter in /proc/interrupts increases, on rt, it
doesn't. It seems that the interrupt just doesn't fire... is there any
kind of "magic" I can do on rt to "enable" the interrupt? It seems
disabled :-(
quoted
What platform are you working on?
I'm working on a Intel Celeron 1,8 GHz board (x86) with Ubuntu Linux
9.10 as a distro.
quoted
 Do you can send the code of a minimal example?
Sure. Below you find the code of a minimal example for a rs232
interface. If you have any suggestions for a smaller example, please
tell me and will create it (I just don't know how to create the
interrupt otherwise).

Thanks a lot for your help,

Julian

-----
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <linux/proc_fs.h>
#include <linux/inet.h>
#include <linux/net.h>
#include <net/tcp.h>
#include <net/ip.h>
#include <net/protocol.h>


/* --- DEFITIONS AND SETTINGS --- */

// Module information
MODULE_AUTHOR("Somebody");
MODULE_DESCRIPTION("some serial interface driver");
MODULE_LICENSE("GPL");
#define MODULE_IDENT "foobar"
#define VERSION_STRING "foobar v0.1\n"

// Configurable parameters
int base_port = 0xcf00;
const int port_range = 8;
int irq = 16;

int irq_counter = 0;

module_param(base_port, int, 0644);
MODULE_PARM_DESC(base_port, "The base port address of the serial interface");
module_param(irq, int, 0644);
MODULE_PARM_DESC(irq, "IRQ of the serial interface");


// Serial port communication
// registers
#define IER 1 // interrupt enable register
#define FCR 2 // FIFO control register (write) / Interrupt
Identification Register (read)
#define LCR 3 // line control register

// serial port settings
#define PARITY_NO 0x0
#define STOPBIT_1 0
#define CS_8 0x3
#define MAX_BAUDRATE 921600

#define DIVISOR_ACCESS 0x80

// interrupt identification and enabling
#define IER_DATA_AVAILABLE 0x1
#define IER_LS_CHANGE 0x4

// Global control variables
int stage = 0;

/* --- SERIAL PORT CONFIGURATION INTERFACE --- */

void set_serial_options(void);

void set_serial_options(void) {
	unsigned int divisor;
	unsigned char parity, stopbit, cs, status;

	stopbit = STOPBIT_1;
	parity = PARITY_NO;
	cs = CS_8;

	status = parity | stopbit | cs | DIVISOR_ACCESS;
	outb(status, base_port+LCR);
	divisor = 24;
	outb(divisor & 0x00ff, base_port);
	outb(divisor & 0xff00, base_port+1);

	// reset divisor access bit
	outb(status &~ DIVISOR_ACCESS, base_port+LCR);
}


irqreturn_t interrupt_handler(int myirq, void *dev_id) {
	
	unsigned char iir_byte, bitmask;
	iir_byte = inb(base_port + FCR);
	bitmask = 1;
	if ( (iir_byte & bitmask) == 1) {
		// interrupt is not for this module
		return IRQ_NONE;
	}
	// interrupt is for this module
	unsigned char data_byte;
	data_byte = inb(base_port);
	
	irq_counter++;
	
	return IRQ_HANDLED;
}


void cleanup_module(void) {
	// I/O ports reserved
	if (stage >= 2) {
		release_region(base_port, port_range);
		printk(KERN_INFO "%s: releasing I/O ports\n", MODULE_IDENT);
	}
	// IRQ reserved
	if (stage >= 1) {
		// because of shared irq, &stage is given, otherwise NULL will do, too
		free_irq(irq, &stage);
		// disable interrupts from the card
		outb(0, base_port + IER);

		printk(KERN_INFO "%s: freeing irq %i\n", MODULE_IDENT, irq);
	}
	printk(KERN_INFO "%s: module unloaded\n", MODULE_IDENT);
	
	printk(KERN_ERR "%d irq_counter\n", irq_counter);
}

/* Setup all operations - called by kernel when module is loaded */
int init_module(void) {

	int err = 0;

	printk(KERN_INFO VERSION_STRING);

	// Stage 1. Request IRQ using shared interrupts
	// dev_id can't be NULL since the kernel needs to label the different ISRs
	// stage is just as a pointer to our address space, any other address
will do, too.
	if ((err = request_irq(irq, interrupt_handler, IRQF_SHARED |
IRQF_DISABLED | IRQF_NODELAY, MODULE_IDENT, &stage)) < 0) return err;
Do you really need to share the irq, can you try without sharing?
	stage++;

	// Stage 2. Request access to I/O ports
	if ((err = check_region(base_port, port_range)) < 0) {
		cleanup_module();
		return err;
	}
	request_region(base_port, port_range, MODULE_IDENT);
don't use check_region, but the return value of request_region to check
for errors.  You're approach is racy.
	stage++;

	printk(KERN_INFO "Using serial port at %x, IRQ %i\n", base_port, irq);

	// Configure serial port
	set_serial_options();

	// disable FIFO
	outb(0, base_port+FCR);

	// enable interrupts if data available or break signal received
	outb(IER_DATA_AVAILABLE | IER_LS_CHANGE, base_port+IER);
	
	printk(KERN_ERR "INIT MODULE DONE!\n");

	return 0;
}
I assume you don't have another driver loaded that might look after your
device?  Does it autoload, so you have to unload it before you start
with your module?  Do you need your module, the existing drivers don't
provide what you need?

Other than that I don't have an idea.

Best regards
Uwe

-- 
Pengutronix e.K.                              | Uwe Kleine-König            |
Industrial Linux Solutions                    | http://www.pengutronix.de/  |
--
To unsubscribe from this list: send the line "unsubscribe linux-rt-users" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help