Staging: comedi: Remove C99 comments
[pandora-kernel.git] / drivers / staging / comedi / drivers / comedi_rt_timer.c
1 /*
2     comedi/drivers/comedi_rt_timer.c
3     virtual driver for using RTL timing sources
4
5     Authors: David A. Schleef, Frank M. Hess
6
7     COMEDI - Linux Control and Measurement Device Interface
8     Copyright (C) 1999,2001 David A. Schleef <ds@schleef.org>
9
10     This program is free software; you can redistribute it and/or modify
11     it under the terms of the GNU General Public License as published by
12     the Free Software Foundation; either version 2 of the License, or
13     (at your option) any later version.
14
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18     GNU General Public License for more details.
19
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
24 **************************************************************************
25 */
26 /*
27 Driver: comedi_rt_timer
28 Description: Command emulator using real-time tasks
29 Author: ds, fmhess
30 Devices:
31 Status: works
32
33 This driver requires RTAI or RTLinux to work correctly.  It doesn't
34 actually drive hardware directly, but calls other drivers and uses
35 a real-time task to emulate commands for drivers and devices that
36 are incapable of native commands.  Thus, you can get accurately
37 timed I/O on any device.
38
39 Since the timing is all done in software, sampling jitter is much
40 higher than with a device that has an on-board timer, and maximum
41 sample rate is much lower.
42
43 Configuration options:
44   [0] - minor number of device you wish to emulate commands for
45   [1] - subdevice number you wish to emulate commands for
46 */
47 /*
48 TODO:
49         Support for digital io commands could be added, except I can't see why
50                 anyone would want to use them
51         What happens if device we are emulating for is de-configured?
52 */
53
54 #include "../comedidev.h"
55 #include "../comedilib.h"
56
57 #include "comedi_fc.h"
58
59 #ifdef CONFIG_COMEDI_RTL_V1
60 #include <rtl_sched.h>
61 #include <asm/rt_irq.h>
62 #endif
63 #ifdef CONFIG_COMEDI_RTL
64 #include <rtl.h>
65 #include <rtl_sched.h>
66 #include <rtl_compat.h>
67 #include <asm/div64.h>
68
69 #ifndef RTLINUX_VERSION_CODE
70 #define RTLINUX_VERSION_CODE 0
71 #endif
72 #ifndef RTLINUX_VERSION
73 #define RTLINUX_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
74 #endif
75
76 /* begin hack to workaround broken HRT_TO_8254() function on rtlinux */
77 #if RTLINUX_VERSION_CODE <= RTLINUX_VERSION(3,0,100)
78 /* this function sole purpose is to divide a long long by 838 */
79 static inline RTIME nano2count(long long ns)
80 {
81         do_div(ns, 838);
82         return ns;
83 }
84
85 #ifdef rt_get_time()
86 #undef rt_get_time()
87 #endif
88 #define rt_get_time() nano2count(gethrtime())
89
90 #else
91
92 #define nano2count(x) HRT_TO_8254(x)
93 #endif
94 /* end hack */
95
96 /* rtl-rtai compatibility */
97 #define rt_task_wait_period() rt_task_wait()
98 #define rt_pend_linux_srq(irq) rtl_global_pend_irq(irq)
99 #define rt_free_srq(irq) rtl_free_soft_irq(irq)
100 #define rt_request_srq(x,y,z) rtl_get_soft_irq(y,"timer")
101 #define rt_task_init(a,b,c,d,e,f,g) rt_task_init(a,b,c,d,(e)+1)
102 #define rt_task_resume(x) rt_task_wakeup(x)
103 #define rt_set_oneshot_mode()
104 #define start_rt_timer(x)
105 #define stop_rt_timer()
106
107 #endif
108 #ifdef CONFIG_COMEDI_RTAI
109 #include <rtai.h>
110 #include <rtai_sched.h>
111
112 #if RTAI_VERSION_CODE < RTAI_MANGLE_VERSION(3,3,0)
113 #define comedi_rt_task_context_t        int
114 #else
115 #define comedi_rt_task_context_t        long
116 #endif
117
118 #endif
119
120 /* This defines the fastest speed we will emulate.  Note that
121  * without a watchdog (like in RTAI), we could easily overrun our
122  * task period because analog input tends to be slow. */
123 #define SPEED_LIMIT 100000      /* in nanoseconds */
124
125 static int timer_attach(struct comedi_device * dev, struct comedi_devconfig * it);
126 static int timer_detach(struct comedi_device * dev);
127 static int timer_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
128         unsigned int trig_num);
129 static int timer_start_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
130
131 static struct comedi_driver driver_timer = {
132       module:THIS_MODULE,
133       driver_name:"comedi_rt_timer",
134       attach:timer_attach,
135       detach:timer_detach,
136 /* open:           timer_open, */
137 };
138
139 COMEDI_INITCLEANUP(driver_timer);
140
141 struct timer_private {
142         comedi_t *device;       /*  device we are emulating commands for */
143         int subd;               /*  subdevice we are emulating commands for */
144         RT_TASK *rt_task;       /*  rt task that starts scans */
145         RT_TASK *scan_task;     /*  rt task that controls conversion timing in a scan */
146         /* io_function can point to either an input or output function
147          * depending on what kind of subdevice we are emulating for */
148         int (*io_function) (struct comedi_device * dev, struct comedi_cmd * cmd,
149                 unsigned int index);
150 /*
151 * RTIME has units of 1 = 838 nanoseconds time at which first scan
152 * started, used to check scan timing
153 */
154         RTIME start;
155         /*  time between scans */
156         RTIME scan_period;
157         /*  time between conversions in a scan */
158         RTIME convert_period;
159         /*  flags */
160         volatile int stop;      /*  indicates we should stop */
161         volatile int rt_task_active;    /*  indicates rt_task is servicing a struct comedi_cmd */
162         volatile int scan_task_active;  /*  indicates scan_task is servicing a struct comedi_cmd */
163         unsigned timer_running:1;
164 };
165 #define devpriv ((struct timer_private *)dev->private)
166
167 static int timer_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
168 {
169         devpriv->stop = 1;
170
171         return 0;
172 }
173
174 /* checks for scan timing error */
175 inline static int check_scan_timing(struct comedi_device * dev,
176         unsigned long long scan)
177 {
178         RTIME now, timing_error;
179
180         now = rt_get_time();
181         timing_error = now - (devpriv->start + scan * devpriv->scan_period);
182         if (timing_error > devpriv->scan_period) {
183                 comedi_error(dev, "timing error");
184                 rt_printk("scan started %i ns late\n", timing_error * 838);
185                 return -1;
186         }
187
188         return 0;
189 }
190
191 /* checks for conversion timing error */
192 inline static int check_conversion_timing(struct comedi_device * dev,
193         RTIME scan_start, unsigned int conversion)
194 {
195         RTIME now, timing_error;
196
197         now = rt_get_time();
198         timing_error =
199                 now - (scan_start + conversion * devpriv->convert_period);
200         if (timing_error > devpriv->convert_period) {
201                 comedi_error(dev, "timing error");
202                 rt_printk("conversion started %i ns late\n",
203                         timing_error * 838);
204                 return -1;
205         }
206
207         return 0;
208 }
209
210 /* devpriv->io_function for an input subdevice */
211 static int timer_data_read(struct comedi_device * dev, struct comedi_cmd * cmd,
212         unsigned int index)
213 {
214         struct comedi_subdevice *s = dev->read_subdev;
215         int ret;
216         unsigned int data;
217
218         ret = comedi_data_read(devpriv->device, devpriv->subd,
219                 CR_CHAN(cmd->chanlist[index]),
220                 CR_RANGE(cmd->chanlist[index]),
221                 CR_AREF(cmd->chanlist[index]), &data);
222         if (ret < 0) {
223                 comedi_error(dev, "read error");
224                 return -EIO;
225         }
226         if (s->flags & SDF_LSAMPL) {
227                 cfc_write_long_to_buffer(s, data);
228         } else {
229                 comedi_buf_put(s->async, data);
230         }
231
232         return 0;
233 }
234
235 /* devpriv->io_function for an output subdevice */
236 static int timer_data_write(struct comedi_device * dev, struct comedi_cmd * cmd,
237         unsigned int index)
238 {
239         struct comedi_subdevice *s = dev->write_subdev;
240         unsigned int num_bytes;
241         short data;
242         unsigned int long_data;
243         int ret;
244
245         if (s->flags & SDF_LSAMPL) {
246                 num_bytes =
247                         cfc_read_array_from_buffer(s, &long_data,
248                         sizeof(long_data));
249         } else {
250                 num_bytes = cfc_read_array_from_buffer(s, &data, sizeof(data));
251                 long_data = data;
252         }
253
254         if (num_bytes == 0) {
255                 comedi_error(dev, "buffer underrun");
256                 return -EAGAIN;
257         }
258         ret = comedi_data_write(devpriv->device, devpriv->subd,
259                 CR_CHAN(cmd->chanlist[index]),
260                 CR_RANGE(cmd->chanlist[index]),
261                 CR_AREF(cmd->chanlist[index]), long_data);
262         if (ret < 0) {
263                 comedi_error(dev, "write error");
264                 return -EIO;
265         }
266
267         return 0;
268 }
269
270 /* devpriv->io_function for DIO subdevices */
271 static int timer_dio_read(struct comedi_device * dev, struct comedi_cmd * cmd,
272         unsigned int index)
273 {
274         struct comedi_subdevice *s = dev->read_subdev;
275         int ret;
276         unsigned int data;
277
278         ret = comedi_dio_bitfield(devpriv->device, devpriv->subd, 0, &data);
279         if (ret < 0) {
280                 comedi_error(dev, "read error");
281                 return -EIO;
282         }
283
284         if (s->flags & SDF_LSAMPL)
285                 cfc_write_long_to_buffer(s, data);
286         else
287                 cfc_write_to_buffer(s, data);
288
289         return 0;
290 }
291
292 /* performs scans */
293 static void scan_task_func(comedi_rt_task_context_t d)
294 {
295         struct comedi_device *dev = (struct comedi_device *) d;
296         struct comedi_subdevice *s = dev->subdevices + 0;
297         struct comedi_async *async = s->async;
298         struct comedi_cmd *cmd = &async->cmd;
299         int i, ret;
300         unsigned long long n;
301         RTIME scan_start;
302
303         /*  every struct comedi_cmd causes one execution of while loop */
304         while (1) {
305                 devpriv->scan_task_active = 1;
306                 /*  each for loop completes one scan */
307                 for (n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE;
308                         n++) {
309                         if (n) {
310                                 /*  suspend task until next scan */
311                                 ret = rt_task_suspend(devpriv->scan_task);
312                                 if (ret < 0) {
313                                         comedi_error(dev,
314                                                 "error suspending scan task");
315                                         async->events |= COMEDI_CB_ERROR;
316                                         goto cleanup;
317                                 }
318                         }
319                         /*  check if stop flag was set (by timer_cancel()) */
320                         if (devpriv->stop)
321                                 goto cleanup;
322                         ret = check_scan_timing(dev, n);
323                         if (ret < 0) {
324                                 async->events |= COMEDI_CB_ERROR;
325                                 goto cleanup;
326                         }
327                         scan_start = rt_get_time();
328                         for (i = 0; i < cmd->scan_end_arg; i++) {
329                                 /*  conversion timing */
330                                 if (cmd->convert_src == TRIG_TIMER && i) {
331                                         rt_task_wait_period();
332                                         ret = check_conversion_timing(dev,
333                                                 scan_start, i);
334                                         if (ret < 0) {
335                                                 async->events |=
336                                                         COMEDI_CB_ERROR;
337                                                 goto cleanup;
338                                         }
339                                 }
340                                 ret = devpriv->io_function(dev, cmd, i);
341                                 if (ret < 0) {
342                                         async->events |= COMEDI_CB_ERROR;
343                                         goto cleanup;
344                                 }
345                         }
346                         s->async->events |= COMEDI_CB_BLOCK;
347                         comedi_event(dev, s);
348                         s->async->events = 0;
349                 }
350
351               cleanup:
352
353                 comedi_unlock(devpriv->device, devpriv->subd);
354                 async->events |= COMEDI_CB_EOA;
355                 comedi_event(dev, s);
356                 async->events = 0;
357                 devpriv->scan_task_active = 0;
358                 /*  suspend task until next struct comedi_cmd */
359                 rt_task_suspend(devpriv->scan_task);
360         }
361 }
362
363 static void timer_task_func(comedi_rt_task_context_t d)
364 {
365         struct comedi_device *dev = (struct comedi_device *) d;
366         struct comedi_subdevice *s = dev->subdevices + 0;
367         struct comedi_cmd *cmd = &s->async->cmd;
368         int ret;
369         unsigned long long n;
370
371         /*  every struct comedi_cmd causes one execution of while loop */
372         while (1) {
373                 devpriv->rt_task_active = 1;
374                 devpriv->scan_task_active = 1;
375                 devpriv->start = rt_get_time();
376
377                 for (n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE;
378                         n++) {
379                         /*  scan timing */
380                         if (n)
381                                 rt_task_wait_period();
382                         if (devpriv->scan_task_active == 0) {
383                                 goto cleanup;
384                         }
385                         ret = rt_task_make_periodic(devpriv->scan_task,
386                                 devpriv->start + devpriv->scan_period * n,
387                                 devpriv->convert_period);
388                         if (ret < 0) {
389                                 comedi_error(dev, "bug!");
390                         }
391                 }
392
393               cleanup:
394
395                 devpriv->rt_task_active = 0;
396                 /*  suspend until next struct comedi_cmd */
397                 rt_task_suspend(devpriv->rt_task);
398         }
399 }
400
401 static int timer_insn(struct comedi_device * dev, struct comedi_subdevice * s,
402         struct comedi_insn * insn, unsigned int * data)
403 {
404         struct comedi_insn xinsn = *insn;
405
406         xinsn.data = data;
407         xinsn.subdev = devpriv->subd;
408
409         return comedi_do_insn(devpriv->device, &xinsn);
410 }
411
412 static int cmdtest_helper(struct comedi_cmd * cmd,
413         unsigned int start_src,
414         unsigned int scan_begin_src,
415         unsigned int convert_src,
416         unsigned int scan_end_src, unsigned int stop_src)
417 {
418         int err = 0;
419         int tmp;
420
421         tmp = cmd->start_src;
422         cmd->start_src &= start_src;
423         if (!cmd->start_src || tmp != cmd->start_src)
424                 err++;
425
426         tmp = cmd->scan_begin_src;
427         cmd->scan_begin_src &= scan_begin_src;
428         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
429                 err++;
430
431         tmp = cmd->convert_src;
432         cmd->convert_src &= convert_src;
433         if (!cmd->convert_src || tmp != cmd->convert_src)
434                 err++;
435
436         tmp = cmd->scan_end_src;
437         cmd->scan_end_src &= scan_end_src;
438         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
439                 err++;
440
441         tmp = cmd->stop_src;
442         cmd->stop_src &= stop_src;
443         if (!cmd->stop_src || tmp != cmd->stop_src)
444                 err++;
445
446         return err;
447 }
448
449 static int timer_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
450         struct comedi_cmd * cmd)
451 {
452         int err = 0;
453         unsigned int start_src = 0;
454
455         if (s->type == COMEDI_SUBD_AO)
456                 start_src = TRIG_INT;
457         else
458                 start_src = TRIG_NOW;
459
460         err = cmdtest_helper(cmd, start_src,    /* start_src */
461                 TRIG_TIMER | TRIG_FOLLOW,       /* scan_begin_src */
462                 TRIG_NOW | TRIG_TIMER,  /* convert_src */
463                 TRIG_COUNT,     /* scan_end_src */
464                 TRIG_COUNT | TRIG_NONE);        /* stop_src */
465         if (err)
466                 return 1;
467
468         /* step 2: make sure trigger sources are unique and mutually
469          * compatible */
470
471         if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_INT)
472                 err++;
473         if (cmd->scan_begin_src != TRIG_TIMER &&
474                 cmd->scan_begin_src != TRIG_FOLLOW)
475                 err++;
476         if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_NOW)
477                 err++;
478         if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
479                 err++;
480         if (cmd->scan_begin_src == TRIG_FOLLOW
481                 && cmd->convert_src != TRIG_TIMER)
482                 err++;
483         if (cmd->convert_src == TRIG_NOW && cmd->scan_begin_src != TRIG_TIMER)
484                 err++;
485
486         if (err)
487                 return 2;
488
489         /* step 3: make sure arguments are trivially compatible */
490         /*  limit frequency, this is fairly arbitrary */
491         if (cmd->scan_begin_src == TRIG_TIMER) {
492                 if (cmd->scan_begin_arg < SPEED_LIMIT) {
493                         cmd->scan_begin_arg = SPEED_LIMIT;
494                         err++;
495                 }
496         }
497         if (cmd->convert_src == TRIG_TIMER) {
498                 if (cmd->convert_arg < SPEED_LIMIT) {
499                         cmd->convert_arg = SPEED_LIMIT;
500                         err++;
501                 }
502         }
503         /*  make sure conversion and scan frequencies are compatible */
504         if (cmd->convert_src == TRIG_TIMER && cmd->scan_begin_src == TRIG_TIMER) {
505                 if (cmd->convert_arg * cmd->scan_end_arg > cmd->scan_begin_arg) {
506                         cmd->scan_begin_arg =
507                                 cmd->convert_arg * cmd->scan_end_arg;
508                         err++;
509                 }
510         }
511         if (err)
512                 return 3;
513
514         /* step 4: fix up and arguments */
515         if (err)
516                 return 4;
517
518         return 0;
519 }
520
521 static int timer_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
522 {
523         int ret;
524         struct comedi_cmd *cmd = &s->async->cmd;
525
526         /* hack attack: drivers are not supposed to do this: */
527         dev->rt = 1;
528
529         /*  make sure tasks have finished cleanup of last struct comedi_cmd */
530         if (devpriv->rt_task_active || devpriv->scan_task_active)
531                 return -EBUSY;
532
533         ret = comedi_lock(devpriv->device, devpriv->subd);
534         if (ret < 0) {
535                 comedi_error(dev, "failed to obtain lock");
536                 return ret;
537         }
538         switch (cmd->scan_begin_src) {
539         case TRIG_TIMER:
540                 devpriv->scan_period = nano2count(cmd->scan_begin_arg);
541                 break;
542         case TRIG_FOLLOW:
543                 devpriv->scan_period =
544                         nano2count(cmd->convert_arg * cmd->scan_end_arg);
545                 break;
546         default:
547                 comedi_error(dev, "bug setting scan period!");
548                 return -1;
549                 break;
550         }
551         switch (cmd->convert_src) {
552         case TRIG_TIMER:
553                 devpriv->convert_period = nano2count(cmd->convert_arg);
554                 break;
555         case TRIG_NOW:
556                 devpriv->convert_period = 1;
557                 break;
558         default:
559                 comedi_error(dev, "bug setting conversion period!");
560                 return -1;
561                 break;
562         }
563
564         if (cmd->start_src == TRIG_NOW)
565                 return timer_start_cmd(dev, s);
566
567         s->async->inttrig = timer_inttrig;
568
569         return 0;
570 }
571
572 static int timer_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
573         unsigned int trig_num)
574 {
575         if (trig_num != 0)
576                 return -EINVAL;
577
578         s->async->inttrig = NULL;
579
580         return timer_start_cmd(dev, s);
581 }
582
583 static int timer_start_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
584 {
585         struct comedi_async *async = s->async;
586         struct comedi_cmd *cmd = &async->cmd;
587         RTIME now, delay, period;
588         int ret;
589
590         devpriv->stop = 0;
591         s->async->events = 0;
592
593         if (cmd->start_src == TRIG_NOW)
594                 delay = nano2count(cmd->start_arg);
595         else
596                 delay = 0;
597
598         now = rt_get_time();
599         /* Using 'period' this way gets around some weird bug in gcc-2.95.2
600          * that generates the compile error 'internal error--unrecognizable insn'
601          * when rt_task_make_period() is called (observed with rtlinux-3.1, linux-2.2.19).
602          *  - fmhess */
603         period = devpriv->scan_period;
604         ret = rt_task_make_periodic(devpriv->rt_task, now + delay, period);
605         if (ret < 0) {
606                 comedi_error(dev, "error starting rt_task");
607                 return ret;
608         }
609         return 0;
610 }
611
612 static int timer_attach(struct comedi_device * dev, struct comedi_devconfig * it)
613 {
614         int ret;
615         struct comedi_subdevice *s, *emul_s;
616         struct comedi_device *emul_dev;
617         /* These should probably be devconfig options[] */
618         const int timer_priority = 4;
619         const int scan_priority = timer_priority + 1;
620         char path[20];
621
622         printk("comedi%d: timer: ", dev->minor);
623
624         dev->board_name = "timer";
625
626         if ((ret = alloc_subdevices(dev, 1)) < 0)
627                 return ret;
628         if ((ret = alloc_private(dev, sizeof(struct timer_private))) < 0)
629                 return ret;
630
631         sprintf(path, "/dev/comedi%d", it->options[0]);
632         devpriv->device = comedi_open(path);
633         devpriv->subd = it->options[1];
634
635         printk("emulating commands for minor %i, subdevice %d\n",
636                 it->options[0], devpriv->subd);
637
638         emul_dev = devpriv->device;
639         emul_s = emul_dev->subdevices + devpriv->subd;
640
641         /*  input or output subdevice */
642         s = dev->subdevices + 0;
643         s->type = emul_s->type;
644         s->subdev_flags = emul_s->subdev_flags; /* SDF_GROUND (to fool check_driver) */
645         s->n_chan = emul_s->n_chan;
646         s->len_chanlist = 1024;
647         s->do_cmd = timer_cmd;
648         s->do_cmdtest = timer_cmdtest;
649         s->cancel = timer_cancel;
650         s->maxdata = emul_s->maxdata;
651         s->range_table = emul_s->range_table;
652         s->range_table_list = emul_s->range_table_list;
653         switch (emul_s->type) {
654         case COMEDI_SUBD_AI:
655                 s->insn_read = timer_insn;
656                 dev->read_subdev = s;
657                 s->subdev_flags |= SDF_CMD_READ;
658                 devpriv->io_function = timer_data_read;
659                 break;
660         case COMEDI_SUBD_AO:
661                 s->insn_write = timer_insn;
662                 s->insn_read = timer_insn;
663                 dev->write_subdev = s;
664                 s->subdev_flags |= SDF_CMD_WRITE;
665                 devpriv->io_function = timer_data_write;
666                 break;
667         case COMEDI_SUBD_DIO:
668                 s->insn_write = timer_insn;
669                 s->insn_read = timer_insn;
670                 s->insn_bits = timer_insn;
671                 dev->read_subdev = s;
672                 s->subdev_flags |= SDF_CMD_READ;
673                 devpriv->io_function = timer_dio_read;
674                 break;
675         default:
676                 comedi_error(dev, "failed to determine subdevice type!");
677                 return -EINVAL;
678         }
679
680         rt_set_oneshot_mode();
681         start_rt_timer(1);
682         devpriv->timer_running = 1;
683
684         devpriv->rt_task = kzalloc(sizeof(RT_TASK), GFP_KERNEL);
685
686         /*  initialize real-time tasks */
687         ret = rt_task_init(devpriv->rt_task, timer_task_func,
688                 (comedi_rt_task_context_t) dev, 3000, timer_priority, 0, 0);
689         if (ret < 0) {
690                 comedi_error(dev, "error initalizing rt_task");
691                 kfree(devpriv->rt_task);
692                 devpriv->rt_task = 0;
693                 return ret;
694         }
695
696         devpriv->scan_task = kzalloc(sizeof(RT_TASK), GFP_KERNEL);
697
698         ret = rt_task_init(devpriv->scan_task, scan_task_func,
699                 (comedi_rt_task_context_t) dev, 3000, scan_priority, 0, 0);
700         if (ret < 0) {
701                 comedi_error(dev, "error initalizing scan_task");
702                 kfree(devpriv->scan_task);
703                 devpriv->scan_task = 0;
704                 return ret;
705         }
706
707         return 1;
708 }
709
710 /* free allocated resources */
711 static int timer_detach(struct comedi_device * dev)
712 {
713         printk("comedi%d: timer: remove\n", dev->minor);
714
715         if (devpriv) {
716                 if (devpriv->rt_task) {
717                         rt_task_delete(devpriv->rt_task);
718                         kfree(devpriv->rt_task);
719                 }
720                 if (devpriv->scan_task) {
721                         rt_task_delete(devpriv->scan_task);
722                         kfree(devpriv->scan_task);
723                 }
724                 if (devpriv->timer_running)
725                         stop_rt_timer();
726                 if (devpriv->device)
727                         comedi_close(devpriv->device);
728         }
729         return 0;
730 }