Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.29-rc7 412 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 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 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, 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(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(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 223typedef void (*V_FP_V) (void); 224static V_FP_V handle_void_irq_ptrs[] = { 225 handle_void_irq_0, 226 handle_void_irq_1, 227 handle_void_irq_2, 228 handle_void_irq_3, 229 handle_void_irq_4, 230 handle_void_irq_5, 231 handle_void_irq_6, 232 handle_void_irq_7, 233 handle_void_irq_8, 234 handle_void_irq_9, 235 handle_void_irq_10, 236 handle_void_irq_11, 237 handle_void_irq_12, 238 handle_void_irq_13, 239 handle_void_irq_14, 240 handle_void_irq_15, 241 handle_void_irq_16, 242 handle_void_irq_17, 243 handle_void_irq_18, 244 handle_void_irq_19, 245 handle_void_irq_20, 246 handle_void_irq_21, 247 handle_void_irq_22, 248 handle_void_irq_23, 249}; 250 251static int comedi_rt_get_irq(struct comedi_irq_struct *it) 252{ 253 rt_request_global_irq(it->irq, handle_void_irq_ptrs[it->irq]); 254 rt_startup_irq(it->irq); 255 256 return 0; 257} 258 259static int comedi_rt_release_irq(struct comedi_irq_struct *it) 260{ 261 rt_shutdown_irq(it->irq); 262 rt_free_global_irq(it->irq); 263 return 0; 264} 265#else 266 267static int comedi_rt_get_irq(struct comedi_irq_struct *it) 268{ 269 int ret; 270 271 ret = rt_request_global_irq_arg(it->irq, it->handler, it->flags, 272 it->device, it->dev_id); 273 if (ret < 0) { 274 rt_printk("rt_request_global_irq_arg() returned %d\n", ret); 275 return ret; 276 } 277 rt_startup_irq(it->irq); 278 279 return 0; 280} 281 282static int comedi_rt_release_irq(struct comedi_irq_struct *it) 283{ 284 rt_shutdown_irq(it->irq); 285 rt_free_global_irq(it->irq); 286 return 0; 287} 288#endif 289 290void comedi_rt_init(void) 291{ 292 rt_mount_rtai(); 293 rt_pend_tq_init(); 294} 295 296void comedi_rt_cleanup(void) 297{ 298 rt_umount_rtai(); 299 rt_pend_tq_cleanup(); 300} 301 302#endif 303 304/* Fusion section */ 305#ifdef CONFIG_COMEDI_FUSION 306 307static void fusion_handle_irq(unsigned int irq, void *cookie) 308{ 309 struct comedi_irq_struct *it = cookie; 310 311 it->handler(irq, it->dev_id PT_REGS_NULL); 312 rthal_irq_enable(irq); 313} 314 315static int comedi_rt_get_irq(struct comedi_irq_struct *it) 316{ 317 rthal_irq_request(it->irq, fusion_handle_irq, it); 318 rthal_irq_enable(it->irq); 319 return 0; 320} 321 322static int comedi_rt_release_irq(struct comedi_irq_struct *it) 323{ 324 rthal_irq_disable(it->irq); 325 rthal_irq_release(it->irq); 326 return 0; 327} 328 329void comedi_rt_init(void) 330{ 331 rt_pend_tq_init(); 332} 333 334void comedi_rt_cleanup(void) 335{ 336 rt_pend_tq_cleanup(); 337} 338 339#endif /*CONFIG_COMEDI_FUSION */ 340 341/* RTLinux section */ 342#ifdef CONFIG_COMEDI_RTL 343 344static unsigned int handle_rtl_irq(unsigned int irq PT_REGS_ARG) 345{ 346 struct comedi_irq_struct *it; 347 348 it = comedi_irqs[irq]; 349 if (it == NULL) 350 return 0; 351 it->handler(irq, it->dev_id PT_REGS_NULL); 352 rtl_hard_enable_irq(irq); 353 return 0; 354} 355 356static int comedi_rt_get_irq(struct comedi_irq_struct *it) 357{ 358 rtl_request_global_irq(it->irq, handle_rtl_irq); 359 return 0; 360} 361 362static int comedi_rt_release_irq(struct comedi_irq_struct *it) 363{ 364 rtl_free_global_irq(it->irq); 365 return 0; 366} 367 368void comedi_rt_init(void) 369{ 370 rt_pend_tq_init(); 371} 372 373void comedi_rt_cleanup(void) 374{ 375 rt_pend_tq_cleanup(); 376} 377 378#endif 379 380#ifdef CONFIG_COMEDI_PIRQ 381static int comedi_rt_get_irq(struct comedi_irq_struct *it) 382{ 383 int ret; 384 385 free_irq(it->irq, it->dev_id); 386 ret = request_irq(it->irq, it->handler, it->flags | SA_PRIORITY, 387 it->device, it->dev_id); 388 389 return ret; 390} 391 392static int comedi_rt_release_irq(struct comedi_irq_struct *it) 393{ 394 int ret; 395 396 free_irq(it->irq, it->dev_id); 397 ret = request_irq(it->irq, it->handler, it->flags, 398 it->device, it->dev_id); 399 400 return ret; 401} 402 403void comedi_rt_init(void) 404{ 405 //rt_pend_tq_init(); 406} 407 408void comedi_rt_cleanup(void) 409{ 410 //rt_pend_tq_cleanup(); 411} 412#endif