/* comedi/rt.c comedi kernel module COMEDI - Linux Control and Measurement Device Interface Copyright (C) 1997-2000 David A. Schleef 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 #include #include #include #include #include #include #include #include #include #include "rt_pend_tq.h" #ifdef CONFIG_COMEDI_RTAI #include #endif #ifdef CONFIG_COMEDI_FUSION #include #endif #ifdef CONFIG_COMEDI_RTL #include #include #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