Staging: comedi: Remove C99 comments
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / staging / comedi / drivers / comedi_rt_timer.c
CommitLineData
d96cba07
DS
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/*
27Driver: comedi_rt_timer
28Description: Command emulator using real-time tasks
29Author: ds, fmhess
30Devices:
31Status: works
32
33This driver requires RTAI or RTLinux to work correctly. It doesn't
34actually drive hardware directly, but calls other drivers and uses
35a real-time task to emulate commands for drivers and devices that
36are incapable of native commands. Thus, you can get accurately
37timed I/O on any device.
38
39Since the timing is all done in software, sampling jitter is much
40higher than with a device that has an on-board timer, and maximum
41sample rate is much lower.
42
43Configuration 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/*
48TODO:
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
2696fb57 76/* begin hack to workaround broken HRT_TO_8254() function on rtlinux */
d96cba07 77#if RTLINUX_VERSION_CODE <= RTLINUX_VERSION(3,0,100)
2696fb57 78/* this function sole purpose is to divide a long long by 838 */
d96cba07
DS
79static 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
2696fb57 94/* end hack */
d96cba07 95
2696fb57 96/* rtl-rtai compatibility */
d96cba07
DS
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
d96cba07
DS
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
0707bb04 125static int timer_attach(struct comedi_device * dev, struct comedi_devconfig * it);
71b5f4f1 126static int timer_detach(struct comedi_device * dev);
34c43922 127static int timer_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
d96cba07 128 unsigned int trig_num);
34c43922 129static int timer_start_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
d96cba07 130
139dfbdf 131static struct comedi_driver driver_timer = {
d96cba07
DS
132 module:THIS_MODULE,
133 driver_name:"comedi_rt_timer",
134 attach:timer_attach,
135 detach:timer_detach,
2696fb57 136/* open: timer_open, */
d96cba07
DS
137};
138
139COMEDI_INITCLEANUP(driver_timer);
140
ecce6332 141struct timer_private {
2696fb57
BP
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 */
d96cba07
DS
146 /* io_function can point to either an input or output function
147 * depending on what kind of subdevice we are emulating for */
ea6d0d4c 148 int (*io_function) (struct comedi_device * dev, struct comedi_cmd * cmd,
d96cba07 149 unsigned int index);
2696fb57
BP
150/*
151* RTIME has units of 1 = 838 nanoseconds time at which first scan
152* started, used to check scan timing
153*/
d96cba07 154 RTIME start;
2696fb57 155 /* time between scans */
d96cba07 156 RTIME scan_period;
2696fb57 157 /* time between conversions in a scan */
d96cba07 158 RTIME convert_period;
2696fb57
BP
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 */
d96cba07 163 unsigned timer_running:1;
ecce6332
BP
164};
165#define devpriv ((struct timer_private *)dev->private)
d96cba07 166
34c43922 167static int timer_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
d96cba07
DS
168{
169 devpriv->stop = 1;
170
171 return 0;
172}
173
2696fb57 174/* checks for scan timing error */
71b5f4f1 175inline static int check_scan_timing(struct comedi_device * dev,
d96cba07
DS
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
2696fb57 191/* checks for conversion timing error */
71b5f4f1 192inline static int check_conversion_timing(struct comedi_device * dev,
d96cba07
DS
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
2696fb57 210/* devpriv->io_function for an input subdevice */
ea6d0d4c 211static int timer_data_read(struct comedi_device * dev, struct comedi_cmd * cmd,
d96cba07
DS
212 unsigned int index)
213{
34c43922 214 struct comedi_subdevice *s = dev->read_subdev;
d96cba07 215 int ret;
790c5541 216 unsigned int data;
d96cba07
DS
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
2696fb57 235/* devpriv->io_function for an output subdevice */
ea6d0d4c 236static int timer_data_write(struct comedi_device * dev, struct comedi_cmd * cmd,
d96cba07
DS
237 unsigned int index)
238{
34c43922 239 struct comedi_subdevice *s = dev->write_subdev;
d96cba07 240 unsigned int num_bytes;
790c5541
BP
241 short data;
242 unsigned int long_data;
d96cba07
DS
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
2696fb57 270/* devpriv->io_function for DIO subdevices */
ea6d0d4c 271static int timer_dio_read(struct comedi_device * dev, struct comedi_cmd * cmd,
d96cba07
DS
272 unsigned int index)
273{
34c43922 274 struct comedi_subdevice *s = dev->read_subdev;
d96cba07 275 int ret;
790c5541 276 unsigned int data;
d96cba07
DS
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
2696fb57 292/* performs scans */
d96cba07
DS
293static void scan_task_func(comedi_rt_task_context_t d)
294{
71b5f4f1 295 struct comedi_device *dev = (struct comedi_device *) d;
34c43922 296 struct comedi_subdevice *s = dev->subdevices + 0;
d163679c 297 struct comedi_async *async = s->async;
ea6d0d4c 298 struct comedi_cmd *cmd = &async->cmd;
d96cba07
DS
299 int i, ret;
300 unsigned long long n;
301 RTIME scan_start;
302
2696fb57 303 /* every struct comedi_cmd causes one execution of while loop */
d96cba07
DS
304 while (1) {
305 devpriv->scan_task_active = 1;
2696fb57 306 /* each for loop completes one scan */
d96cba07
DS
307 for (n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE;
308 n++) {
309 if (n) {
2696fb57 310 /* suspend task until next scan */
d96cba07
DS
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 }
2696fb57 319 /* check if stop flag was set (by timer_cancel()) */
d96cba07
DS
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++) {
2696fb57 329 /* conversion timing */
d96cba07
DS
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;
2696fb57 358 /* suspend task until next struct comedi_cmd */
d96cba07
DS
359 rt_task_suspend(devpriv->scan_task);
360 }
361}
362
363static void timer_task_func(comedi_rt_task_context_t d)
364{
71b5f4f1 365 struct comedi_device *dev = (struct comedi_device *) d;
34c43922 366 struct comedi_subdevice *s = dev->subdevices + 0;
ea6d0d4c 367 struct comedi_cmd *cmd = &s->async->cmd;
d96cba07
DS
368 int ret;
369 unsigned long long n;
370
2696fb57 371 /* every struct comedi_cmd causes one execution of while loop */
d96cba07
DS
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++) {
2696fb57 379 /* scan timing */
d96cba07
DS
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;
2696fb57 396 /* suspend until next struct comedi_cmd */
d96cba07
DS
397 rt_task_suspend(devpriv->rt_task);
398 }
399}
400
34c43922 401static int timer_insn(struct comedi_device * dev, struct comedi_subdevice * s,
90035c08 402 struct comedi_insn * insn, unsigned int * data)
d96cba07 403{
90035c08 404 struct comedi_insn xinsn = *insn;
d96cba07
DS
405
406 xinsn.data = data;
407 xinsn.subdev = devpriv->subd;
408
409 return comedi_do_insn(devpriv->device, &xinsn);
410}
411
ea6d0d4c 412static int cmdtest_helper(struct comedi_cmd * cmd,
d96cba07
DS
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
34c43922 449static int timer_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
ea6d0d4c 450 struct comedi_cmd * cmd)
d96cba07
DS
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 */
2696fb57 490 /* limit frequency, this is fairly arbitrary */
d96cba07
DS
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 }
2696fb57 503 /* make sure conversion and scan frequencies are compatible */
d96cba07
DS
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
34c43922 521static int timer_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
d96cba07
DS
522{
523 int ret;
ea6d0d4c 524 struct comedi_cmd *cmd = &s->async->cmd;
d96cba07
DS
525
526 /* hack attack: drivers are not supposed to do this: */
527 dev->rt = 1;
528
2696fb57 529 /* make sure tasks have finished cleanup of last struct comedi_cmd */
d96cba07
DS
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
34c43922 572static int timer_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
d96cba07
DS
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
34c43922 583static int timer_start_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
d96cba07 584{
d163679c 585 struct comedi_async *async = s->async;
ea6d0d4c 586 struct comedi_cmd *cmd = &async->cmd;
d96cba07
DS
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
0707bb04 612static int timer_attach(struct comedi_device * dev, struct comedi_devconfig * it)
d96cba07
DS
613{
614 int ret;
34c43922 615 struct comedi_subdevice *s, *emul_s;
71b5f4f1 616 struct comedi_device *emul_dev;
d96cba07
DS
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;
ecce6332 628 if ((ret = alloc_private(dev, sizeof(struct timer_private))) < 0)
d96cba07
DS
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
2696fb57 641 /* input or output subdevice */
d96cba07
DS
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
2696fb57 686 /* initialize real-time tasks */
d96cba07
DS
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
2696fb57 710/* free allocated resources */
71b5f4f1 711static int timer_detach(struct comedi_device * dev)
d96cba07
DS
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}