diff options
Diffstat (limited to 'drivers/staging/comedi/rt.c')
-rw-r--r-- | drivers/staging/comedi/rt.c | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/drivers/staging/comedi/rt.c b/drivers/staging/comedi/rt.c new file mode 100644 index 00000000000..385b81b94ac --- /dev/null +++ b/drivers/staging/comedi/rt.c @@ -0,0 +1,412 @@ +/* + 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 |