| /* |
| comedi/rt.c |
| comedi kernel module |
| |
| COMEDI - Linux Control and Measurement Device Interface |
| Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org> |
| |
| 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., 675 Mass Ave, Cambridge, MA 02139, USA. |
| |
| */ |
| |
| #undef DEBUG |
| |
| #define __NO_VERSION__ |
| #include <linux/comedidev.h> |
| |
| #include <linux/errno.h> |
| #include <linux/kernel.h> |
| #include <linux/sched.h> |
| #include <linux/fcntl.h> |
| #include <linux/delay.h> |
| #include <linux/ioport.h> |
| #include <linux/mm.h> |
| #include <linux/slab.h> |
| #include <asm/io.h> |
| |
| #include "rt_pend_tq.h" |
| |
| #ifdef CONFIG_COMEDI_RTAI |
| #include <rtai.h> |
| #endif |
| |
| #ifdef CONFIG_COMEDI_FUSION |
| #include <nucleus/asm/hal.h> |
| #endif |
| |
| #ifdef CONFIG_COMEDI_RTL |
| #include <rtl_core.h> |
| #include <rtl_sync.h> |
| #endif |
| |
| struct comedi_irq_struct { |
| int rt; |
| int irq; |
| irqreturn_t(*handler) (int irq, void *dev_id PT_REGS_ARG); |
| unsigned long flags; |
| const char *device; |
| comedi_device *dev_id; |
| }; |
| |
| static int comedi_rt_get_irq(struct comedi_irq_struct *it); |
| static int comedi_rt_release_irq(struct comedi_irq_struct *it); |
| |
| static struct comedi_irq_struct *comedi_irqs[NR_IRQS]; |
| |
| int comedi_request_irq(unsigned irq, irqreturn_t(*handler) (int, |
| void *PT_REGS_ARG), unsigned long flags, const char *device, |
| comedi_device * dev_id) |
| { |
| struct comedi_irq_struct *it; |
| int ret; |
| /* null shared interrupt flag, since rt interrupt handlers do not |
| * support it, and this version of comedi_request_irq() is only |
| * called for kernels with rt support */ |
| unsigned long unshared_flags = flags & ~IRQF_SHARED; |
| |
| ret = request_irq(irq, handler, unshared_flags, device, dev_id); |
| if (ret < 0) { |
| // we failed, so fall back on allowing shared interrupt (which we won't ever make RT) |
| if (flags & IRQF_SHARED) { |
| rt_printk |
| ("comedi: cannot get unshared interrupt, will not use RT interrupts.\n"); |
| ret = request_irq(irq, handler, flags, device, dev_id); |
| } |
| if (ret < 0) { |
| return ret; |
| } |
| } else { |
| it = kzalloc(sizeof(struct comedi_irq_struct), GFP_KERNEL); |
| if (!it) |
| return -ENOMEM; |
| |
| it->handler = handler; |
| it->irq = irq; |
| it->dev_id = dev_id; |
| it->device = device; |
| it->flags = unshared_flags; |
| comedi_irqs[irq] = it; |
| } |
| return 0; |
| } |
| |
| void comedi_free_irq(unsigned int irq, comedi_device * dev_id) |
| { |
| struct comedi_irq_struct *it; |
| |
| free_irq(irq, dev_id); |
| |
| it = comedi_irqs[irq]; |
| if (it == NULL) |
| return; |
| |
| if (it->rt) { |
| printk("real-time IRQ allocated at board removal (ignore)\n"); |
| comedi_rt_release_irq(it); |
| } |
| |
| kfree(it); |
| comedi_irqs[irq] = NULL; |
| } |
| |
| int comedi_switch_to_rt(comedi_device * dev) |
| { |
| struct comedi_irq_struct *it; |
| unsigned long flags; |
| |
| it = comedi_irqs[dev->irq]; |
| /* drivers might not be using an interrupt for commands, |
| or we might not have been able to get an unshared irq */ |
| if (it == NULL) |
| return -1; |
| |
| comedi_spin_lock_irqsave(&dev->spinlock, flags); |
| |
| if (!dev->rt) |
| comedi_rt_get_irq(it); |
| |
| dev->rt++; |
| it->rt = 1; |
| |
| comedi_spin_unlock_irqrestore(&dev->spinlock, flags); |
| |
| return 0; |
| } |
| |
| void comedi_switch_to_non_rt(comedi_device * dev) |
| { |
| struct comedi_irq_struct *it; |
| unsigned long flags; |
| |
| it = comedi_irqs[dev->irq]; |
| if (it == NULL) |
| return; |
| |
| comedi_spin_lock_irqsave(&dev->spinlock, flags); |
| |
| dev->rt--; |
| if (!dev->rt) |
| comedi_rt_release_irq(it); |
| |
| it->rt = 0; |
| |
| comedi_spin_unlock_irqrestore(&dev->spinlock, flags); |
| } |
| |
| void wake_up_int_handler(int arg1, void *arg2) |
| { |
| wake_up_interruptible((wait_queue_head_t *) arg2); |
| } |
| |
| void comedi_rt_pend_wakeup(wait_queue_head_t * q) |
| { |
| rt_pend_call(wake_up_int_handler, 0, q); |
| } |
| |
| /* RTAI section */ |
| #ifdef CONFIG_COMEDI_RTAI |
| |
| #ifndef HAVE_RT_REQUEST_IRQ_WITH_ARG |
| #define DECLARE_VOID_IRQ(irq) \ |
| static void handle_void_irq_ ## irq (void){ handle_void_irq(irq);} |
| |
| static void handle_void_irq(int irq) |
| { |
| struct comedi_irq_struct *it; |
| |
| it = comedi_irqs[irq]; |
| if (it == NULL) { |
| rt_printk("comedi: null irq struct?\n"); |
| return; |
| } |
| it->handler(irq, it->dev_id PT_REGS_NULL); |
| rt_enable_irq(irq); //needed by rtai-adeos, seems like it shouldn't hurt earlier versions |
| } |
| |
| DECLARE_VOID_IRQ(0); |
| DECLARE_VOID_IRQ(1); |
| DECLARE_VOID_IRQ(2); |
| DECLARE_VOID_IRQ(3); |
| DECLARE_VOID_IRQ(4); |
| DECLARE_VOID_IRQ(5); |
| DECLARE_VOID_IRQ(6); |
| DECLARE_VOID_IRQ(7); |
| DECLARE_VOID_IRQ(8); |
| DECLARE_VOID_IRQ(9); |
| DECLARE_VOID_IRQ(10); |
| DECLARE_VOID_IRQ(11); |
| DECLARE_VOID_IRQ(12); |
| DECLARE_VOID_IRQ(13); |
| DECLARE_VOID_IRQ(14); |
| DECLARE_VOID_IRQ(15); |
| DECLARE_VOID_IRQ(16); |
| DECLARE_VOID_IRQ(17); |
| DECLARE_VOID_IRQ(18); |
| DECLARE_VOID_IRQ(19); |
| DECLARE_VOID_IRQ(20); |
| DECLARE_VOID_IRQ(21); |
| DECLARE_VOID_IRQ(22); |
| DECLARE_VOID_IRQ(23); |
| |
| typedef void (*V_FP_V) (void); |
| static V_FP_V handle_void_irq_ptrs[] = { |
| handle_void_irq_0, |
| handle_void_irq_1, |
| handle_void_irq_2, |
| handle_void_irq_3, |
| handle_void_irq_4, |
| handle_void_irq_5, |
| handle_void_irq_6, |
| handle_void_irq_7, |
| handle_void_irq_8, |
| handle_void_irq_9, |
| handle_void_irq_10, |
| handle_void_irq_11, |
| handle_void_irq_12, |
| handle_void_irq_13, |
| handle_void_irq_14, |
| handle_void_irq_15, |
| handle_void_irq_16, |
| handle_void_irq_17, |
| handle_void_irq_18, |
| handle_void_irq_19, |
| handle_void_irq_20, |
| handle_void_irq_21, |
| handle_void_irq_22, |
| handle_void_irq_23, |
| }; |
| |
| static int comedi_rt_get_irq(struct comedi_irq_struct *it) |
| { |
| rt_request_global_irq(it->irq, handle_void_irq_ptrs[it->irq]); |
| rt_startup_irq(it->irq); |
| |
| return 0; |
| } |
| |
| static int comedi_rt_release_irq(struct comedi_irq_struct *it) |
| { |
| rt_shutdown_irq(it->irq); |
| rt_free_global_irq(it->irq); |
| return 0; |
| } |
| #else |
| |
| static int comedi_rt_get_irq(struct comedi_irq_struct *it) |
| { |
| int ret; |
| |
| ret = rt_request_global_irq_arg(it->irq, it->handler, it->flags, |
| it->device, it->dev_id); |
| if (ret < 0) { |
| rt_printk("rt_request_global_irq_arg() returned %d\n", ret); |
| return ret; |
| } |
| rt_startup_irq(it->irq); |
| |
| return 0; |
| } |
| |
| static int comedi_rt_release_irq(struct comedi_irq_struct *it) |
| { |
| rt_shutdown_irq(it->irq); |
| rt_free_global_irq(it->irq); |
| return 0; |
| } |
| #endif |
| |
| void comedi_rt_init(void) |
| { |
| rt_mount_rtai(); |
| rt_pend_tq_init(); |
| } |
| |
| void comedi_rt_cleanup(void) |
| { |
| rt_umount_rtai(); |
| rt_pend_tq_cleanup(); |
| } |
| |
| #endif |
| |
| /* Fusion section */ |
| #ifdef CONFIG_COMEDI_FUSION |
| |
| static void fusion_handle_irq(unsigned int irq, void *cookie) |
| { |
| struct comedi_irq_struct *it = cookie; |
| |
| it->handler(irq, it->dev_id PT_REGS_NULL); |
| rthal_irq_enable(irq); |
| } |
| |
| static int comedi_rt_get_irq(struct comedi_irq_struct *it) |
| { |
| rthal_irq_request(it->irq, fusion_handle_irq, it); |
| rthal_irq_enable(it->irq); |
| return 0; |
| } |
| |
| static int comedi_rt_release_irq(struct comedi_irq_struct *it) |
| { |
| rthal_irq_disable(it->irq); |
| rthal_irq_release(it->irq); |
| return 0; |
| } |
| |
| void comedi_rt_init(void) |
| { |
| rt_pend_tq_init(); |
| } |
| |
| void comedi_rt_cleanup(void) |
| { |
| rt_pend_tq_cleanup(); |
| } |
| |
| #endif /*CONFIG_COMEDI_FUSION */ |
| |
| /* RTLinux section */ |
| #ifdef CONFIG_COMEDI_RTL |
| |
| static unsigned int handle_rtl_irq(unsigned int irq PT_REGS_ARG) |
| { |
| struct comedi_irq_struct *it; |
| |
| it = comedi_irqs[irq]; |
| if (it == NULL) |
| return 0; |
| it->handler(irq, it->dev_id PT_REGS_NULL); |
| rtl_hard_enable_irq(irq); |
| return 0; |
| } |
| |
| static int comedi_rt_get_irq(struct comedi_irq_struct *it) |
| { |
| rtl_request_global_irq(it->irq, handle_rtl_irq); |
| return 0; |
| } |
| |
| static int comedi_rt_release_irq(struct comedi_irq_struct *it) |
| { |
| rtl_free_global_irq(it->irq); |
| return 0; |
| } |
| |
| void comedi_rt_init(void) |
| { |
| rt_pend_tq_init(); |
| } |
| |
| void comedi_rt_cleanup(void) |
| { |
| rt_pend_tq_cleanup(); |
| } |
| |
| #endif |
| |
| #ifdef CONFIG_COMEDI_PIRQ |
| static int comedi_rt_get_irq(struct comedi_irq_struct *it) |
| { |
| int ret; |
| |
| free_irq(it->irq, it->dev_id); |
| ret = request_irq(it->irq, it->handler, it->flags | SA_PRIORITY, |
| it->device, it->dev_id); |
| |
| return ret; |
| } |
| |
| static int comedi_rt_release_irq(struct comedi_irq_struct *it) |
| { |
| int ret; |
| |
| free_irq(it->irq, it->dev_id); |
| ret = request_irq(it->irq, it->handler, it->flags, |
| it->device, it->dev_id); |
| |
| return ret; |
| } |
| |
| void comedi_rt_init(void) |
| { |
| //rt_pend_tq_init(); |
| } |
| |
| void comedi_rt_cleanup(void) |
| { |
| //rt_pend_tq_cleanup(); |
| } |
| #endif |