412 lines
8.3 KiB
C
412 lines
8.3 KiB
C
/*
|
|
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);
|
|
|
|
static void 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
|