Missing parallel port interrupts

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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); 




_______________________________________________
Kernelnewbies mailing list
Kernelnewbies@xxxxxxxxxxxxxxxxx
http://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies

[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux