Commit | Line | Data |
---|---|---|
ed9eccbe DS |
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) { | |
b6c77757 | 81 | /* we failed, so fall back on allowing shared interrupt (which we won't ever make RT) */ |
ed9eccbe DS |
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); | |
b6c77757 | 195 | rt_enable_irq(irq); /* needed by rtai-adeos, seems like it shouldn't hurt earlier versions */ |
ed9eccbe DS |
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 | { | |
b6c77757 | 405 | /* rt_pend_tq_init(); */ |
ed9eccbe DS |
406 | } |
407 | ||
408 | void comedi_rt_cleanup(void) | |
409 | { | |
b6c77757 | 410 | /* rt_pend_tq_cleanup(); */ |
ed9eccbe DS |
411 | } |
412 | #endif |