Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging-2.6
[linux-2.6] / drivers / staging / comedi / rt.c
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
54 struct 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
63 static int comedi_rt_get_irq(struct comedi_irq_struct *it);
64 static int comedi_rt_release_irq(struct comedi_irq_struct *it);
65
66 static struct comedi_irq_struct *comedi_irqs[NR_IRQS];
67
68 int 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
105 void 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
124 int 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
148 void 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
168 void wake_up_int_handler(int arg1, void *arg2)
169 {
170         wake_up_interruptible((wait_queue_head_t *) arg2);
171 }
172
173 void 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) \
183 static void handle_void_irq_ ## irq (void){ handle_void_irq(irq);}
184
185 static 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
198 DECLARE_VOID_IRQ(0);
199 DECLARE_VOID_IRQ(1);
200 DECLARE_VOID_IRQ(2);
201 DECLARE_VOID_IRQ(3);
202 DECLARE_VOID_IRQ(4);
203 DECLARE_VOID_IRQ(5);
204 DECLARE_VOID_IRQ(6);
205 DECLARE_VOID_IRQ(7);
206 DECLARE_VOID_IRQ(8);
207 DECLARE_VOID_IRQ(9);
208 DECLARE_VOID_IRQ(10);
209 DECLARE_VOID_IRQ(11);
210 DECLARE_VOID_IRQ(12);
211 DECLARE_VOID_IRQ(13);
212 DECLARE_VOID_IRQ(14);
213 DECLARE_VOID_IRQ(15);
214 DECLARE_VOID_IRQ(16);
215 DECLARE_VOID_IRQ(17);
216 DECLARE_VOID_IRQ(18);
217 DECLARE_VOID_IRQ(19);
218 DECLARE_VOID_IRQ(20);
219 DECLARE_VOID_IRQ(21);
220 DECLARE_VOID_IRQ(22);
221 DECLARE_VOID_IRQ(23);
222
223 typedef void (*V_FP_V) (void);
224 static 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
251 static 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
259 static 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
267 static 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
282 static 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
290 void comedi_rt_init(void)
291 {
292         rt_mount_rtai();
293         rt_pend_tq_init();
294 }
295
296 void 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
307 static 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
315 static 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
322 static 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
329 void comedi_rt_init(void)
330 {
331         rt_pend_tq_init();
332 }
333
334 void 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
344 static 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
356 static 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
362 static int comedi_rt_release_irq(struct comedi_irq_struct *it)
363 {
364         rtl_free_global_irq(it->irq);
365         return 0;
366 }
367
368 void comedi_rt_init(void)
369 {
370         rt_pend_tq_init();
371 }
372
373 void comedi_rt_cleanup(void)
374 {
375         rt_pend_tq_cleanup();
376 }
377
378 #endif
379
380 #ifdef CONFIG_COMEDI_PIRQ
381 static 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
392 static 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
403 void comedi_rt_init(void)
404 {
405         //rt_pend_tq_init();
406 }
407
408 void comedi_rt_cleanup(void)
409 {
410         //rt_pend_tq_cleanup();
411 }
412 #endif