at v2.6.30 411 lines 8.5 kB view raw
1/* 2 comedi/rt.c 3 comedi kernel module 4 5 COMEDI - Linux Control and Measurement Device Interface 6 Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org> 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 22*/ 23 24#undef DEBUG 25 26#define __NO_VERSION__ 27#include <linux/comedidev.h> 28 29#include <linux/errno.h> 30#include <linux/kernel.h> 31#include <linux/sched.h> 32#include <linux/fcntl.h> 33#include <linux/delay.h> 34#include <linux/ioport.h> 35#include <linux/mm.h> 36#include <linux/slab.h> 37#include <asm/io.h> 38 39#include "rt_pend_tq.h" 40 41#ifdef CONFIG_COMEDI_RTAI 42#include <rtai.h> 43#endif 44 45#ifdef CONFIG_COMEDI_FUSION 46#include <nucleus/asm/hal.h> 47#endif 48 49#ifdef CONFIG_COMEDI_RTL 50#include <rtl_core.h> 51#include <rtl_sync.h> 52#endif 53 54struct comedi_irq_struct { 55 int rt; 56 int irq; 57 irqreturn_t(*handler) (int irq, void *dev_id PT_REGS_ARG); 58 unsigned long flags; 59 const char *device; 60 struct comedi_device *dev_id; 61}; 62 63static int comedi_rt_get_irq(struct comedi_irq_struct *it); 64static int comedi_rt_release_irq(struct comedi_irq_struct *it); 65 66static struct comedi_irq_struct *comedi_irqs[NR_IRQS]; 67 68int comedi_request_irq(unsigned irq, irqreturn_t(*handler) (int, 69 void *PT_REGS_ARG), unsigned long flags, const char *device, 70 struct comedi_device *dev_id) 71{ 72 struct comedi_irq_struct *it; 73 int ret; 74 /* null shared interrupt flag, since rt interrupt handlers do not 75 * support it, and this version of comedi_request_irq() is only 76 * called for kernels with rt support */ 77 unsigned long unshared_flags = flags & ~IRQF_SHARED; 78 79 ret = request_irq(irq, handler, unshared_flags, device, dev_id); 80 if (ret < 0) { 81 /* we failed, so fall back on allowing shared interrupt (which we won't ever make RT) */ 82 if (flags & IRQF_SHARED) { 83 rt_printk 84 ("comedi: cannot get unshared interrupt, will not use RT interrupts.\n"); 85 ret = request_irq(irq, handler, flags, device, dev_id); 86 } 87 if (ret < 0) 88 return ret; 89 90 } else { 91 it = kzalloc(sizeof(struct comedi_irq_struct), GFP_KERNEL); 92 if (!it) 93 return -ENOMEM; 94 95 it->handler = handler; 96 it->irq = irq; 97 it->dev_id = dev_id; 98 it->device = device; 99 it->flags = unshared_flags; 100 comedi_irqs[irq] = it; 101 } 102 return 0; 103} 104 105void comedi_free_irq(unsigned int irq, struct comedi_device *dev_id) 106{ 107 struct comedi_irq_struct *it; 108 109 free_irq(irq, dev_id); 110 111 it = comedi_irqs[irq]; 112 if (it == NULL) 113 return; 114 115 if (it->rt) { 116 printk("real-time IRQ allocated at board removal (ignore)\n"); 117 comedi_rt_release_irq(it); 118 } 119 120 kfree(it); 121 comedi_irqs[irq] = NULL; 122} 123 124int comedi_switch_to_rt(struct comedi_device *dev) 125{ 126 struct comedi_irq_struct *it; 127 unsigned long flags; 128 129 it = comedi_irqs[dev->irq]; 130 /* drivers might not be using an interrupt for commands, 131 or we might not have been able to get an unshared irq */ 132 if (it == NULL) 133 return -1; 134 135 comedi_spin_lock_irqsave(&dev->spinlock, flags); 136 137 if (!dev->rt) 138 comedi_rt_get_irq(it); 139 140 dev->rt++; 141 it->rt = 1; 142 143 comedi_spin_unlock_irqrestore(&dev->spinlock, flags); 144 145 return 0; 146} 147 148void comedi_switch_to_non_rt(struct comedi_device *dev) 149{ 150 struct comedi_irq_struct *it; 151 unsigned long flags; 152 153 it = comedi_irqs[dev->irq]; 154 if (it == NULL) 155 return; 156 157 comedi_spin_lock_irqsave(&dev->spinlock, flags); 158 159 dev->rt--; 160 if (!dev->rt) 161 comedi_rt_release_irq(it); 162 163 it->rt = 0; 164 165 comedi_spin_unlock_irqrestore(&dev->spinlock, flags); 166} 167 168void wake_up_int_handler(int arg1, void *arg2) 169{ 170 wake_up_interruptible((wait_queue_head_t *) arg2); 171} 172 173void comedi_rt_pend_wakeup(wait_queue_head_t *q) 174{ 175 rt_pend_call(wake_up_int_handler, 0, q); 176} 177 178/* RTAI section */ 179#ifdef CONFIG_COMEDI_RTAI 180 181#ifndef HAVE_RT_REQUEST_IRQ_WITH_ARG 182#define DECLARE_VOID_IRQ(irq) \ 183static void handle_void_irq_ ## irq (void){ handle_void_irq(irq); } 184 185static void handle_void_irq(int irq) 186{ 187 struct comedi_irq_struct *it; 188 189 it = comedi_irqs[irq]; 190 if (it == NULL) { 191 rt_printk("comedi: null irq struct?\n"); 192 return; 193 } 194 it->handler(irq, it->dev_id PT_REGS_NULL); 195 rt_enable_irq(irq); /* needed by rtai-adeos, seems like it shouldn't hurt earlier versions */ 196} 197 198DECLARE_VOID_IRQ(0); 199DECLARE_VOID_IRQ(1); 200DECLARE_VOID_IRQ(2); 201DECLARE_VOID_IRQ(3); 202DECLARE_VOID_IRQ(4); 203DECLARE_VOID_IRQ(5); 204DECLARE_VOID_IRQ(6); 205DECLARE_VOID_IRQ(7); 206DECLARE_VOID_IRQ(8); 207DECLARE_VOID_IRQ(9); 208DECLARE_VOID_IRQ(10); 209DECLARE_VOID_IRQ(11); 210DECLARE_VOID_IRQ(12); 211DECLARE_VOID_IRQ(13); 212DECLARE_VOID_IRQ(14); 213DECLARE_VOID_IRQ(15); 214DECLARE_VOID_IRQ(16); 215DECLARE_VOID_IRQ(17); 216DECLARE_VOID_IRQ(18); 217DECLARE_VOID_IRQ(19); 218DECLARE_VOID_IRQ(20); 219DECLARE_VOID_IRQ(21); 220DECLARE_VOID_IRQ(22); 221DECLARE_VOID_IRQ(23); 222 223static void handle_void_irq_ptrs[] = { 224 handle_void_irq_0, 225 handle_void_irq_1, 226 handle_void_irq_2, 227 handle_void_irq_3, 228 handle_void_irq_4, 229 handle_void_irq_5, 230 handle_void_irq_6, 231 handle_void_irq_7, 232 handle_void_irq_8, 233 handle_void_irq_9, 234 handle_void_irq_10, 235 handle_void_irq_11, 236 handle_void_irq_12, 237 handle_void_irq_13, 238 handle_void_irq_14, 239 handle_void_irq_15, 240 handle_void_irq_16, 241 handle_void_irq_17, 242 handle_void_irq_18, 243 handle_void_irq_19, 244 handle_void_irq_20, 245 handle_void_irq_21, 246 handle_void_irq_22, 247 handle_void_irq_23, 248}; 249 250static int comedi_rt_get_irq(struct comedi_irq_struct *it) 251{ 252 rt_request_global_irq(it->irq, handle_void_irq_ptrs[it->irq]); 253 rt_startup_irq(it->irq); 254 255 return 0; 256} 257 258static int comedi_rt_release_irq(struct comedi_irq_struct *it) 259{ 260 rt_shutdown_irq(it->irq); 261 rt_free_global_irq(it->irq); 262 return 0; 263} 264#else 265 266static int comedi_rt_get_irq(struct comedi_irq_struct *it) 267{ 268 int ret; 269 270 ret = rt_request_global_irq_arg(it->irq, it->handler, it->flags, 271 it->device, it->dev_id); 272 if (ret < 0) { 273 rt_printk("rt_request_global_irq_arg() returned %d\n", ret); 274 return ret; 275 } 276 rt_startup_irq(it->irq); 277 278 return 0; 279} 280 281static int comedi_rt_release_irq(struct comedi_irq_struct *it) 282{ 283 rt_shutdown_irq(it->irq); 284 rt_free_global_irq(it->irq); 285 return 0; 286} 287#endif 288 289void comedi_rt_init(void) 290{ 291 rt_mount_rtai(); 292 rt_pend_tq_init(); 293} 294 295void comedi_rt_cleanup(void) 296{ 297 rt_umount_rtai(); 298 rt_pend_tq_cleanup(); 299} 300 301#endif 302 303/* Fusion section */ 304#ifdef CONFIG_COMEDI_FUSION 305 306static void fusion_handle_irq(unsigned int irq, void *cookie) 307{ 308 struct comedi_irq_struct *it = cookie; 309 310 it->handler(irq, it->dev_id PT_REGS_NULL); 311 rthal_irq_enable(irq); 312} 313 314static int comedi_rt_get_irq(struct comedi_irq_struct *it) 315{ 316 rthal_irq_request(it->irq, fusion_handle_irq, it); 317 rthal_irq_enable(it->irq); 318 return 0; 319} 320 321static int comedi_rt_release_irq(struct comedi_irq_struct *it) 322{ 323 rthal_irq_disable(it->irq); 324 rthal_irq_release(it->irq); 325 return 0; 326} 327 328void comedi_rt_init(void) 329{ 330 rt_pend_tq_init(); 331} 332 333void comedi_rt_cleanup(void) 334{ 335 rt_pend_tq_cleanup(); 336} 337 338#endif /*CONFIG_COMEDI_FUSION */ 339 340/* RTLinux section */ 341#ifdef CONFIG_COMEDI_RTL 342 343static unsigned int handle_rtl_irq(unsigned int irq PT_REGS_ARG) 344{ 345 struct comedi_irq_struct *it; 346 347 it = comedi_irqs[irq]; 348 if (it == NULL) 349 return 0; 350 it->handler(irq, it->dev_id PT_REGS_NULL); 351 rtl_hard_enable_irq(irq); 352 return 0; 353} 354 355static int comedi_rt_get_irq(struct comedi_irq_struct *it) 356{ 357 rtl_request_global_irq(it->irq, handle_rtl_irq); 358 return 0; 359} 360 361static int comedi_rt_release_irq(struct comedi_irq_struct *it) 362{ 363 rtl_free_global_irq(it->irq); 364 return 0; 365} 366 367void comedi_rt_init(void) 368{ 369 rt_pend_tq_init(); 370} 371 372void comedi_rt_cleanup(void) 373{ 374 rt_pend_tq_cleanup(); 375} 376 377#endif 378 379#ifdef CONFIG_COMEDI_PIRQ 380static int comedi_rt_get_irq(struct comedi_irq_struct *it) 381{ 382 int ret; 383 384 free_irq(it->irq, it->dev_id); 385 ret = request_irq(it->irq, it->handler, it->flags | SA_PRIORITY, 386 it->device, it->dev_id); 387 388 return ret; 389} 390 391static int comedi_rt_release_irq(struct comedi_irq_struct *it) 392{ 393 int ret; 394 395 free_irq(it->irq, it->dev_id); 396 ret = request_irq(it->irq, it->handler, it->flags, 397 it->device, it->dev_id); 398 399 return ret; 400} 401 402void comedi_rt_init(void) 403{ 404 /* rt_pend_tq_init(); */ 405} 406 407void comedi_rt_cleanup(void) 408{ 409 /* rt_pend_tq_cleanup(); */ 410} 411#endif