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