Missing parallel port interrupts
Eric Fowler
eric.fowler at gmail.com
Tue Jan 7 19:02:02 EST 2014
I am writing a simple driver that hooks the parallel port irq and when
written to, enables the port for generating interrupts (the pins 9&10 are
wired together) and then toggles the high bit once for every byte in the
written input. This means that if I do:
>echo "0123456789" >> /dev/foobar
I expect to toggle that bit 10 times, and generate 10 interrupts. My ISR is
a simple counter that bumps a number every time it is called.
I am seeing that I am missing about 85% or more of my interrupts. Sometimes
I get 2/10 but more often only 1/10. I have tried udelay()-ing between the
port toggles but no go, and of course that is not really a solution. I am
not trying to protect the counter but nothing else is accessing it.
I would expect to see *some* losses here, but not that many. What is going
on?
Code follows.
Thanks loads
Eric
///////////////////////////////-----------cut here------------------------
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>
MODULE_LICENSE("GPL");
#define NUM_DEVICES (1)
#define DEVNAME "foobar"
#define FOOBAR_MAJOR (248)
#define FOOBAR_MINOR (0)
int foobar_init(void);
void foobar_exit(void);
int foobar_release(struct inode *, struct file*);
ssize_t foobar_read(struct file *, char __user *, size_t, loff_t *);
ssize_t foobar_write(struct file *, const char __user *, size_t, loff_t *);
int foobar_open(struct inode * inode, struct file * file);
void set_port_and_irq(void);
static irqreturn_t irqhandler(int irq, void * data);
int foobar_set_irq(void);
int port_address = -1;
module_param(port_address, int, 0);
unsigned int irq = 0;
module_param(irq, uint, 0);
int irqavail = -1;
size_t chars_read = 0;
static struct file_operations foobar_i_fops =
{
.owner = THIS_MODULE,
.read = foobar_read,
.write = foobar_write,
.open = foobar_open,
.release = foobar_release,
};
struct resource * region = NULL;
static unsigned long mask;
static unsigned long probe;
static dev_t g_dev;
unsigned dev_id = (unsigned)&foobar_i_fops;
int foobar_init(void)
{
int result = 0;
result = register_chrdev(0, DEVNAME, &foobar_i_fops);
g_dev = MKDEV(result, FOOBAR_MINOR);
if(result < 0)
{
printk(KERN_WARNING "foobar: can't get device number, returning %d",
result);
return result;
}
printk(KERN_WARNING "foobar: Module %s got device number %d, minor is %d,
result is %d\n", THIS_MODULE->name, MAJOR(g_dev), MINOR(g_dev), result);
g_dev = MKDEV(MAJOR(g_dev), FOOBAR_MINOR + NUM_DEVICES);
set_port_and_irq();
region = request_region(port_address, 1, DEVNAME);
printk(KERN_WARNING "Port address is 0x%x, IRQ is %d, region is 0x%p\n",
port_address, irq, region);
if(region == 0)
{
printk(KERN_WARNING "Can't get region");
return -ENODEV;
}
mask = probe_irq_on();
return 0;
}
void foobar_exit(void)
{
int major = MAJOR(g_dev);
release_region(port_address, 1);
printk(KERN_WARNING "unregister_chrdev(%d) called for %s, dev # is %d\n",
major, DEVNAME, g_dev);
unregister_chrdev(major, DEVNAME);
}
int foobar_open(struct inode * inode, struct file * file)
{
irqavail = request_irq(irq, irqhandler, IRQF_SHARED, DEVNAME,
(void*)dev_id);
printk(KERN_WARNING "IRQ:%d\tPort Address:0x%x\tAvailable:%s\n", irq,
port_address, irqavail == 0 ? "true" : "false");
return irqavail;
}
int foobar_release(struct inode * inode, struct file * filp)
{
printk(KERN_WARNING "foobar_release() is called, fil is 0x%p, inode is
0x%p\n", filp, inode);
if(irqavail == 0)
{
printk(KERN_WARNING "Freeing irq %d, dev_id is 0x%x\n", irq, dev_id);
free_irq(irq, (void*)dev_id);
}
else
printk(KERN_WARNING "NOT freeing irq %d, dev_id is 0x%x\n", irq,
dev_id);
probe = probe_irq_off(mask);
return 0;
}
ssize_t foobar_read(struct file * filp, char __user * buf, size_t count,
loff_t * f_pos)
{
if(chars_read == 0)
{
printk(KERN_WARNING "Read is called, nothing to return.");
return 0;
}
sprintf(buf, "Chars>%d\n", chars_read);
chars_read = 0;
printk(KERN_WARNING "Read is called, returning %s, length is %d\n", buf,
strlen(buf));
return count;
}
ssize_t foobar_write(struct file * filp, const char __user * buf, size_t
count, loff_t *f_pos)
{
size_t idx;
unsigned long address = (unsigned long)port_address;
printk(KERN_WARNING "Write is called, count is %d, address is 0x%lx,
buffer is %s\n", count, address, buf);
outb_p(0x10, address + 2);
for(idx = 0; idx < strlen(buf); ++idx)
{
outb_p(0x00, address);
udelay(100);
outb_p(0xff, address);
udelay(100);
}
outb_p(0x00, address + 2);
udelay(5);
printk(KERN_WARNING "probe_irq_off(0x%lx) returned 0x%lx", mask, probe);
if(f_pos)
* f_pos = 0;
return count;
}
static irqreturn_t irqhandler(int irq, void * data)
{
chars_read++;
printk(KERN_WARNING "Interrupt is fired, IRQ is %d, data is 0x%p, chars
%d\n", irq, data, chars_read);
return IRQ_HANDLED;
}
void set_port_and_irq(void)
{
if(irq < 0)
switch(port_address)
{
case 0x378:
irq = 7;
break;
case 0x278:
irq = 2;
break;
}
switch(irq)
{
case 2:
port_address = 0x278;
break;
case 7:
port_address = 0x378;
break;
}
}
module_init(foobar_init);
module_exit(foobar_exit);
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.kernelnewbies.org/pipermail/kernelnewbies/attachments/20140107/1f34924e/attachment-0001.html
More information about the Kernelnewbies
mailing list