/* * A virtual device driver * by matsu@igel.co.jp */ #include #include #include #include #include #include #include MODULE_LICENSE("Dual BSD/GPL"); #define IRQHOOK_DIRNAME "irqhook" static struct proc_dir_entry *root_irq_dir; struct irq_proc { unsigned long irq; atomic_t open; wait_queue_head_t q; atomic_t count; char devname[TASK_COMM_LEN]; } ip[NR_IRQS]; static struct timeval tm; static ssize_t irqhook_proc_write(struct file *filp, const char __user *bufp, size_t len, loff_t *ppos) { struct irq_proc *ipp = (struct irq_proc *)filp->private_data; do_gettimeofday(&tm); atomic_inc(&ipp->count); wake_up(&ipp->q); return len; } static ssize_t irqhook_proc_read(struct file *filp, char __user *bufp, size_t len, loff_t *ppos) { struct irq_proc *ipp = (struct irq_proc *)filp->private_data; int pending; DEFINE_WAIT(wait); if (len < sizeof(int)) return -EINVAL; pending = atomic_read(&ipp->count); if (pending == 0) { if (filp->f_flags & O_NONBLOCK) return -EWOULDBLOCK; } while (pending == 0) { prepare_to_wait(&ipp->q, &wait, TASK_INTERRUPTIBLE); pending = atomic_read(&ipp->count); if (pending == 0) schedule(); finish_wait(&ipp->q, &wait); if (signal_pending(current)) return -ERESTARTSYS; } if (copy_to_user(bufp, &tm, sizeof(struct timeval))) return -EFAULT; *ppos += sizeof(struct timeval); atomic_sub(pending, &ipp->count); return sizeof(struct timeval); } static unsigned int irqhook_proc_poll(struct file *filp, struct poll_table_struct *wait) { struct irq_proc *ipp = (struct irq_proc *)filp->private_data; if (atomic_read(&ipp->count) > 0) return POLLIN | POLLRDNORM; /* readable */ poll_wait(filp, &ipp->q, wait); if (atomic_read(&ipp->count) > 0) return POLLIN | POLLRDNORM; /* readable */ return 0; } static int irqhook_proc_release(struct inode *inop, struct file *filp) { struct irq_proc *ipp = (struct irq_proc *)filp->private_data; (void)inop; if (!(filp->f_flags & O_WRONLY)) atomic_set(&ipp->open, 1); return 0; } static int irqhook_proc_open(struct inode *inop, struct file *filp) { struct irq_proc *ipp; struct proc_dir_entry *ent = PDE(inop); int error = 0; ipp = &ip[(unsigned long)ent->data]; printk(KERN_ALERT "irqhook_proc_open() ipp->open = %d\n", atomic_read(&ipp->open)); if (atomic_dec_and_test(&ipp->open)) { strcpy(ipp->devname, current->comm); } else { atomic_inc(&ipp->open); if (!(filp->f_flags & O_WRONLY)) error = -EBUSY; } if (!error) filp->private_data = (void *)ipp; return error; } static struct file_operations irqhook_proc_file_operations = { .open = irqhook_proc_open, .read = irqhook_proc_read, .write = irqhook_proc_write, /* for test */ .release = irqhook_proc_release, .poll = irqhook_proc_poll, }; #define MAX_NAMELEN 4 static void init_irqhook_proc(void) { struct proc_dir_entry *entry; char name [MAX_NAMELEN]; int irq; /* create /proc/irqhook */ if (!root_irq_dir) { if (!(root_irq_dir = proc_mkdir(IRQHOOK_DIRNAME, NULL))) return; } for (irq = 0; irq< NR_IRQS; irq++) { /* initialize irq_proc */ init_waitqueue_head(&(ip[irq].q)); atomic_set(&(ip[irq].count), 0); atomic_set(&(ip[irq].open), 1); ip[irq].irq = irq; memset(name, 0, MAX_NAMELEN); sprintf(name, "%d", irq); /* * Create handles for user-mode interrupt handlers * if the kernel hasn't already grabbed the IRQ */ entry = create_proc_entry(name, 0600, root_irq_dir); if (entry) { entry->data = (void *)(unsigned long)irq; entry->read_proc = NULL; entry->write_proc = NULL; entry->proc_fops = &irqhook_proc_file_operations; } } } #undef MAX_NAMELEN static int irqhook_init(void) { printk(KERN_ALERT "Intterrupt signaler " "for user-space device drivers\n"); init_irqhook_proc(); return 0; } #define MAX_NAMELEN 4 static void irqhook_exit(void) { char name [MAX_NAMELEN]; int i; for (i=0; i