From ed9eccbe8970f6eedc1b978c157caf1251a896d4 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Tue, 4 Nov 2008 20:29:31 -0800 Subject: Staging: add comedi core This adds the Comedi core to the staging tree. This is a data acquision infrastructure for Linux, providing a common interface for these types of drivers. Taken directly from the comedi git tree, with only minor tweaks by Greg to get it to build properly within the kernel tree. From: David Schleef Cc: Ian Abbott Cc: Shawn Bohrer Signed-off-by: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/rt.c | 412 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 412 insertions(+) create mode 100644 drivers/staging/comedi/rt.c (limited to 'drivers/staging/comedi/rt.c') 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 + + 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 -- cgit v1.2.3