Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux...
[sfrench/cifs-2.6.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         // RTIME has units of 1 = 838 nanoseconds
151         // time at which first scan started, used to check scan timing
152         RTIME start;
153         // time between scans
154         RTIME scan_period;
155         // time between conversions in a scan
156         RTIME convert_period;
157         // flags
158         volatile int stop;      // indicates we should stop
159         volatile int rt_task_active;    // indicates rt_task is servicing a struct comedi_cmd
160         volatile int scan_task_active;  // indicates scan_task is servicing a struct comedi_cmd
161         unsigned timer_running:1;
162 };
163 #define devpriv ((struct timer_private *)dev->private)
164
165 static int timer_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
166 {
167         devpriv->stop = 1;
168
169         return 0;
170 }
171
172 // checks for scan timing error
173 inline static int check_scan_timing(struct comedi_device * dev,
174         unsigned long long scan)
175 {
176         RTIME now, timing_error;
177
178         now = rt_get_time();
179         timing_error = now - (devpriv->start + scan * devpriv->scan_period);
180         if (timing_error > devpriv->scan_period) {
181                 comedi_error(dev, "timing error");
182                 rt_printk("scan started %i ns late\n", timing_error * 838);
183                 return -1;
184         }
185
186         return 0;
187 }
188
189 // checks for conversion timing error
190 inline static int check_conversion_timing(struct comedi_device * dev,
191         RTIME scan_start, unsigned int conversion)
192 {
193         RTIME now, timing_error;
194
195         now = rt_get_time();
196         timing_error =
197                 now - (scan_start + conversion * devpriv->convert_period);
198         if (timing_error > devpriv->convert_period) {
199                 comedi_error(dev, "timing error");
200                 rt_printk("conversion started %i ns late\n",
201                         timing_error * 838);
202                 return -1;
203         }
204
205         return 0;
206 }
207
208 // devpriv->io_function for an input subdevice
209 static int timer_data_read(struct comedi_device * dev, struct comedi_cmd * cmd,
210         unsigned int index)
211 {
212         struct comedi_subdevice *s = dev->read_subdev;
213         int ret;
214         unsigned int data;
215
216         ret = comedi_data_read(devpriv->device, devpriv->subd,
217                 CR_CHAN(cmd->chanlist[index]),
218                 CR_RANGE(cmd->chanlist[index]),
219                 CR_AREF(cmd->chanlist[index]), &data);
220         if (ret < 0) {
221                 comedi_error(dev, "read error");
222                 return -EIO;
223         }
224         if (s->flags & SDF_LSAMPL) {
225                 cfc_write_long_to_buffer(s, data);
226         } else {
227                 comedi_buf_put(s->async, data);
228         }
229
230         return 0;
231 }
232
233 // devpriv->io_function for an output subdevice
234 static int timer_data_write(struct comedi_device * dev, struct comedi_cmd * cmd,
235         unsigned int index)
236 {
237         struct comedi_subdevice *s = dev->write_subdev;
238         unsigned int num_bytes;
239         short data;
240         unsigned int long_data;
241         int ret;
242
243         if (s->flags & SDF_LSAMPL) {
244                 num_bytes =
245                         cfc_read_array_from_buffer(s, &long_data,
246                         sizeof(long_data));
247         } else {
248                 num_bytes = cfc_read_array_from_buffer(s, &data, sizeof(data));
249                 long_data = data;
250         }
251
252         if (num_bytes == 0) {
253                 comedi_error(dev, "buffer underrun");
254                 return -EAGAIN;
255         }
256         ret = comedi_data_write(devpriv->device, devpriv->subd,
257                 CR_CHAN(cmd->chanlist[index]),
258                 CR_RANGE(cmd->chanlist[index]),
259                 CR_AREF(cmd->chanlist[index]), long_data);
260         if (ret < 0) {
261                 comedi_error(dev, "write error");
262                 return -EIO;
263         }
264
265         return 0;
266 }
267
268 // devpriv->io_function for DIO subdevices
269 static int timer_dio_read(struct comedi_device * dev, struct comedi_cmd * cmd,
270         unsigned int index)
271 {
272         struct comedi_subdevice *s = dev->read_subdev;
273         int ret;
274         unsigned int data;
275
276         ret = comedi_dio_bitfield(devpriv->device, devpriv->subd, 0, &data);
277         if (ret < 0) {
278                 comedi_error(dev, "read error");
279                 return -EIO;
280         }
281
282         if (s->flags & SDF_LSAMPL)
283                 cfc_write_long_to_buffer(s, data);
284         else
285                 cfc_write_to_buffer(s, data);
286
287         return 0;
288 }
289
290 // performs scans
291 static void scan_task_func(comedi_rt_task_context_t d)
292 {
293         struct comedi_device *dev = (struct comedi_device *) d;
294         struct comedi_subdevice *s = dev->subdevices + 0;
295         struct comedi_async *async = s->async;
296         struct comedi_cmd *cmd = &async->cmd;
297         int i, ret;
298         unsigned long long n;
299         RTIME scan_start;
300
301         // every struct comedi_cmd causes one execution of while loop
302         while (1) {
303                 devpriv->scan_task_active = 1;
304                 // each for loop completes one scan
305                 for (n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE;
306                         n++) {
307                         if (n) {
308                                 // suspend task until next scan
309                                 ret = rt_task_suspend(devpriv->scan_task);
310                                 if (ret < 0) {
311                                         comedi_error(dev,
312                                                 "error suspending scan task");
313                                         async->events |= COMEDI_CB_ERROR;
314                                         goto cleanup;
315                                 }
316                         }
317                         // check if stop flag was set (by timer_cancel())
318                         if (devpriv->stop)
319                                 goto cleanup;
320                         ret = check_scan_timing(dev, n);
321                         if (ret < 0) {
322                                 async->events |= COMEDI_CB_ERROR;
323                                 goto cleanup;
324                         }
325                         scan_start = rt_get_time();
326                         for (i = 0; i < cmd->scan_end_arg; i++) {
327                                 // conversion timing
328                                 if (cmd->convert_src == TRIG_TIMER && i) {
329                                         rt_task_wait_period();
330                                         ret = check_conversion_timing(dev,
331                                                 scan_start, i);
332                                         if (ret < 0) {
333                                                 async->events |=
334                                                         COMEDI_CB_ERROR;
335                                                 goto cleanup;
336                                         }
337                                 }
338                                 ret = devpriv->io_function(dev, cmd, i);
339                                 if (ret < 0) {
340                                         async->events |= COMEDI_CB_ERROR;
341                                         goto cleanup;
342                                 }
343                         }
344                         s->async->events |= COMEDI_CB_BLOCK;
345                         comedi_event(dev, s);
346                         s->async->events = 0;
347                 }
348
349               cleanup:
350
351                 comedi_unlock(devpriv->device, devpriv->subd);
352                 async->events |= COMEDI_CB_EOA;
353                 comedi_event(dev, s);
354                 async->events = 0;
355                 devpriv->scan_task_active = 0;
356                 // suspend task until next struct comedi_cmd
357                 rt_task_suspend(devpriv->scan_task);
358         }
359 }
360
361 static void timer_task_func(comedi_rt_task_context_t d)
362 {
363         struct comedi_device *dev = (struct comedi_device *) d;
364         struct comedi_subdevice *s = dev->subdevices + 0;
365         struct comedi_cmd *cmd = &s->async->cmd;
366         int ret;
367         unsigned long long n;
368
369         // every struct comedi_cmd causes one execution of while loop
370         while (1) {
371                 devpriv->rt_task_active = 1;
372                 devpriv->scan_task_active = 1;
373                 devpriv->start = rt_get_time();
374
375                 for (n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE;
376                         n++) {
377                         // scan timing
378                         if (n)
379                                 rt_task_wait_period();
380                         if (devpriv->scan_task_active == 0) {
381                                 goto cleanup;
382                         }
383                         ret = rt_task_make_periodic(devpriv->scan_task,
384                                 devpriv->start + devpriv->scan_period * n,
385                                 devpriv->convert_period);
386                         if (ret < 0) {
387                                 comedi_error(dev, "bug!");
388                         }
389                 }
390
391               cleanup:
392
393                 devpriv->rt_task_active = 0;
394                 // suspend until next struct comedi_cmd
395                 rt_task_suspend(devpriv->rt_task);
396         }
397 }
398
399 static int timer_insn(struct comedi_device * dev, struct comedi_subdevice * s,
400         struct comedi_insn * insn, unsigned int * data)
401 {
402         struct comedi_insn xinsn = *insn;
403
404         xinsn.data = data;
405         xinsn.subdev = devpriv->subd;
406
407         return comedi_do_insn(devpriv->device, &xinsn);
408 }
409
410 static int cmdtest_helper(struct comedi_cmd * cmd,
411         unsigned int start_src,
412         unsigned int scan_begin_src,
413         unsigned int convert_src,
414         unsigned int scan_end_src, unsigned int stop_src)
415 {
416         int err = 0;
417         int tmp;
418
419         tmp = cmd->start_src;
420         cmd->start_src &= start_src;
421         if (!cmd->start_src || tmp != cmd->start_src)
422                 err++;
423
424         tmp = cmd->scan_begin_src;
425         cmd->scan_begin_src &= scan_begin_src;
426         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
427                 err++;
428
429         tmp = cmd->convert_src;
430         cmd->convert_src &= convert_src;
431         if (!cmd->convert_src || tmp != cmd->convert_src)
432                 err++;
433
434         tmp = cmd->scan_end_src;
435         cmd->scan_end_src &= scan_end_src;
436         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
437                 err++;
438
439         tmp = cmd->stop_src;
440         cmd->stop_src &= stop_src;
441         if (!cmd->stop_src || tmp != cmd->stop_src)
442                 err++;
443
444         return err;
445 }
446
447 static int timer_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
448         struct comedi_cmd * cmd)
449 {
450         int err = 0;
451         unsigned int start_src = 0;
452
453         if (s->type == COMEDI_SUBD_AO)
454                 start_src = TRIG_INT;
455         else
456                 start_src = TRIG_NOW;
457
458         err = cmdtest_helper(cmd, start_src,    /* start_src */
459                 TRIG_TIMER | TRIG_FOLLOW,       /* scan_begin_src */
460                 TRIG_NOW | TRIG_TIMER,  /* convert_src */
461                 TRIG_COUNT,     /* scan_end_src */
462                 TRIG_COUNT | TRIG_NONE);        /* stop_src */
463         if (err)
464                 return 1;
465
466         /* step 2: make sure trigger sources are unique and mutually
467          * compatible */
468
469         if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_INT)
470                 err++;
471         if (cmd->scan_begin_src != TRIG_TIMER &&
472                 cmd->scan_begin_src != TRIG_FOLLOW)
473                 err++;
474         if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_NOW)
475                 err++;
476         if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
477                 err++;
478         if (cmd->scan_begin_src == TRIG_FOLLOW
479                 && cmd->convert_src != TRIG_TIMER)
480                 err++;
481         if (cmd->convert_src == TRIG_NOW && cmd->scan_begin_src != TRIG_TIMER)
482                 err++;
483
484         if (err)
485                 return 2;
486
487         /* step 3: make sure arguments are trivially compatible */
488         // limit frequency, this is fairly arbitrary
489         if (cmd->scan_begin_src == TRIG_TIMER) {
490                 if (cmd->scan_begin_arg < SPEED_LIMIT) {
491                         cmd->scan_begin_arg = SPEED_LIMIT;
492                         err++;
493                 }
494         }
495         if (cmd->convert_src == TRIG_TIMER) {
496                 if (cmd->convert_arg < SPEED_LIMIT) {
497                         cmd->convert_arg = SPEED_LIMIT;
498                         err++;
499                 }
500         }
501         // make sure conversion and scan frequencies are compatible
502         if (cmd->convert_src == TRIG_TIMER && cmd->scan_begin_src == TRIG_TIMER) {
503                 if (cmd->convert_arg * cmd->scan_end_arg > cmd->scan_begin_arg) {
504                         cmd->scan_begin_arg =
505                                 cmd->convert_arg * cmd->scan_end_arg;
506                         err++;
507                 }
508         }
509         if (err)
510                 return 3;
511
512         /* step 4: fix up and arguments */
513         if (err)
514                 return 4;
515
516         return 0;
517 }
518
519 static int timer_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
520 {
521         int ret;
522         struct comedi_cmd *cmd = &s->async->cmd;
523
524         /* hack attack: drivers are not supposed to do this: */
525         dev->rt = 1;
526
527         // make sure tasks have finished cleanup of last struct comedi_cmd
528         if (devpriv->rt_task_active || devpriv->scan_task_active)
529                 return -EBUSY;
530
531         ret = comedi_lock(devpriv->device, devpriv->subd);
532         if (ret < 0) {
533                 comedi_error(dev, "failed to obtain lock");
534                 return ret;
535         }
536         switch (cmd->scan_begin_src) {
537         case TRIG_TIMER:
538                 devpriv->scan_period = nano2count(cmd->scan_begin_arg);
539                 break;
540         case TRIG_FOLLOW:
541                 devpriv->scan_period =
542                         nano2count(cmd->convert_arg * cmd->scan_end_arg);
543                 break;
544         default:
545                 comedi_error(dev, "bug setting scan period!");
546                 return -1;
547                 break;
548         }
549         switch (cmd->convert_src) {
550         case TRIG_TIMER:
551                 devpriv->convert_period = nano2count(cmd->convert_arg);
552                 break;
553         case TRIG_NOW:
554                 devpriv->convert_period = 1;
555                 break;
556         default:
557                 comedi_error(dev, "bug setting conversion period!");
558                 return -1;
559                 break;
560         }
561
562         if (cmd->start_src == TRIG_NOW)
563                 return timer_start_cmd(dev, s);
564
565         s->async->inttrig = timer_inttrig;
566
567         return 0;
568 }
569
570 static int timer_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
571         unsigned int trig_num)
572 {
573         if (trig_num != 0)
574                 return -EINVAL;
575
576         s->async->inttrig = NULL;
577
578         return timer_start_cmd(dev, s);
579 }
580
581 static int timer_start_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
582 {
583         struct comedi_async *async = s->async;
584         struct comedi_cmd *cmd = &async->cmd;
585         RTIME now, delay, period;
586         int ret;
587
588         devpriv->stop = 0;
589         s->async->events = 0;
590
591         if (cmd->start_src == TRIG_NOW)
592                 delay = nano2count(cmd->start_arg);
593         else
594                 delay = 0;
595
596         now = rt_get_time();
597         /* Using 'period' this way gets around some weird bug in gcc-2.95.2
598          * that generates the compile error 'internal error--unrecognizable insn'
599          * when rt_task_make_period() is called (observed with rtlinux-3.1, linux-2.2.19).
600          *  - fmhess */
601         period = devpriv->scan_period;
602         ret = rt_task_make_periodic(devpriv->rt_task, now + delay, period);
603         if (ret < 0) {
604                 comedi_error(dev, "error starting rt_task");
605                 return ret;
606         }
607         return 0;
608 }
609
610 static int timer_attach(struct comedi_device * dev, struct comedi_devconfig * it)
611 {
612         int ret;
613         struct comedi_subdevice *s, *emul_s;
614         struct comedi_device *emul_dev;
615         /* These should probably be devconfig options[] */
616         const int timer_priority = 4;
617         const int scan_priority = timer_priority + 1;
618         char path[20];
619
620         printk("comedi%d: timer: ", dev->minor);
621
622         dev->board_name = "timer";
623
624         if ((ret = alloc_subdevices(dev, 1)) < 0)
625                 return ret;
626         if ((ret = alloc_private(dev, sizeof(struct timer_private))) < 0)
627                 return ret;
628
629         sprintf(path, "/dev/comedi%d", it->options[0]);
630         devpriv->device = comedi_open(path);
631         devpriv->subd = it->options[1];
632
633         printk("emulating commands for minor %i, subdevice %d\n",
634                 it->options[0], devpriv->subd);
635
636         emul_dev = devpriv->device;
637         emul_s = emul_dev->subdevices + devpriv->subd;
638
639         // input or output subdevice
640         s = dev->subdevices + 0;
641         s->type = emul_s->type;
642         s->subdev_flags = emul_s->subdev_flags; /* SDF_GROUND (to fool check_driver) */
643         s->n_chan = emul_s->n_chan;
644         s->len_chanlist = 1024;
645         s->do_cmd = timer_cmd;
646         s->do_cmdtest = timer_cmdtest;
647         s->cancel = timer_cancel;
648         s->maxdata = emul_s->maxdata;
649         s->range_table = emul_s->range_table;
650         s->range_table_list = emul_s->range_table_list;
651         switch (emul_s->type) {
652         case COMEDI_SUBD_AI:
653                 s->insn_read = timer_insn;
654                 dev->read_subdev = s;
655                 s->subdev_flags |= SDF_CMD_READ;
656                 devpriv->io_function = timer_data_read;
657                 break;
658         case COMEDI_SUBD_AO:
659                 s->insn_write = timer_insn;
660                 s->insn_read = timer_insn;
661                 dev->write_subdev = s;
662                 s->subdev_flags |= SDF_CMD_WRITE;
663                 devpriv->io_function = timer_data_write;
664                 break;
665         case COMEDI_SUBD_DIO:
666                 s->insn_write = timer_insn;
667                 s->insn_read = timer_insn;
668                 s->insn_bits = timer_insn;
669                 dev->read_subdev = s;
670                 s->subdev_flags |= SDF_CMD_READ;
671                 devpriv->io_function = timer_dio_read;
672                 break;
673         default:
674                 comedi_error(dev, "failed to determine subdevice type!");
675                 return -EINVAL;
676         }
677
678         rt_set_oneshot_mode();
679         start_rt_timer(1);
680         devpriv->timer_running = 1;
681
682         devpriv->rt_task = kzalloc(sizeof(RT_TASK), GFP_KERNEL);
683
684         // initialize real-time tasks
685         ret = rt_task_init(devpriv->rt_task, timer_task_func,
686                 (comedi_rt_task_context_t) dev, 3000, timer_priority, 0, 0);
687         if (ret < 0) {
688                 comedi_error(dev, "error initalizing rt_task");
689                 kfree(devpriv->rt_task);
690                 devpriv->rt_task = 0;
691                 return ret;
692         }
693
694         devpriv->scan_task = kzalloc(sizeof(RT_TASK), GFP_KERNEL);
695
696         ret = rt_task_init(devpriv->scan_task, scan_task_func,
697                 (comedi_rt_task_context_t) dev, 3000, scan_priority, 0, 0);
698         if (ret < 0) {
699                 comedi_error(dev, "error initalizing scan_task");
700                 kfree(devpriv->scan_task);
701                 devpriv->scan_task = 0;
702                 return ret;
703         }
704
705         return 1;
706 }
707
708 // free allocated resources
709 static int timer_detach(struct comedi_device * dev)
710 {
711         printk("comedi%d: timer: remove\n", dev->minor);
712
713         if (devpriv) {
714                 if (devpriv->rt_task) {
715                         rt_task_delete(devpriv->rt_task);
716                         kfree(devpriv->rt_task);
717                 }
718                 if (devpriv->scan_task) {
719                         rt_task_delete(devpriv->scan_task);
720                         kfree(devpriv->scan_task);
721                 }
722                 if (devpriv->timer_running)
723                         stop_rt_timer();
724                 if (devpriv->device)
725                         comedi_close(devpriv->device);
726         }
727         return 0;
728 }