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