/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * fpga_int.c * * AUTHOR: Mark McDermott * CREATED: June 12, 2008 * COPYRIGHT: 2008 The Learning Labs, Inc. * * DESCRIPTION: This kernel module registers interrupts from FPGA * through PF16 on the iMX21. * DEPENDENCIES: none * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include /* outb */ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define MODULE_VERSION "1.0" #define MODULE_NAME "fpga_int" #define INTERRUPT 240 /* PF16 on iMX21 */ #define FPGA_MAJOR 245 #define MODULE_NAME "fpga_int" #undef DEBUG /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ int interruptcount = 0; static struct proc_dir_entry *interrupt_arm_file; static struct fasync_struct *fasync_fpga_queue ; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * function: interrupt_interrupt_arm * * This function is the interrupt handler for interrupt 7. It sets the tv2 * structure using do_gettimeofday. It then deasserts D7. */ void interrupt_interrupt_arm(int irq, void *dev_id, struct pt_regs *regs) { interruptcount++; printk(KERN_INFO "fpga_int: Interrupt detected in kernel \n"); // DEBUG /* Signal the user application that an interupt occured */ kill_fasync(&fasync_fpga_queue, SIGIO, POLL_IN); } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * function: proc_read_interrupt_arm * * The kernel executes this function when a read operation occurs on * /proc/interrupt_arm. */ static int proc_read_interrupt_arm(char *page, char **start, off_t off, int count, int *eof, void *data) { int len; len = sprintf(page, "Total number of interrupts %19i\n", interruptcount); return len; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * function: fpga_open * * This function is called when the fpga_int device is opened * */ static int fpga_open (struct inode *inode, struct file *file) { printk(KERN_INFO "fpga_int: Inside fpga_open \n"); // DEBUG return 0; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * function: fpga_release * * This function is called when the fpga_int device is * released * */ static int fpga_release (struct inode *inode, struct file *file) { printk(KERN_INFO "\nfpga_int: Inside fpga_release \n"); // DEBUG return 0; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * function: fpga_fasync * * This is invoked by the kernel when the user program opens this * input device and issues fcntl(F_SETFL) on the associated file * descriptor. fasync_helper() ensures that if the driver issues a * kill_fasync(), a SIGIO is dispatched to the owning application. */ static int fpga_fasync (int fd, struct file *filp, int on) { printk(KERN_INFO "\nfpga_int: Inside fpga_fasync \n"); // DEBUG return fasync_helper(fd, filp, on, &fasync_fpga_queue); } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Define which file operations are supported * */ struct file_operations fpga_fops = { .owner = THIS_MODULE, .llseek = NULL, .read = NULL, .write = NULL, .readdir= NULL, .poll = NULL, .ioctl = NULL, .mmap = NULL, .open = fpga_open, .flush = NULL, .release= fpga_release, .fsync = NULL, .fasync = fpga_fasync, .lock = NULL, .readv = NULL, .writev = NULL, }; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * function: init_interrupt_arm * * This function creates the /proc directory entry interrupt_arm. It * also configures the parallel port then requests interrupt 240 from Linux. */ static int __init init_interrupt_arm(void) { int rv = 0; printk("i.MX21 FPGA Interrupt Module\n"); printk("i.MX21 FPGA Driver Loading.\n"); printk("Using Major Number %d on %s\n", FPGA_MAJOR, MODULE_NAME); if (register_chrdev(FPGA_MAJOR, MODULE_NAME, &fpga_fops)) { printk("fpga_int: unable to get major %d. ABORTING!\n", FPGA_MAJOR); return -EBUSY; } interrupt_arm_file = create_proc_entry("interrupt_arm", 0444, NULL); if(interrupt_arm_file == NULL) { printk("fpga_int: create /proc entry returned NULL. ABORTING!\n"); return -ENOMEM; } interrupt_arm_file->data = NULL; interrupt_arm_file->read_proc = &proc_read_interrupt_arm; interrupt_arm_file->write_proc = NULL; interrupt_arm_file->owner = THIS_MODULE; /* request interrupt from linux */ rv = request_irq(INTERRUPT, interrupt_interrupt_arm, SA_TRIGGER_RISING, "interrupt_arm",NULL); if ( rv ) { printk("Can't get interrupt %d\n", INTERRUPT); goto no_interrupt_arm; } /* everything initialized */ printk(KERN_INFO "%s %s Initialized\n",MODULE_NAME, MODULE_VERSION); return 0; /* remove the proc entry on error */ no_interrupt_arm: remove_proc_entry("interrupt_arm", NULL); return -EBUSY; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * function: cleanup_interrupt_arm * * This function frees interrupt 240 then removes the /proc directory entry * interrupt_arm. */ static void __exit cleanup_interrupt_arm(void) { /* free the interrupt */ free_irq(INTERRUPT,NULL); unregister_chrdev(FPGA_MAJOR, MODULE_NAME); remove_proc_entry("interrupt_arm", NULL); printk(KERN_INFO "%s %s removed\n", MODULE_NAME, MODULE_VERSION); } module_init(init_interrupt_arm); module_exit(cleanup_interrupt_arm); MODULE_AUTHOR("Mark McDermott"); MODULE_DESCRIPTION("interrupt_arm proc module"); MODULE_LICENSE("GPL");