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