aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/comedi/rt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/comedi/rt.c')
-rw-r--r--drivers/staging/comedi/rt.c412
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 000000000000..385b81b94ac5
--- /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